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 .Application;
4 import android.content.Context;
5 import .ApplicationInfo;
6 import .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的值.如:


感谢大佬提供的方案.
GitHub地址:https:///shuihuadx/XposedHook
















