6.6.3 - Alpha3 - 修复 JavaAdapter 栈溢出问题 (issue #376)
This commit is contained in:
@@ -16,6 +16,7 @@
|
||||
"APK 文件类型信息对话框可能无法获取应用名称及 SDK 信息的问题",
|
||||
"Android 15 部分页面状态栏背景颜色可能无法动态跟随主题色的问题",
|
||||
"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)_",
|
||||
"README.md 中部分语言日期格式不正确的问题"
|
||||
],
|
||||
|
||||
@@ -1,14 +1,200 @@
|
||||
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.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.
|
||||
*/
|
||||
public class AndroidClassLoader extends org.autojs.autojs.rhino.AndroidClassLoader {
|
||||
public AndroidClassLoader(@NotNull ClassLoader parent, @NotNull File cacheDir) {
|
||||
super(parent, cacheDir);
|
||||
|
||||
public class AndroidClassLoader extends ClassLoader implements GeneratedClassLoader {
|
||||
|
||||
|
||||
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 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 {
|
||||
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 {
|
||||
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)
|
||||
public override fun loadClass(name: String, resolve: Boolean): Class<*> {
|
||||
findLoadedClass(name)?.let { return it }
|
||||
for (dex in dexClassLoaders.values) try {
|
||||
for (dex in mDexClassLoaders.values) try {
|
||||
dex.loadClass(name)?.let { return it }
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
@@ -216,7 +218,6 @@ open class AndroidClassLoader(private val parent: ClassLoader, private val cache
|
||||
companion object {
|
||||
|
||||
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
|
||||
BUILD_TIME=1746780943861
|
||||
#Fri May 09 18:20:25 CST 2025
|
||||
BUILD_TIME=1746786025154
|
||||
COMPILE_SDK_VERSION=35
|
||||
JAVA_VERSION=23
|
||||
JAVA_VERSION_MIN_RADICAL=0
|
||||
@@ -17,6 +17,6 @@ RAPID_OCR_OPENCV_MOBILE_LABEL_VERSION=13
|
||||
RAPID_OCR_OPENCV_MOBILE_VERSION=4.5.3
|
||||
TARGET_SDK_VERSION=35
|
||||
TARGET_SDK_VERSION_INRT=29
|
||||
VERSION_BUILD=3211
|
||||
VERSION_NAME=6.6.3 Alpha2
|
||||
VERSION_BUILD=3212
|
||||
VERSION_NAME=6.6.3 Alpha3
|
||||
VSCODE_EXT_REQUIRED_VERSION=1.0.8
|
||||
|
||||
Reference in New Issue
Block a user