Xposed免重启更新模块
Xposed模块在每次更新后都需要重启才能生效,公司给我的测试机是小米4,非常古董,每次重启都需要花费大量时间.而且手机系统是我刷的一个原生6.0的系统,总有些小bug,有时候需要重启很多次才行,等的我黄花菜都凉了.所以有必要把这个搞一搞了.
简单总结一下原理:安装模块时,Android系统会在data/app/对应包名的目录下保存原始apk,通过读取这个原始的apk,然后new一个PathClassLoader,该PathClassLoader用于加载写有hook逻辑的类,最后通过反射的方式完成hook的具体逻辑.
工具: XposedBridgeApi-54.jar
我使用的是一个已root的原生6.0系统的小米4
方法:1.新建一个HookLoader类,具体代码如下:
1 package com.example.xposedhook; 2 3 import android.app.Application; 4 import android.content.Context; 5 import android.content.pm.ApplicationInfo; 6 import android.content.pm.PackageManager; 7 8 import java.io.File; 9 10 import dalvik.system.PathClassLoader; 11 import de.robv.android.xposed.IXposedHookLoadPackage; 12 import de.robv.android.xposed.IXposedHookZygoteInit; 13 import de.robv.android.xposed.XC_MethodHook; 14 import de.robv.android.xposed.XposedBridge; 15 import de.robv.android.xposed.XposedHelpers; 16 import de.robv.android.xposed.callbacks.XC_LoadPackage; 17 18 /** 19 * @author DX 20 * 这种方案建议只在开发调试的时候使用,因为这将损耗一些性能(需要额外加载apk文件),调试没问题后,直接修改xposed_init文件为正确的类即可 21 * 可以实现免重启,由于存在缓存,需要杀死宿主程序以后才能生效 22 * Created by DX on 2017/10/4. 23 * Modified by chengxuncc on 2019/4/16. 24 */ 25 26 public class HookLoader implements IXposedHookLoadPackage, IXposedHookZygoteInit { 27 //按照实际使用情况修改下面几项的值 28 /** 29 * 当前Xposed模块的包名,方便寻找apk文件 30 */ 31 private final static String modulePackageName = HookLoader.class.getPackage().getName(); 32 33 /** 34 * 实际hook逻辑处理类 35 */ 36 private final String handleHookClass = HookLogic.class.getName(); 37 /** 38 * 实际hook逻辑处理类的入口方法 39 */ 40 private final String handleHookMethod = "handleLoadPackage"; 41 42 private final String initMethod = "initZygote"; 43 44 private IXposedHookZygoteInit.StartupParam startupparam; 45 46 /** 47 * 重定向handleLoadPackage函数前会执行initZygote 48 * 49 * @param loadPackageParam 50 * @throws Throwable 51 */ 52 @Override 53 public void handleLoadPackage(final XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable { 54 // 排除系统应用 55 if (loadPackageParam.appInfo == null || 56 (loadPackageParam.appInfo.flags & (ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP)) == 1) { 57 return; 58 } 59 //将loadPackageParam的classloader替换为宿主程序Application的classloader,解决宿主程序存在多个.dex文件时,有时候ClassNotFound的问题 60 XposedHelpers.findAndHookMethod(Application.class, "attach", Context.class, new XC_MethodHook() { 61 @Override 62 protected void afterHookedMethod(MethodHookParam param) throws Throwable { 63 Context context = (Context) param.args[0]; 64 loadPackageParam.classLoader = context.getClassLoader(); 65 Class<?> cls = getApkClass(context, modulePackageName, handleHookClass); 66 Object instance = cls.newInstance(); 67 try { 68 cls.getDeclaredMethod(initMethod, startupparam.getClass()).invoke(instance, startupparam); 69 }catch (NoSuchMethodException e){ 70 // 找不到initZygote方法 71 } 72 cls.getDeclaredMethod(handleHookMethod, loadPackageParam.getClass()).invoke(instance, loadPackageParam); 73 } 74 }); 75 } 76 77 /** 78 * 实现initZygote,保存启动参数。 79 * 80 * @param startupParam 81 */ 82 @Override 83 public void initZygote(IXposedHookZygoteInit.StartupParam startupParam) { 84 this.startupparam = startupParam; 85 } 86 87 private Class<?> getApkClass(Context context, String modulePackageName, String handleHookClass) throws Throwable { 88 File apkFile = findApkFile(context, modulePackageName); 89 if (apkFile == null) { 90 throw new RuntimeException("寻找模块apk失败"); 91 } 92 //加载指定的hook逻辑处理类,并调用它的handleHook方法 93 PathClassLoader pathClassLoader = new PathClassLoader(apkFile.getAbsolutePath(), ClassLoader.getSystemClassLoader()); 94 Class<?> cls = Class.forName(handleHookClass, true, pathClassLoader); 95 return cls; 96 } 97 98 /** 99 * 根据包名构建目标Context,并调用getPackageCodePath()来定位apk 100 * 101 * @param context context参数 102 * @param modulePackageName 当前模块包名 103 * @return apk file 104 */ 105 private File findApkFile(Context context, String modulePackageName) { 106 if (context == null) { 107 return null; 108 } 109 try { 110 Context moudleContext = context.createPackageContext(modulePackageName, Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY); 111 String apkPath = moudleContext.getPackageCodePath(); 112 return new File(apkPath); 113 } catch (PackageManager.NameNotFoundException e) { 114 e.printStackTrace(); 115 } 116 return null; 117 } 118 }
2.将代码第36行改为你自己写的hook逻辑的类.class.getName()即可,其它地方基本不用动.比如我写的hook类名字为XposedUtils,里面是具体的hook逻辑,只需将第36行代码改成:
3.将xposed_init文件程序入口处改为新建的HookLoader这个类.如:
4.大功告成,开始为所欲为.
注意:有的可能会抛出"寻找apk模块失败"的异常,这是因为在findApkFile方法中传入的modulePackageName可能不是当前模块的完整包名,可以手动改成app build.gradle中appcationId的值.如:
感谢大佬提供的方案.
原文:https://blog.csdn.net/u011956004/article/details/78612502