6.6.3 - Alpha3 - 修复 JavaAdapter 栈溢出问题 (issue #376)
This commit is contained in:
@@ -16,6 +16,7 @@
|
|||||||
"APK 文件类型信息对话框可能无法获取应用名称及 SDK 信息的问题",
|
"APK 文件类型信息对话框可能无法获取应用名称及 SDK 信息的问题",
|
||||||
"Android 15 部分页面状态栏背景颜色可能无法动态跟随主题色的问题",
|
"Android 15 部分页面状态栏背景颜色可能无法动态跟随主题色的问题",
|
||||||
"dialogs 模块无法正常使用 customView 属性的问题 _[`issue #364`](http://issues.autojs6.com/364)_",
|
"dialogs 模块无法正常使用 customView 属性的问题 _[`issue #364`](http://issues.autojs6.com/364)_",
|
||||||
|
"使用 JavaAdapter 时导致 ClassLoader 调用栈溢出的问题 _[`issue #376`](http://issues.autojs6.com/376)_",
|
||||||
"console.setContentTextColor 方法导致日志字体颜色丢失默认值的问题 _[`issue #346`](http://issues.autojs6.com/346)_",
|
"console.setContentTextColor 方法导致日志字体颜色丢失默认值的问题 _[`issue #346`](http://issues.autojs6.com/346)_",
|
||||||
"README.md 中部分语言日期格式不正确的问题"
|
"README.md 中部分语言日期格式不正确的问题"
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -1,14 +1,200 @@
|
|||||||
package com.stardust.autojs.rhino;
|
package com.stardust.autojs.rhino;
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.android.dx.command.dexer.Main;
|
||||||
|
import com.stardust.pio.PFiles;
|
||||||
|
import com.stardust.util.MD5;
|
||||||
|
|
||||||
|
import org.mozilla.javascript.GeneratedClassLoader;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import dalvik.system.DexClassLoader;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by Stardust on 2017/4/5.
|
* Created by Stardust on 2017/4/5.
|
||||||
*/
|
*/
|
||||||
public class AndroidClassLoader extends org.autojs.autojs.rhino.AndroidClassLoader {
|
|
||||||
public AndroidClassLoader(@NotNull ClassLoader parent, @NotNull File cacheDir) {
|
public class AndroidClassLoader extends ClassLoader implements GeneratedClassLoader {
|
||||||
super(parent, cacheDir);
|
|
||||||
|
|
||||||
|
private static final String LOG_TAG = "AndroidClassLoader";
|
||||||
|
private final ClassLoader parent;
|
||||||
|
private final List<DexClassLoader> mDexClassLoaders = new ArrayList<>();
|
||||||
|
private final File mCacheDir;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new instance with the given parent classloader and cache dierctory
|
||||||
|
*
|
||||||
|
* @param parent the parent
|
||||||
|
* @param dir the cache directory
|
||||||
|
*/
|
||||||
|
public AndroidClassLoader(ClassLoader parent, File dir) {
|
||||||
|
this.parent = parent;
|
||||||
|
mCacheDir = dir;
|
||||||
|
if (dir.exists()) {
|
||||||
|
PFiles.deleteFilesOfDir(dir);
|
||||||
|
} else {
|
||||||
|
dir.mkdirs();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Class<?> defineClass(String name, byte[] data) {
|
||||||
|
Log.d(LOG_TAG, "defineClass: name = " + name + " data.length = " + data.length);
|
||||||
|
File classFile = null;
|
||||||
|
try {
|
||||||
|
classFile = generateTempFile(name, false);
|
||||||
|
java.util.jar.JarOutputStream jos = new java.util.jar.JarOutputStream(new java.io.FileOutputStream(classFile));
|
||||||
|
java.util.jar.JarEntry entry = new java.util.jar.JarEntry(name.replace('.', '/') + ".class");
|
||||||
|
jos.putNextEntry(entry);
|
||||||
|
jos.write(data);
|
||||||
|
jos.closeEntry();
|
||||||
|
jos.close();
|
||||||
|
return dexJar(classFile, null).loadClass(name);
|
||||||
|
} catch (IOException | ClassNotFoundException e) {
|
||||||
|
throw new FatalLoadingException(e);
|
||||||
|
} finally {
|
||||||
|
if (classFile != null) {
|
||||||
|
classFile.delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private File generateTempFile(String name, boolean create) throws IOException {
|
||||||
|
File file = new File(mCacheDir, name.hashCode() + System.currentTimeMillis() + ".jar");
|
||||||
|
if (create) {
|
||||||
|
if (!file.exists()) {
|
||||||
|
file.createNewFile();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
file.delete();
|
||||||
|
}
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void loadJar(File jar) throws IOException {
|
||||||
|
Log.d(LOG_TAG, "loadJar: jar = " + jar);
|
||||||
|
if (!jar.exists() || !jar.canRead()) {
|
||||||
|
throw new FileNotFoundException("File does not exist or readable: " + jar.getPath());
|
||||||
|
}
|
||||||
|
File dexFile = new File(mCacheDir, generateDexFileName(jar));
|
||||||
|
if (dexFile.exists()) {
|
||||||
|
loadDex(dexFile);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
final File classFile = generateTempFile(jar.getPath(), false);
|
||||||
|
java.util.jar.JarOutputStream jos = new java.util.jar.JarOutputStream(new java.io.FileOutputStream(classFile));
|
||||||
|
java.util.jar.JarFile jarFile = new java.util.jar.JarFile(jar);
|
||||||
|
java.util.Enumeration<java.util.jar.JarEntry> entries = jarFile.entries();
|
||||||
|
while (entries.hasMoreElements()) {
|
||||||
|
java.util.jar.JarEntry entry = entries.nextElement();
|
||||||
|
if (!entry.isDirectory()) {
|
||||||
|
jos.putNextEntry(new java.util.jar.JarEntry(entry.getName()));
|
||||||
|
java.io.InputStream is = jarFile.getInputStream(entry);
|
||||||
|
byte[] buffer = new byte[4096];
|
||||||
|
int bytesRead;
|
||||||
|
while ((bytesRead = is.read(buffer)) != -1) {
|
||||||
|
jos.write(buffer, 0, bytesRead);
|
||||||
|
}
|
||||||
|
is.close();
|
||||||
|
jos.closeEntry();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
jos.close();
|
||||||
|
jarFile.close();
|
||||||
|
dexJar(classFile, dexFile);
|
||||||
|
classFile.delete();
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new IOException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String generateDexFileName(File jar) {
|
||||||
|
String message = jar.getPath() + "_" + jar.lastModified();
|
||||||
|
return MD5.md5(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DexClassLoader loadDex(File file) throws FileNotFoundException {
|
||||||
|
Log.d(LOG_TAG, "loadDex: file = " + file);
|
||||||
|
if (!file.exists()) {
|
||||||
|
throw new FileNotFoundException(file.getPath());
|
||||||
|
}
|
||||||
|
DexClassLoader loader = new DexClassLoader(file.getPath(), mCacheDir.getPath(), null, parent);
|
||||||
|
mDexClassLoaders.add(loader);
|
||||||
|
return loader;
|
||||||
|
}
|
||||||
|
|
||||||
|
private DexClassLoader dexJar(File classFile, File dexFile) throws IOException {
|
||||||
|
final Main.Arguments arguments = new Main.Arguments();
|
||||||
|
arguments.fileNames = new String[]{classFile.getPath()};
|
||||||
|
boolean isTmpDex = dexFile == null;
|
||||||
|
if (isTmpDex) {
|
||||||
|
dexFile = generateTempFile("dex-" + classFile.getPath(), true);
|
||||||
|
}
|
||||||
|
arguments.outName = dexFile.getPath();
|
||||||
|
arguments.jarOutput = true;
|
||||||
|
Main.run(arguments);
|
||||||
|
DexClassLoader loader = loadDex(dexFile);
|
||||||
|
if (isTmpDex) {
|
||||||
|
dexFile.delete();
|
||||||
|
}
|
||||||
|
return loader;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does nothing
|
||||||
|
*
|
||||||
|
* @param aClass ignored
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void linkClass(Class<?> aClass) {
|
||||||
|
//doesn't make sense on android
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Try to load a class. This will search all defined classes, all loaded jars and the parent class loader.
|
||||||
|
*
|
||||||
|
* @param name the name of the class to load
|
||||||
|
* @param resolve ignored
|
||||||
|
* @return the class
|
||||||
|
* @throws ClassNotFoundException if the class could not be found in any of the locations
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Class<?> loadClass(String name, boolean resolve)
|
||||||
|
throws ClassNotFoundException {
|
||||||
|
Class<?> loadedClass = findLoadedClass(name);
|
||||||
|
if (loadedClass == null) {
|
||||||
|
for (DexClassLoader dex : mDexClassLoaders) {
|
||||||
|
loadedClass = dex.loadClass(name);
|
||||||
|
if (loadedClass != null) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (loadedClass == null) {
|
||||||
|
loadedClass = parent.loadClass(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return loadedClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Might be thrown in any Rhino method that loads bytecode if the loading failed
|
||||||
|
*/
|
||||||
|
public static class FatalLoadingException extends RuntimeException {
|
||||||
|
FatalLoadingException(Throwable t) {
|
||||||
|
super("Failed to define class", t);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,9 @@ import com.legacy.android.dx.command.dexer.Main as LegacyMain
|
|||||||
* @param parent the parent
|
* @param parent the parent
|
||||||
* @param cacheDir the cache directory
|
* @param cacheDir the cache directory
|
||||||
*/
|
*/
|
||||||
open class AndroidClassLoader(private val parent: ClassLoader, private val cacheDir: File) : ClassLoader(), GeneratedClassLoader {
|
class AndroidClassLoader(private val parent: ClassLoader, private val cacheDir: File) : ClassLoader(), GeneratedClassLoader {
|
||||||
|
|
||||||
|
private val mDexClassLoaders = HashMap<String, DexClassLoader>()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
if (cacheDir.exists()) {
|
if (cacheDir.exists()) {
|
||||||
@@ -78,7 +80,7 @@ open class AndroidClassLoader(private val parent: ClassLoader, private val cache
|
|||||||
}
|
}
|
||||||
|
|
||||||
return DexClassLoader(safeDexFile.path, cacheDir.path, null, parent).also {
|
return DexClassLoader(safeDexFile.path, cacheDir.path, null, parent).also {
|
||||||
dexClassLoaders[safeDexFile.path] = it
|
mDexClassLoaders[safeDexFile.path] = it
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,7 +127,7 @@ open class AndroidClassLoader(private val parent: ClassLoader, private val cache
|
|||||||
@Throws(ClassNotFoundException::class)
|
@Throws(ClassNotFoundException::class)
|
||||||
public override fun loadClass(name: String, resolve: Boolean): Class<*> {
|
public override fun loadClass(name: String, resolve: Boolean): Class<*> {
|
||||||
findLoadedClass(name)?.let { return it }
|
findLoadedClass(name)?.let { return it }
|
||||||
for (dex in dexClassLoaders.values) try {
|
for (dex in mDexClassLoaders.values) try {
|
||||||
dex.loadClass(name)?.let { return it }
|
dex.loadClass(name)?.let { return it }
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
@@ -216,7 +218,6 @@ open class AndroidClassLoader(private val parent: ClassLoader, private val cache
|
|||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
private val TAG = AndroidClassLoader::class.java.simpleName
|
private val TAG = AndroidClassLoader::class.java.simpleName
|
||||||
private val dexClassLoaders = HashMap<String, DexClassLoader>()
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#Fri May 09 16:55:43 CST 2025
|
#Fri May 09 18:20:25 CST 2025
|
||||||
BUILD_TIME=1746780943861
|
BUILD_TIME=1746786025154
|
||||||
COMPILE_SDK_VERSION=35
|
COMPILE_SDK_VERSION=35
|
||||||
JAVA_VERSION=23
|
JAVA_VERSION=23
|
||||||
JAVA_VERSION_MIN_RADICAL=0
|
JAVA_VERSION_MIN_RADICAL=0
|
||||||
@@ -17,6 +17,6 @@ RAPID_OCR_OPENCV_MOBILE_LABEL_VERSION=13
|
|||||||
RAPID_OCR_OPENCV_MOBILE_VERSION=4.5.3
|
RAPID_OCR_OPENCV_MOBILE_VERSION=4.5.3
|
||||||
TARGET_SDK_VERSION=35
|
TARGET_SDK_VERSION=35
|
||||||
TARGET_SDK_VERSION_INRT=29
|
TARGET_SDK_VERSION_INRT=29
|
||||||
VERSION_BUILD=3211
|
VERSION_BUILD=3212
|
||||||
VERSION_NAME=6.6.3 Alpha2
|
VERSION_NAME=6.6.3 Alpha3
|
||||||
VSCODE_EXT_REQUIRED_VERSION=1.0.8
|
VSCODE_EXT_REQUIRED_VERSION=1.0.8
|
||||||
|
|||||||
Reference in New Issue
Block a user