工欲善其事,必先利其器.我们在开发Xposed模块的时候,每一次修改Hook方法,都需要重启一次设备,很是麻烦,还浪费了很多宝贵的时间,有没有不重启的办法呢?当然有的,不然我在这里bb什么?
Android设备安装一个app后,系统会在/data/app/目录下保存一份原始的apk安装包,当我们覆盖安装这个app时,系统同样会删除旧的apk文件,而保留新的apk文件。同样的,我们的Xposed模块(即使没有界面)安装后,也会在/data/app/目录下保存或者更新这个apk。这里就是利用系统备份的这个apk做了些文章
不过在Android api_21之前和Android api_21及之后,系统在/data/app/目录下保存原始apk文件方式是不一样的。
假设当前程序的包名为com.dx.test
,则在api_21之前,保存方式为/data/app/com.dx.test-1.apk
或者/data/app/com.dx.test-2.apk
,而在api_21及之后保存方式为/data/app/com.dx.test-1/base.apk
或者/data/app/com.dx.test-2/base.apk
.那么xxx.xxx.xxx-1
和xxx.xxx.xxx-2
有什么区别呢?如果该app属于新安装则保存为xxx.xxx.xxx-1
,如果为覆盖安装则保存为xxx.xxx.xxx-2
。这样的话要找到这个文件不是很简单么?下面马上给出代码。
//通过包名寻找apk文件
private File findApkFile(String pkg){
//具体实现略
}
额。。。你TM在逗我么?大哥,我错了,不要打我。
完整的代码我已经上传到github了,下面放出链接!
apk文件倒是找到了,我们怎样才能实例化这个类,进而调用其中的方法呢?
Android虚拟机如同其他Java虚拟机一样,在运行程序时首先需要将对应的类加载到内存中,我们我们为什么不可以自己去加载一个类呢?这里我们自定义一个Classloader,并使用这个Classloader去加载这个apk文件,然后使用反射去实例化对象,并调用具体的方法。
/**调用真正的包含hook逻辑的方法
* @param pkg 当前app的packageName
* @param handleHookClass 指定由哪一个类处理相关的hook逻辑
* @param handleHookMethodName 处理相关的hook逻辑的方法名
* @param loadPackageParam 传入XC_LoadPackage.LoadPackageParam参数
* @throws Exception
*/
private void invokeHandleHookMethod(String pkg, String handleHookClass, String handleHookMethodName, XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
//利用上面实现的代码寻找apk文件
File apkFile = findApkFile(pkg);
//自定义Classloader
PathClassLoader pathClassLoader = new PathClassLoader(apkFile.getAbsolutePath(), ClassLoader.getSystemClassLoader());
try {
//使用反射的方式去调用具体的Hook逻辑
Class> cls = Class.forName(handleHookClass, true, pathClassLoader);
Object instance = cls.newInstance();
Method method = cls.getDeclaredMethod(handleHookMethodName, XC_LoadPackage.LoadPackageParam.class);
method.invoke(instance, loadPackageParam);
} catch (Exception e) {
throw e;
}
}
这里,我们假设有一个HookLogic
类,其中的doHook
方法中实现了具体的Hook逻辑,我们只需要在模块的入口处调用上面的invokeHandleHookMethod
方法,正真的Hook逻辑就会被调用了(注意xposed_init配置文件要指向HookLoader
).
//注意assets下面的xposed_init配置文件要指向这个类
public class HookLoader implements IXposedHookLoadPackage {
@Override
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
invokeHandleHookMethod(pkg, "xxx.xxx.HookLogic", "doHook", loadPackageParam);
}
此处省略若干行代码....
}
然后我们就可以愉快的玩耍了,我们只需要在HookLogic
类的doHook
方法中像以前一样实现具体的hook逻辑.
public class HookLogic{
@Override
public void doHook(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
if (loadPackageParam.packageName.equals("xxx.xxx.xxx")){
...此处省略具体的hook逻辑
}
}
}
当然,第一次安装还是需要重启设备的,重启后我们在HookLogic的doHook方法中的任何修改,都不需要再重启设备,只需要杀死宿主程序让其重新运行即可.
采用传统方式开发Xposed模块时,每次修改Hook逻辑时,都需要重启设备;
值得注意的是,由于这些hook代码提前加载进了Android虚拟几种,因此这种方式效率更高。
采用改进方式开发Xposed模块时,由于每次调用的Hook逻辑都是从apk文件中动态加载出来的,所以效率略慢,但优点是只需要在第一次安装时重启设备,大大提高了开发效率;
如果你对实际运行效率特别在意,可以在开发的时候使用改进方式,而在具体发布使用的时候修改配置文件,效率和传统方式是一样的.
完整代码: https://github.com/shuihuadx/XposedHook.git