这篇文章主要写一下我使用 dexposed 的一个简单流程。
一开始就不多废话了,总之dexposed是阿里巴巴新开源的一个非侵入式的AOP框架。
GitHub传送门:https://github.com/alibaba/dexposed
以下翻译自 GitHub:
1、Dexposed是什么:
Dexposed 是Android应用开发一个功能强大且非侵入式的运行时AOP(面向方向编程)框架,他是基于开源的Xposed框架实现的。
Dexposed 的AOP实现是完全非侵入式的,没有使用任何注解处理器,编织器或字节码重写。集成Dexposed框架很简单,只需要使用一行代码,在程序的初始化阶段加载一个小小的JNI库即可。
Dexposed 不仅可以hook你自己的程序代码,也可以hook你的应用程序中调用的Android框架中的函数。开发者经常严重的依赖Fragment碎片化来开发程序,这个功能对这一情况大有用处。
机遇动态加载的机制,运行中的 app可以加载一小段经过编译的Java AOP代码,在不需要重新启动程序的情况下改变目标app的行为。
2、经典实用场景
1、经典AOP编程
2、仪表化(Instrumentation),用于测试,性能监控等
3、在线热修复,用于修复,紧急漏洞,安全漏洞、
4、SDK hook,以提供更好的开发体验。
3、集成
这部分是我个人集成详细过程,和官方给的方法可能有出入。(因为我按它说的做根本没弄好(⊙﹏⊙)b)
首先先把 GitHub上的东西下载下来解压后打开,目录如下:
在 Android Studio上新建项目DexposedDemo。把dexposed\src\main目录下的jniLibs文件夹拷贝到自己项目的src/main目录下。在自己的项目中添加一个包,取名为com.taobao.android.dexposed ,接着把dexposed\src\main\java\com\taobao\android\dexposed 下的源码都复制到com.taobao.android.dexposed包中。。添加后,在Project视图中,目录结构如下:
接下来检测设备是否支持 dexposed 框架,根据官方文档,因在程序中今早进行检测,所以这段代码被放到MyApplication类中。
package com.bajie.dexposed;
import android.app.Application;
import android.os.Build;
import android.util.Log;
import com.taobao.android.dexposed.DexposedBridge;
public class MyApplication extends Application {
private static final String TAG = "MyApplication";
private boolean mCanDexposed = false;
@Override
public void onCreate() {
super.onCreate();
mCanDexposed = DexposedBridge.canDexposed(this);
Log.i(TAG, "canDexposed = " + mCanDexposed);
}
public boolean canDexposed() {
return mCanDexposed;
}
}
别忘了在 manifest文件中配置application哦:
<application
android:name=".MyApplication" />
4.基本使用方法
针对一个方法,可以用三种方法对它进行改造,分别是:方法前,方法后,替代方法。
比如有一个显示 Toast的方法如下:
private void showToast() {
toast = Toast.makeText(this, "this is a toast", Toast.LENGTH_SHORT);
}
我们想在这个方法执行前或执行后在一个 TextView中显示消息,可以这样实现:
private void hookShowToast() {
DexposedBridge.findAndHookMethod(this.getClass(), "showToast", new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
}
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
tv.setText("showToast() afterHooked");
}
});
}
Dexposed 框架还可以替换掉整个函数:
private void methodReplacement() {
if(canDexposed) {
DexposedBridge.findAndHookMethod(this.getClass(), "showToast", new XC_MethodReplacement() {
@Override
protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {
Toast.makeText(MainActivity.this, "this is a replacement toast", Toast.LENGTH_SHORT).show();
return null;
}
});
}
}
Dexposed 还拥有热补丁的方法。就是在项目上线的时候,在不发布新版本的情况下,动态修复线上的bug的功能。
首先先建一个 Patch项目作为补丁,引入dexposedbridge.jar和patchloader.jar,在目标项目中加入com.taobao.android.dexposed.patch 下面的源码,这里需要注意,需要在目标项目中加入读SdCard的权限。
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
首先在 目标项目中添加此方法
public void runPathApk() {
String fullPath = Environment.getExternalStorageDirectory() + File.separator + "patch.apk";
setText("\n" + fullPath);
PatchResult result = PatchMain.load(this, fullPath, null);
if (result.isSuccess()) {
setText("patch success!");
} else {
setText("\n" + result.getErrorInfo());
}
}
其中 fullPath为补丁apk的路径,可以自定义,当然在路径中你需要放入补丁apk。
接着在补丁中添加类:
public class ToastPach implements IPatch {
@Override
public void handlePatch(PatchParam patchParam) throws Throwable {
final Class<?> cls = patchParam.context.getClassLoader().loadClass("com.bajie.dexposed.MainActivity");
DexposedBridge.findAndHookMethod(cls, "showToast", new XC_MethodReplacement() {
@Override
protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {
Toast.makeText((Activity)param.thisObject, "success", Toast.LENGTH_LONG).show();
return null;
}
});
}
}
补丁项目中只需要实现此类就可以,然后把补丁项目打包,放到相应的位置,运行原来的项目,在这里,showToast方法将被替换掉。
需要注意几点是,我当时用gradle:2.0.2-beta6进行运行的时候,没办法使用,后来改成gradle:1.5.0就可以运行了。