在Android 4.45.08.0 测试动态加载jarapk

xiaoxiao2021-02-28  46

不同于java虚拟机 JVM加载jar中的 .class文件,Android的 Dalvik/ART虚拟机加载的是jar/apk/zip中的 .dex文件,因此两种环境下的ClassLoader使用也是不同的。 java虚拟机环境下一般使用java.net.URLClassLoader; Android虚拟机环境下一般使用dalvik.system.DexClassLoader和dalvik.system.PathClassLoader; DexClassLoader和PathClassLoader这两个ClassLoader都继承自dalvik.system.BaseDexClassLoader,在8.0之前主要区别在于DexClassLoader会提供optimizedDirectory参数,但从8.0开始optimizedDirectory已经弃用两者从代码加载流程上来看的区别见后边。 关于optimizedDirectory的要求:This class loader requires an application-private, writable directory to cache optimized classes. Use Context.getDir(String, int) (从API level 21起,官方变成了推荐使用Context.getCodeCacheDir()) to create such a directory. Do not cache optimized classes on external storage. External storage does not provide access controls necessary to protect your application from code injection attacks.(https://developer.android.google.cn/reference/dalvik/system/DexClassLoader.html) this.getDir("abc", Context.MODE_PRIVATE) 就会在/data/data/pkg-name/下产生app_abc目录,即会有个app_前缀。 网上很多人都说如下结论:

DexClassLoader:能够加载未安装的jar/apk/dex PathClassLoader:只能加载系统中已经安装过的apk 但实际上我的测试并非如此: 在Android 8.0 和5.0模拟器上测试发现,PathClassLoader一样可以加载未安装的jar/apk,而在4.4上才会加载失败。 从 Android 5.0 开始,ART 运行时取代 Dalvik 成为平台默认设置。 也就是说,上述结论适用于Dalvik,但在ART上 PathClassLoader也 可以加载非安装的jar/apk。 在4.4模拟器上测试情况如下: 使用PathClassLoader加载/sdcard/MyTools.apk失败(加载/data/data/com.wtz.testo/files/jar/MyTools.apk也报一样的错): 02-06 06:22:17.878 3019-3019/com.wtz.testo D/MainActivity: button3.onClick 02-06 06:22:17.878 3019-3019/com.wtz.testo E/dalvikvm: Dex cache directory isn't writable: /data/dalvik-cache 02-06 06:22:17.878 3019-3019/com.wtz.testo I/dalvikvm: Unable to open or create cache for /storage/sdcard/MyTools.apk (/data/dalvik-cache/storage@sdcard@MyTools.apk@classes.dex) 02-06 06:22:17.878 3019-3019/com.wtz.testo W/System.err: java.lang.ClassNotFoundException: Didn't find class "com.wtz.tools.DateTimeUtil" on path: DexPathList[[zip file "/sdcard/MyTools.apk"],nativeLibraryDirectories=[/vendor/lib, /system/lib]] 02-06 06:22:17.888 3019-3019/com.wtz.testo W/System.err: at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56) 02-06 06:22:17.888 3019-3019/com.wtz.testo W/System.err: at java.lang.ClassLoader.loadClass(ClassLoader.java:497) 02-06 06:22:17.888 3019-3019/com.wtz.testo W/System.err: at java.lang.ClassLoader.loadClass(ClassLoader.java:457) 02-06 06:22:17.888 3019-3019/com.wtz.testo W/System.err: at com.wtz.testo.utils.DexTools.loadDex(DexTools.java:27) ...... 02-06 06:22:17.888 3019-3019/com.wtz.testo W/System.err: Suppressed: java.io.IOException: unable to open DEX file 02-06 06:22:17.898 3019-3019/com.wtz.testo W/System.err: at dalvik.system.DexFile.openDexFileNative(Native Method) 02-06 06:22:17.898 3019-3019/com.wtz.testo W/System.err: at dalvik.system.DexFile.openDexFile(DexFile.java:296) 02-06 06:22:17.898 3019-3019/com.wtz.testo W/System.err: at dalvik.system.DexFile.<init>(DexFile.java:80) 02-06 06:22:17.898 3019-3019/com.wtz.testo W/System.err: at dalvik.system.DexFile.<init>(DexFile.java:59) 02-06 06:22:17.898 3019-3019/com.wtz.testo W/System.err: at dalvik.system.DexPathList.loadDexFile(DexPathList.java:263) 02-06 06:22:17.898 3019-3019/com.wtz.testo W/System.err: at dalvik.system.DexPathList.makeDexElements(DexPathList.java:230) 02-06 06:22:17.898 3019-3019/com.wtz.testo W/System.err: at dalvik.system.DexPathList.<init>(DexPathList.java:112) 02-06 06:22:17.898 3019-3019/com.wtz.testo W/System.err: at dalvik.system.BaseDexClassLoader.<init>(BaseDexClassLoader.java:48) 02-06 06:22:17.898 3019-3019/com.wtz.testo W/System.err: at dalvik.system.PathClassLoader.<init>(PathClassLoader.java:65) 02-06 06:22:17.898 3019-3019/com.wtz.testo W/System.err: at com.wtz.testo.utils.DexTools.loadDex(DexTools.java:23) 02-06 06:22:17.898 3019-3019/com.wtz.testo W/System.err: ... 15 more 使用DexClassLoader 加载/sdcard/ MyTools.apk成功: 02-06 06:30:50.318 10700-10700/com.wtz.testo D/MainActivity: button3.onClick 02-06 06:30:50.368 10700-10700/com.wtz.testo D/dalvikvm: DexOpt: --- BEGIN 'MyTools.apk' (bootstrap=0) --- 02-06 06:30:50.508 10700-10700/com.wtz.testo D/dalvikvm: DexOpt: --- END 'MyTools.apk' (success) --- 02-06 06:30:50.508 10700-10700/com.wtz.testo D/dalvikvm: DEX prep '/storage/sdcard/MyTools.apk': unzip in 16ms, rewrite 145ms 02-06 06:30:50.508 10700-10700/com.wtz.testo D/MainActivity: testLoadClassJar2...DateTimeUtil: 2018-02-06_06:30:50 02-06 06:31:06.848 10700-10700/com.wtz.testo D/MainActivity: button3.onClick 02-06 06:31:06.848 10700-10700/com.wtz.testo D/MainActivity: testLoadClassJar2...DateTimeUtil: 2018-02-06_06:31:06 查看优化目录“abc”: root@generic_x86:/data/data/com.wtz.testo/app_abc # ls MyTools.dex 在5.0模拟器上测试情况如下: 使用PathClassLoader加载/sdcard/MyTools.apk成功: 02-06 06:50:45.354 2931-2931/com.wtz.testo D/MainActivity: button3.onClick 02-06 06:50:45.437 2931-2931/com.wtz.testo D/MainActivity: testLoadClassJar2...DateTimeUtil: 2018-02-06_06:50:45 使用DexClassLoader 加载/sdcard/ MyTools.apk成功: 02-06 06:54:55.503 6689-6689/com.wtz.testo D/MainActivity: button3.onClick 02-06 06:54:56.647 6689-6689/com.wtz.testo D/MainActivity: testLoadClassJar2...DateTimeUtil: 2018-02-06_06:54:56 查看优化目录“abc”: root@generic_x86_64:/data/data/com.wtz.testo/app_abc # ls MyTools.dex 在8.0模拟器上测试情况如下: 在/data/data/pkg-name/下如预期DexClassLoader能正常加载jar,入参optimizedDirectory并没有产生对应的优化目录,但加载后jar同目录下生成了几个目录和文件如下: /data/data/com.wtz.testo/ └─files └─jar │-test_class_jar.jar │-test_class_jar.jar.prof └─oat └─x86_64 test_class_jar.odex test_class_jar.vdex 也可以在/sdcard目录下正常加载jar,只是别忘了在AndroidManifest.xml和代码动态申请权限两个地方申请对应的外部存储读权限。 否则会在dalvik.system.DexFile.openDexFileNative(Native Method)报错,对应到上层会报“ClassNotFoundException”: 02-01 17:38:08.930 27999-27999/com.wtz.testo W/System.err: java.lang.ClassNotFoundException: Didn't find class "com.test.java2.Student" on path: DexPathList[[zip file "/sdcard/test_class_jar.jar"],nativeLibraryDirectories=[/system/lib64, /vendor/lib64]] 02-01 17:38:08.930 27999-27999/com.wtz.testo W/System.err: at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:93) ...... 02-01 17:38:08.932 27999-27999/com.wtz.testo W/System.err: Suppressed: java.io.IOException: No original dex files found for dex location /sdcard/test_class_jar.jar 02-01 17:38:08.932 27999-27999/com.wtz.testo W/System.err: at dalvik.system.DexFile.openDexFileNative(Native Method) 但还是有一个问题,在连续操作几次加载/sdcard中的jar后,系统报错“ vdex wasn't explicitly flushed before destruction ”并且崩溃了: 01 20:41:55.314 30045-30045/com.wtz.testo D/MainActivity: button3.onClick 02-01 20:41:55.332 30045-30045/com.wtz.testo I/System.out: a = test loadJar 02-01 20:41:56.902 30045-30045/com.wtz.testo D/MainActivity: button3.onClick 02-01 20:41:56.913 30045-30045/com.wtz.testo I/System.out: a = test loadJar 02-01 20:41:58.284 30045-30045/com.wtz.testo D/MainActivity: button3.onClick 02-01 20:41:58.288 30045-30045/com.wtz.testo E/zygote64: File /storage/emulated/0/oat/x86_64/test_class_jar.vdex wasn't explicitly flushed before destruction. 02-01 20:41:58.288 30045-30045/com.wtz.testo E/zygote64: File /storage/emulated/0/oat/x86_64/test_class_jar.vdex wasn't explicitly closed before destruction. 02-01 20:41:58.288 30045-30045/com.wtz.testo A/zygote64: fd_file.cc:72] Check failed: guard_state_ >= GuardState::kClosed (guard_state_=Base, GuardState::kClosed=Closed) 02-01 20:41:58.454 30045-30052/com.wtz.testo W/zygote64: Suspending all threads took: 158.280ms 02-01 20:41:58.505 30045-30045/com.wtz.testo A/zygote64: runtime.cc:492] Runtime aborting... 02-01 20:41:58.505 30045-30045/com.wtz.testo A/zygote64: runtime.cc:492] Aborting thread: 02-01 20:41:58.505 30045-30045/com.wtz.testo A/zygote64: runtime.cc:492] "main" prio=10 tid=1 Native 02-01 20:41:58.505 30045-30045/com.wtz.testo A/zygote64: runtime.cc:492] | group="" sCount=0 dsCount=0 flags=0 obj=0x72818670 self=0x7fb3978bea00 02-01 20:41:58.505 30045-30045/com.wtz.testo A/zygote64: runtime.cc:492] | sysTid=30045 nice=-10 cgrp=default sched=0/0 handle=0x7fb39c7faa08 02-01 20:41:58.505 30045-30045/com.wtz.testo A/zygote64: runtime.cc:492] | state=R schedstat=( 165171692 506149647 230 ) utm=14 stm=2 core=0 HZ=100 02-01 20:41:58.505 30045-30045/com.wtz.testo A/zygote64: runtime.cc:492] | stack=0x7ffeb3de1000-0x7ffeb3de3000 stackSize=8MB 02-01 20:41:58.505 30045-30045/com.wtz.testo A/zygote64: runtime.cc:492] | held mutexes= "abort lock" 02-01 20:41:58.505 30045-30045/com.wtz.testo A/zygote64: runtime.cc:492] native: #00 pc 0000000000435cc5 /system/lib64/libart.so (_ZN3art15DumpNativeStackERNSt3__113basic_ostreamIcNS0_11char_traitsIcEEEEiP12BacktraceMapPKcPNS_9ArtMethodEPv+213) 这个目前没查到原因,但在/data/data/com.wtz.testo/files下加载jar共30多次和关闭程序重新打开后加载都不会出错,上述错说明在/sdcard上加载jar还是存在问题的,实际使用也不应该直接从/sdcard上加载jar。 使用PathClassLoader在/data/data/pkg-name/下和/sdcard/下加载也都能成功。 =========================================== DexClassLoader和PathClassLoader代码加载流程区别: (以下以5.0源码为例) /libcore/dalvik/src/main/java/dalvik/system/DexClassLoader.java public class DexClassLoader extends BaseDexClassLoader { public DexClassLoader(String dexPath, String optimizedDirectory, String libraryPath, ClassLoader parent) { super(dexPath, new File(optimizedDirectory), libraryPath, parent); } } /libcore/dalvik/src/main/java/dalvik/system/PathClassLoader.java public class PathClassLoader extends BaseDexClassLoader { public PathClassLoader(String dexPath, ClassLoader parent) { super(dexPath, null, null, parent); } public PathClassLoader(String dexPath, String libraryPath, ClassLoader parent) { super(dexPath, null, libraryPath, parent); } } 两者都继承BaseDexClassLoader,只是入参optimizedDirectory是否为空而已,从8.0开始,在DexPathList入口参数optimizedDirectory直接传入了null: /libcore/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java public BaseDexClassLoader(String dexPath, File optimizedDirectory, String libraryPath, ClassLoader parent) { super(parent); this.pathList = new DexPathList(this, dexPath, libraryPath, optimizedDirectory); } /libcore/dalvik/src/main/java/dalvik/system/DexPathList.java 在DexPathList构造方法中会执行: this.dexElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory, suppressedExceptions); 对于jar/apk/zip会调用: dex = loadDexFile(file, optimizedDirectory); 方法loadDexFile定义如下: private static DexFile loadDexFile(File file, File optimizedDirectory) throws IOException { if (optimizedDirectory == null) { return new DexFile(file); } else { String optimizedPath = optimizedPathFor(file, optimizedDirectory); return DexFile.loadDex(file.getPath(), optimizedPath, 0); } } 对于有optimizedDirectory参数而调用DexFile.loadDex会最终调用到如下: /libcore/dalvik/src/main/java/dalvik/system/DexFile.java static public DexFile loadDex(String sourcePathName, String outputPathName, int flags) throws IOException { return new DexFile(sourcePathName, outputPathName, flags); } 无论是new DexFile(file)还是new DexFile(sourcePathName, outputPathName, flags),最终都会调用到同样的方法,只是参数outputName是否为空而已: /libcore/dalvik/src/main/java/dalvik/system/DexFile.java private static long openDexFile(String sourceName, String outputName, int flags) throws IOException { // Use absolute paths to enable the use of relative paths when testing on host. return openDexFileNative( new File(sourceName).getAbsolutePath(),(outputName == null) ? null : new File(outputName).getAbsolutePath(), flags); }

转载请注明原文地址: https://www.6miu.com/read-2250224.html

最新回复(0)