二、完整代码示例
下面代码中
// 替换 LoadedApk 中的 类加载器 ClassLoader // 然后使用替换的类加载器加载 DEX 字节码文件中的 Activity 组件 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { startDexActivityWithoutReplacedClassLoader(this, mDexPath); }
就是先替换 LoadedApk 中的 类加载器 ClassLoader , 然后使用替换的类加载器加载 DEX 字节码文件中的 Activity 组件 ;
完整代码示例 :
package com.example.classloader_demo; import androidx.annotation.RequiresApi; import androidx.appcompat.app.AppCompatActivity; import android.content.Context; import android.content.Intent; import android.os.Build; import android.os.Bundle; import android.util.ArrayMap; import android.util.Log; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.lang.ref.WeakReference; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import dalvik.system.DexClassLoader; public class MainActivity extends AppCompatActivity { private static final String TAG = "MainActivity"; /** * Dex 文件路径 */ private String mDexPath; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 打印类加载器及父节点 classloaderLog(); // 拷贝 dex 文件 mDexPath = copyFile2(); // 测试 DEX 文件中的方法 testDex(this, mDexPath); // 拷贝 dex2 文件 //mDexPath = copyFile2(); // 启动 DEX 中的 Activity 组件 , 此处启动会失败 //startDexActivityWithoutClassLoader(this, mDexPath); // 替换 LoadedApk 中的 类加载器 ClassLoader // 然后使用替换的类加载器加载 DEX 字节码文件中的 Activity 组件 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { startDexActivityWithoutReplacedClassLoader(this, mDexPath); } } /** * 打印当前的类加载器及父节点 */ private void classloaderLog() { // 获取当前 Activity 的 类加载器 ClassLoader ClassLoader classLoader = MainActivity.class.getClassLoader(); // 打印当前 Activity 的 ClassLoader 类加载器 Log.i(TAG, "MainActivity ClassLoader : " + classLoader); // 获取 类加载器 父类 ClassLoader parentClassLoader = classLoader.getParent(); // 打印当前 Activity 的 ClassLoader 类加载器 的父类 Log.i(TAG, "MainActivity Parent ClassLoader : " + parentClassLoader); } /** * 将 app\src\main\assets\classes.dex 文件 , * 拷贝到 /data/user/0/com.example.classloader_demo/files/classes.dex 位置 */ private String copyFile() { // DEX 文件 File dexFile = new File(getFilesDir(), "classes.dex"); // DEX 文件路径 String dexPath = dexFile.getAbsolutePath(); Log.i(TAG, "开始拷贝文件 dexPath : " + dexPath); // 如果之前已经加载过 , 则退出 if (dexFile.exists()) { Log.i(TAG, "文件已经拷贝 , 退出"); return dexPath; } try { InputStream inputStream = getAssets().open("classes.dex"); FileOutputStream fileOutputStream = new FileOutputStream(dexPath); byte[] buffer = new byte[1024 * 4]; int readLen = 0; while ((readLen = inputStream.read(buffer)) != -1) { fileOutputStream.write(buffer, 0, readLen); } inputStream.close(); fileOutputStream.close(); } catch (IOException e) { e.printStackTrace(); } finally { Log.i("HSL", "classes.dex 文件拷贝完毕"); } return dexPath; } /** * 将 app\src\main\assets\classes2.dex 文件 , * 拷贝到 /data/user/0/com.example.classloader_demo/files/classes2.dex 位置 */ private String copyFile2() { // DEX 文件 File dexFile = new File(getFilesDir(), "classes2.dex"); // DEX 文件路径 String dexPath = dexFile.getAbsolutePath(); Log.i(TAG, "开始拷贝文件 dexPath : " + dexPath); // 如果之前已经加载过 , 则退出 if (dexFile.exists()) { Log.i(TAG, "文件已经拷贝 , 退出"); return dexPath; } try { InputStream inputStream = getAssets().open("classes2.dex"); FileOutputStream fileOutputStream = new FileOutputStream(dexPath); byte[] buffer = new byte[1024 * 4]; int readLen = 0; while ((readLen = inputStream.read(buffer)) != -1) { fileOutputStream.write(buffer, 0, readLen); } inputStream.close(); fileOutputStream.close(); } catch (IOException e) { e.printStackTrace(); } finally { Log.i("HSL", "classes2.dex 文件拷贝完毕"); } return dexPath; } /** * 测试调用 Dex 字节码文件中的方法 * * @param context * @param dexFilePath */ private void testDex(Context context, String dexFilePath) { // 优化目录 File optFile = new File(getFilesDir(), "opt_dex"); // 依赖库目录 , 用于存放 so 文件 File libFile = new File(getFilesDir(), "lib_path"); // 初始化 DexClassLoader DexClassLoader dexClassLoader = new DexClassLoader( dexFilePath, // Dex 字节码文件路径 optFile.getAbsolutePath(), // 优化目录 libFile.getAbsolutePath(), // 依赖库目录 context.getClassLoader() // 父节点类加载器 ); // 加载 com.example.dex_demo.DexTest 类 // 该类中有可执行方法 test() Class<?> clazz = null; try { clazz = dexClassLoader.loadClass("com.example.dex_demo.DexTest"); } catch (ClassNotFoundException e) { e.printStackTrace(); } // 获取 com.example.dex_demo.DexTest 类 中的 test() 方法 if (clazz != null) { try { // 获取 test 方法 Method method = clazz.getDeclaredMethod("test"); // 获取 Object 对象 Object object = clazz.newInstance(); // 调用 test() 方法 method.invoke(object); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } } /** * 不修改类加载器的前提下 , 运行 Dex 字节码文件中的组件 * * @param context * @param dexFilePath */ private void startDexActivityWithoutClassLoader(Context context, String dexFilePath) { // 优化目录 File optFile = new File(getFilesDir(), "opt_dex"); // 依赖库目录 , 用于存放 so 文件 File libFile = new File(getFilesDir(), "lib_path"); // 初始化 DexClassLoader DexClassLoader dexClassLoader = new DexClassLoader( dexFilePath, // Dex 字节码文件路径 optFile.getAbsolutePath(), // 优化目录 libFile.getAbsolutePath(), // 依赖库目录 context.getClassLoader() // 父节点类加载器 ); // 加载 com.example.dex_demo.DexTest 类 // 该类中有可执行方法 test() Class<?> clazz = null; try { clazz = dexClassLoader.loadClass("com.example.dex_demo.MainActivity2"); } catch (ClassNotFoundException e) { e.printStackTrace(); } // 启动 com.example.dex_demo.MainActivity2 组件 if (clazz != null) { context.startActivity(new Intent(context, clazz)); } } /** * 替换 LoadedApk 中的 类加载器 ClassLoader * * @param context * @param dexFilePath */ @RequiresApi(api = Build.VERSION_CODES.KITKAT) private void startDexActivityWithoutReplacedClassLoader(Context context, String dexFilePath) { // 优化目录 File optFile = new File(getFilesDir(), "opt_dex"); // 依赖库目录 , 用于存放 so 文件 File libFile = new File(getFilesDir(), "lib_path"); // 初始化 DexClassLoader DexClassLoader dexClassLoader = new DexClassLoader( dexFilePath, // Dex 字节码文件路径 optFile.getAbsolutePath(), // 优化目录 libFile.getAbsolutePath(), // 依赖库目录 context.getClassLoader() // 父节点类加载器 ); //------------------------------------------------------------------------------------------ // 下面开始替换 LoadedApk 中的 ClassLoader // I. 获取 ActivityThread 实例对象 // 获取 ActivityThread 字节码类 , 这里可以使用自定义的类加载器加载 // 原因是 基于 双亲委派机制 , 自定义的 DexClassLoader 无法加载 , 但是其父类可以加载 // 即使父类不可加载 , 父类的父类也可以加载 Class<?> ActivityThreadClass = null; try { ActivityThreadClass = dexClassLoader.loadClass( "android.app.ActivityThread"); } catch (ClassNotFoundException e) { e.printStackTrace(); } // 获取 ActivityThread 中的 sCurrentActivityThread 成员 // 获取的字段如下 : // private static volatile ActivityThread sCurrentActivityThread; // 获取字段的方法如下 : // public static ActivityThread currentActivityThread() {return sCurrentActivityThread;} Method currentActivityThreadMethod = null; try { currentActivityThreadMethod = ActivityThreadClass.getDeclaredMethod( "currentActivityThread"); // 设置可访问性 , 所有的 方法 , 字段 反射 , 都要设置可访问性 currentActivityThreadMethod.setAccessible(true); } catch (NoSuchMethodException e) { e.printStackTrace(); } // 执行 ActivityThread 的 currentActivityThread() 方法 , 传入参数 null Object activityThreadObject = null; try { activityThreadObject = currentActivityThreadMethod.invoke(null); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } // II. 获取 LoadedApk 实例对象 // 获取 ActivityThread 实例对象的 mPackages 成员 // final ArrayMap<String, WeakReference<LoadedApk>> mPackages = new ArrayMap<>(); Field mPackagesField = null; try { mPackagesField = ActivityThreadClass.getDeclaredField("mPackages"); // 设置可访问性 , 所有的 方法 , 字段 反射 , 都要设置可访问性 mPackagesField.setAccessible(true); } catch (NoSuchFieldException e) { e.printStackTrace(); } // 从 ActivityThread 实例对象 activityThreadObject 中 // 获取 mPackages 成员 ArrayMap mPackagesObject = null; try { mPackagesObject = (ArrayMap) mPackagesField.get(activityThreadObject); } catch (IllegalAccessException e) { e.printStackTrace(); } // 获取 WeakReference<LoadedApk> 弱引用对象 WeakReference weakReference = (WeakReference) mPackagesObject.get(this.getPackageName()); // 获取 LoadedApk 实例对象 Object loadedApkObject = weakReference.get(); // III. 替换 LoadedApk 实例对象中的 mClassLoader 类加载器 // 加载 android.app.LoadedApk 类 Class LoadedApkClass = null; try { LoadedApkClass = dexClassLoader.loadClass("android.app.LoadedApk"); } catch (ClassNotFoundException e) { e.printStackTrace(); } // 通过反射获取 private ClassLoader mClassLoader; 类加载器对象 Field mClassLoaderField = null; try { mClassLoaderField = LoadedApkClass.getDeclaredField("mClassLoader"); // 设置可访问性 mClassLoaderField.setAccessible(true); } catch (NoSuchFieldException e) { e.printStackTrace(); } // 替换 mClassLoader 成员 try { mClassLoaderField.set(loadedApkObject, dexClassLoader); } catch (IllegalAccessException e) { e.printStackTrace(); } //------------------------------------------------------------------------------------------ // 加载 com.example.dex_demo.DexTest 类 // 该类中有可执行方法 test() Class<?> clazz = null; try { clazz = dexClassLoader.loadClass("com.example.dex_demo.MainActivity2"); } catch (ClassNotFoundException e) { e.printStackTrace(); } // 启动 com.example.dex_demo.MainActivity2 组件 if (clazz != null) { context.startActivity(new Intent(context, clazz)); } } }
三、执行结果
执行结果 :
2021-12-12 11:49:06.816 26145-26145/com.example.classloader_demo I/MainActivity: MainActivity ClassLoader : dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.example.classloader_demo-Ij2qh4a32d4VghdA5YWXdA==/base.apk"],nativeLibraryDirectories=[/data/app/com.example.classloader_demo-Ij2qh4a32d4VghdA5YWXdA==/lib/arm64, /system/lib64]]] 2021-12-12 11:49:06.816 26145-26145/com.example.classloader_demo I/MainActivity: MainActivity Parent ClassLoader : java.lang.BootClassLoader@6457c5 2021-12-12 11:49:06.817 26145-26145/com.example.classloader_demo I/MainActivity: 开始拷贝文件 dexPath : /data/user/0/com.example.classloader_demo/files/classes2.dex 2021-12-12 11:49:06.879 26145-26145/com.example.classloader_demo I/HSL: classes2.dex 文件拷贝完毕 2021-12-12 11:49:07.939 26145-26145/com.example.classloader_demo I/lassloader_dem: The ClassLoaderContext is a special shared library. 2021-12-12 11:49:07.942 26145-26145/com.example.classloader_demo I/DexTest: DexTest : Hello World!!! 2021-12-12 11:49:07.943 26145-26145/com.example.classloader_demo I/lassloader_dem: The ClassLoaderContext is a special shared library. 2021-12-12 11:49:07.945 26145-26145/com.example.classloader_demo W/lassloader_dem: Accessing hidden method Landroid/app/ActivityThread;->currentActivityThread()Landroid/app/ActivityThread; (light greylist, reflection) 2021-12-12 11:49:07.945 26145-26145/com.example.classloader_demo W/lassloader_dem: Accessing hidden field Landroid/app/ActivityThread;->mPackages:Landroid/util/ArrayMap; (light greylist, reflection) 2021-12-12 11:49:07.945 26145-26145/com.example.classloader_demo W/lassloader_dem: Accessing hidden field Landroid/app/LoadedApk;->mClassLoader:Ljava/lang/ClassLoader; (light greylist, reflection) 2021-12-12 11:49:07.995 26145-26145/com.example.classloader_demo W/lassloader_dem: Accessing hidden method Landroid/graphics/Insets;->of(IIII)Landroid/graphics/Insets; (light greylist, linking) 2021-12-12 11:49:08.008 26145-26220/com.example.classloader_demo I/Adreno: QUALCOMM build : 6fb5a5b, Ife855c4895 Build Date : 08/21/18 OpenGL ES Shader Compiler Version: EV031.25.00.00 Local Branch : Googledrop_0815 Remote Branch : Remote Branch : Reconstruct Branch : 2021-12-12 11:49:08.008 26145-26220/com.example.classloader_demo I/Adreno: Build Config : S L 4.0.10 AArch64 2021-12-12 11:49:08.011 26145-26220/com.example.classloader_demo I/Adreno: PFP: 0x005ff110, ME: 0x005ff066 2021-12-12 11:49:08.013 26145-26220/com.example.classloader_demo I/ConfigStore: android::hardware::configstore::V1_0::ISurfaceFlingerConfigs::hasWideColorDisplay retrieved: 1 2021-12-12 11:49:08.014 26145-26220/com.example.classloader_demo I/ConfigStore: android::hardware::configstore::V1_0::ISurfaceFlingerConfigs::hasHDRDisplay retrieved: 0 2021-12-12 11:49:08.014 26145-26220/com.example.classloader_demo I/OpenGLRenderer: Initialized EGL, version 1.4 2021-12-12 11:49:08.015 26145-26145/com.example.classloader_demo W/ActivityThread: handleWindowVisibility: no activity for token android.os.BinderProxy@463dd0 2021-12-12 11:49:08.036 26145-26145/com.example.classloader_demo I/MainActivity2: com.example.dex_demo.MainActivity2 onCreate 执行成功 !!!
启动的 DEX 中的 Activity 组件如下 :
package com.example.dex_demo; import android.os.Bundle; import android.util.Log; import androidx.appcompat.app.AppCompatActivity; public class MainActivity2 extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Log.i("MainActivity2", "com.example.dex_demo.MainActivity2 onCreate 执行成功 !!!"); } }
在上面的日志中 , 成功打印出了 com.example.dex_demo.MainActivity2 onCreate 执行成功 !!! 内容 , 说明 com.example.dex_demo.MainActivity2 执行成功 ;