1.前言
上一篇跟大家介绍了占位式插件化的实现方式,如果没有看过的小伙伴可以点击这个链接: Android插件化探索(一)占位式(插桩式)插件化详解
今天就跟大家来讲一下hook式插件化的实现方式。今天会用到比较多的反射,而且也会设计到比较多的源码。请小伙伴们耐心阅读完本篇文章。
上一篇说的占位式插件化有个缺点:就是要依赖于宿主的环境,比如context 上下文这些。本篇所讲的hook式插件化不会有这种问题。可以直接使用里面的环境,并不需要重写像 setContentView 、findViewById 这类依赖环境的方法。下面我们开始进入正题。
startActivity源码解析
我们上篇提到,调用 startActivity 启动一个没有在 Manifest.xml 文件中注册的 Activity 会报异常,那么我们今天从 startActivity 开始分析为什么会报异常。
startActivity 的流程大概是上面所示,最后执行到 AMS.startActivity 后如果没有在 Manifest.xml 中注册,软件就会发生崩溃, 那么怎么样才能避免这样呢。其实非常简单,我们只要把我们未注册的 Activity 替换成我们已经在 Manifest.xml 注册不就行了吗。那么问题又来了,我们应该如何实现替换操作呢?说到替换,我们不得不借助动态代理的方式来实现了。
ClassLoader loader: 类加载器
Class<?>[] interfaces: 需要监听的接口
InvocationHandler h: 监听的回调
说到这里,我们又延伸到另一个问题,我们应该监听哪一个接口呢?,其实从上面我们已经分析过了,我们应该要监听 AMS.startActivity 这一步,所以我们要监听的接口就是 IActivityTaskManager(9.0及以下是IActivityManager) 这个接口。接下来我们就对这个接口实现动态代理。
2.动态代理的实现
第一步:
我们可以通过 Class.forName() 得到 IActivityTaskManager 或者 IActivityManager 这个类,如下面代码所示:
Class<?> mIActivityManagerClass;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
mIActivityManagerClass = Class.forName("android.app.IActivityTaskManager");
} else {
mIActivityManagerClass = Class.forName("android.app.IActivityManager");
}
有了类之后,我们可以创建动态代理对象了,但是 IActivityTaskManager 或者 IActivityManager 接口的方法那么多,我们没必要全部监听,我们只需要监听我们关注的 "startActivity" 这个方法就好了,如下代码所示:
//创建动态代理
Object mActivityManagerProxy = Proxy.newProxyInstance(
getClassLoader(),//类加载器
new Class[]{mIActivityManagerClass},//要监听的回调接口
new InvocationHandler() {//回调的监听
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("startActivity".equals(method.getName())) {
//做自己的业务逻辑
//换成可以通过AMS检测的Activity
}
//为了程序能够正确执行,我们需要系统的IActivityManager实例
return method.invoke(需要系统的IActivityManager实例, args);
}
);
从上述代码中可以看出,要使动态代理生效,我们还需要一个 IActivityManager,查阅源码 ActivityTaskManager(8.0~9.0以下看 ActivityManager,8.0以下看 ActivityManagerNative) 可发现:
ActivityTaskManager.java:(Androdid10.0)
/** @hide */
public static IActivityTaskManager getService() {
return IActivityTaskManagerSingleton.get();
}
ActivityManager.java:(Android 8.0 ~ Android9.0)
/**
* @hide
*/
public static IActivityManager getService() {
return IActivityManagerSingleton.get();
}
ActivityManagerNative.java:(Android 7.0及以下)
/**
* Retrieve the system's default/global activity manager.
*/
static public IActivityManager getDefault() {
return gDefault.get();
}
通过 getService() 或者 getDefault() 可以返回一个我们需要的对象实例,我们接下来可以反射来执行该方法来获取 IActivityTaskManager 或者 IActivityManager 实例对象。代码如下:
//获取 ActivityManager 或 ActivityManagerNative 或 ActivityTaskManager
Class<?> mActivityManagerClass;
Method getActivityManagerMethod;
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1) {
mActivityManagerClass = Class.forName("android.app.ActivityManagerNative");
getActivityManagerMethod = mActivityManagerClass.getDeclaredMethod("getDefault");
} else if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1 && Build.VERSION.SDK_INT < Build.VERSION_CODES.Q){
mActivityManagerClass = Class.forName("android.app.ActivityManager");
getActivityManagerMethod = mActivityManagerClass.getDeclaredMethod("getService");
} else {
mActivityManagerClass = Class.forName("android.app.ActivityTaskManager");
getActivityManagerMethod = mActivityManagerClass.getDeclaredMethod("getService");
}
getActivityManagerMethod.setAccessible(true);
//这个实例本质是 IActivityManager或者IActivityTaskManager
final Object IActivityManager = getActivityManagerMethod.invoke(null);
现在有了 IActivityTaskManager 或者 IActivityManager 实例对象我们就可以让程序继续能够执行下去了。
第二步: 既然我们自己创建了 IActivityTaskManager 或者 IActivityManager 的动态代理,我们就要把原来系统的 IActivityTaskManager 或者 IActivityManager 实例对象给替换掉。还是通过上面的 getService() 或者 getDefault() 方法入手,我们继续跟踪代码发现: ActivityManager 或 ActivityManagerNative 或 ActivityTaskManager 都有一个 Singleton 共同的属性,我们查看一下这个类的源码:
public abstract class Singleton<T> {
private T mInstance;
protected abstract T create();
public final T get() {
synchronized (this) {
if (mInstance == null) {
mInstance = create();
}
return mInstance;
}
}
}
里面的 mInstance 属性正好是 IActivityTaskManager 或者 IActivityManager 实例,所以我们直接替换掉 mInstance 值就可以了。代码如下:
//获取 IActivityTaskManagerSingleton 或者 IActivityManagerSingleton 或者 gDefault 属性
Field mSingletonField;
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1) {
mSingletonField = mActivityManagerClass.getDeclaredField("gDefault");
} else if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1 && Build.VERSION.SDK_INT < Build.VERSION_CODES.Q){
mSingletonField = mActivityManagerClass.getDeclaredField("IActivityManagerSingleton");
} else {
mSingletonField = mActivityManagerClass.getDeclaredField("IActivityTaskManagerSingleton");
}
mSingletonField.setAccessible(true);
Object mSingleton = mSingletonField.get(null);
//替换点
Class<?> mSingletonClass = Class.forName("android.util.Singleton");
Field mInstanceField = mSingletonClass.getDeclaredField("mInstance");
mInstanceField.setAccessible(true);
//将我们创建的动态代理设置到 mInstance 属性当中
mInstanceField.set(mSingleton, mActivityManagerProxy);
到这里我们的动态代理算是实现好了,完整的代码如下:
private void hookAMSAction() throws Exception {
//动态代理
Class<?> mIActivityManagerClass;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
mIActivityManagerClass = Class.forName("android.app.IActivityTaskManager");
} else {
mIActivityManagerClass = Class.forName("android.app.IActivityManager");
}
//获取 ActivityManager 或 ActivityManagerNative 或 ActivityTaskManager
Class<?> mActivityManagerClass;
Method getActivityManagerMethod;
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1) {
mActivityManagerClass = Class.forName("android.app.ActivityManagerNative");
getActivityManagerMethod = mActivityManagerClass.getDeclaredMethod("getDefault");
} else if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1 && Build.VERSION.SDK_INT < Build.VERSION_CODES.Q){
mActivityManagerClass = Class.forName("android.app.ActivityManager");
getActivityManagerMethod = mActivityManagerClass.getDeclaredMethod("getService");
} else {
mActivityManagerClass = Class.forName("android.app.ActivityTaskManager");
getActivityManagerMethod = mActivityManagerClass.getDeclaredMethod("getService");
}
getActivityManagerMethod.setAccessible(true);
//这个实例本质是 IActivityManager或者IActivityTaskManager
final Object IActivityManager = getActivityManagerMethod.invoke(null);
//创建动态代理
Object mActivityManagerProxy = Proxy.newProxyInstance(
getClassLoader(),
new Class[]{mIActivityManagerClass},//要监听的回调接口
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("startActivity".equals(method.getName())) {
//做自己的业务逻辑
//换成可以通过AMS检测的Activity
Intent intent = new Intent(MyApplication.this, ProxyActivity.class);
intent.putExtra("actonIntent", (Intent)args[2]);
args[2] = intent;
}
//让程序继续能够执行下去
return method.invoke(IActivityManager, args);
}
}
);
//获取 IActivityTaskManagerSingleton 或者 IActivityManagerSingleton 或者 gDefault 属性
Field mSingletonField;
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1) {
mSingletonField = mActivityManagerClass.getDeclaredField("gDefault");
} else if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1 && Build.VERSION.SDK_INT < Build.VERSION_CODES.Q){
mSingletonField = mActivityManagerClass.getDeclaredField("IActivityManagerSingleton");
} else {
mSingletonField = mActivityManagerClass.getDeclaredField("IActivityTaskManagerSingleton");
}
mSingletonField.setAccessible(true);
Object mSingleton = mSingletonField.get(null);
//替换点
Class<?> mSingletonClass = Class.forName(Constans.SINGLETON);
Field mInstanceField = mSingletonClass.getDeclaredField("mInstance");
mInstanceField.setAccessible(true);
//将我们创建的动态代理设置到 mInstance 属性当中
mInstanceField.set(mSingleton, mActivityManagerProxy);
}
3.hook ActivityThread LaunchActivity
实现了动态代理,我们现在就要hook ActivityThread 了,为什么要 hook ActivityThread 这个类呢,因为我们的 Activity 启动的时候就是这个类给调用的,我们要把之前替换成代理 Activity 的目标 Activity 给替换回来,所以我们现在分析一下 ActivityThread 是如何启动一个 Activity 的。
ActivityThread.class:(Android 9.0 ~ 10.0)
ActivityThread.class:(Android 8.0及以下)
我们发现,Android 9.0 ~ 10.0 的代码比较绕,绕了一大圈最后执行了 handleLaunchActivity() 方法来启动一个 Activity,不管怎样,我们都找到了 hook点,那就是在 handleLaunchActivity() 执行之前把我们的目标 Activity 替换回来就可以了。在 handleLaunchActivity() 执行之前也就需要在 Handler 回调 handleMessage() 之前把它替换回来,那么 handleMessage() 是如何回调的呢,我们看一下 Handler 的源码发现,它是通过调用 **dispatchMessage() ** 来回调的,从代码里看到,有一个属性 "mCallback",如果我们设置了这个属性的值是不是就走我们自己的逻辑了?答案是yes。所以接下来我们就要找到 ActivityThread 的 Handler 对象。
查看 ActivityThread 源码发现,“mH” 属性就是我们需要的对象,所以我们先要得到这个对象,再把我们自己的 CallBack 设置到这里就可以执行我们的流程了。
得到 “mH” 对象之前我们先要获取到 ActivityThread 类和实例对象,通过 currentActivityThread() 可以获取到
private void hookLaunchActivity() throws Exception{
//获取 ActivityThread 类
Class<?> mActivityThreadClass = Class.forName("android.app.ActivityThread");
//获取 ActivityThread 的 currentActivityThread() 方法
Method currentActivityThread = mActivityThreadClass.getDeclaredMethod("currentActivityThread");
currentActivityThread.setAccessible(true);
//获取 ActivityThread 实例
Object mActivityThread = currentActivityThread.invoke(null);
//获取 ActivityThread 的 mH 属性
Field mHField = mActivityThreadClass.getDeclaredField("mH");
mHField.setAccessible(true);
Handler mH = (Handler) mHField.get(mActivityThread);
//获取 Handler 的 mCallback 属性
Field mCallbackField = Handler.class.getDeclaredField("mCallback");
mCallbackField.setAccessible(true);
//设置我们自定义的 CallBack
mCallbackField.set(mH, new MyCallBack());
}
class MyCallBack implements Handler.Callback{
@Override
public boolean handleMessage(@NonNull Message msg) {
return false;
}
}
执行了上面的代码,启动的时候就会先进入我们自定义的 handleMessage(),我们只需要在这方法里面把我们的目标的 Activity给替换回来就可以了。我们再次分析 handleLaunchActivity() 前的代码:
LaunchActivityItem.class:
我们可以发现,在调用 handleLaunchActivity() 之前都有一个 ActivityClientRecord 的属性,我们继续跟这个类:
注意: Android 9.0及以上版本我们可以只替换 LaunchActivityItem 里面的 mIntent 属性就可以了。所以代码实现如下:
class MyCallBack implements Handler.Callback{
@Override
public boolean handleMessage(@NonNull Message msg) {
//159 对应的是 EXECUTE_TRANSACTION
if (msg.what == 159) {
try {
Field mActivityCallbacksField = msg.obj.getClass().getDeclaredField("mActivityCallbacks");
mActivityCallbacksField.setAccessible(true);
List<Object> mActivityCallbacks = (List<Object>) mActivityCallbacksField.get(msg.obj);
if (mActivityCallbacks != null && mActivityCallbacks.size() > 0) {
Object mClientTransactionItem = mActivityCallbacks.get(0);
Class<?> mLaunchActivityItemClass = Class.forName("android.app.servertransaction.LaunchActivityItem");
if (mLaunchActivityItemClass.isInstance(mClientTransactionItem)) {
//获取 LaunchActivityItem 的 mIntent 属性
Field mIntentField = mClientTransactionItem.getClass().getDeclaredField("mIntent");
mIntentField.setAccessible(true);
Intent intent = (Intent) mIntentField.get(mClientTransactionItem);
//取出我们传递的值
Intent actonIntent = intent.getParcelableExtra("actonIntent");
if (actonIntent != null) {
//替换掉原来的intent属性的值
mIntentField.set(mClientTransactionItem, actonIntent);
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
//100 对应的是 LAUNCH_ACTIVITY
} else if (msg.what == 100) {
try {
//获取 ActivityClientRecord 的 intent 属性
Field intentField = msg.obj.getClass().getDeclaredField("intent");
intentField.setAccessible(true);
Intent intent = (Intent) intentField.get(msg.obj);
//取出我们传递的值
Intent actonIntent = intent.getParcelableExtra("actonIntent");
if (actonIntent != null) {
//替换掉原来的intent属性的值
intentField.set(msg.obj, actonIntent);
}
} catch (Exception e) {
e.printStackTrace();
}
}
return false;
}
}
到此我们就把我们的目标的 Activity 给替换回来了,这时候我们可以启动一个未在 Manifest.xml 注册的 Activity了。
4.加载插件apk的class文件
完成了动态代理及hook住了launchActivity 后我们就要加载插件中的Activity了,那么我们的Activity的类文件是如何被加载到内存当中的呢,我们还是从 startActivity() 开始看起吧。
根据源码一步步分析,我们可以得到 加载一个 Activity 需要非常复杂的步骤,由于步骤太多,我们直接进入主题,从 BaseDexClassLoader.findClass() 说起:
4.1 融合dexElements数组
要融合 dexElements数组,我们分五个步骤来进行。
第一步:找到宿主的dexElements 对象
我们首先要拿到 BaseDexClassLoader 中的 pathList 属性,然后再拿 PathList 里面的 dexElements 属性
//第一步:找到宿主的 dexElements 对象
//获取 BaseDexClassLoader 类
Class<?> mainBaseDexClassLoaderClass = Class.forName("dalvik.system.BaseDexClassLoader");
//获取 pathList 属性
Field mainPathListField = mainBaseDexClassLoaderClass.getDeclaredField("pathList");
mainPathListField.setAccessible(true);
//获取 PathClassLoader
PathClassLoader mainClassLoader = (PathClassLoader) context.getClassLoader();
//获取 pathList 属性值
Object mainPathList = mainPathListField.get(mainClassLoader);
//获取 dexElements
Field mainDexElementsField = mainPathList.getClass().getDeclaredField("dexElements");
mainDexElementsField.setAccessible(true);
Object mainDexElements = mainDexElementsField.get(mainPathList);
第二步:找到插件的dexElements 对象
第二步跟第一步差不多的,这是用的 ClassLoader不一样,插件用的是 DexClassLoader,而宿主的是用 PathClassLoader
//获取 BaseDexClassLoader 类
Class<?> pluginBaseDexClassLoaderClass = Class.forName("dalvik.system.BaseDexClassLoader");
//获取 pathList 属性
Field pluginPathListField = pluginBaseDexClassLoaderClass.getDeclaredField("pathList");
pluginPathListField.setAccessible(true);
//获取 DexClassLoader
File file = new File(path);
if (!file.exists()) {
Log.e(TAG, "插件不存在");
return;
}
File pluginDir = context.getDir("plugin", Context.MODE_PRIVATE);
//加载插件的class
dexClassLoader = new DexClassLoader(path, pluginDir.getAbsolutePath(), null, context.getClassLoader());
//获取 pathList 属性值
Object pluginPathList = pluginPathListField.get(dexClassLoader);
//获取 dexElements
Field pluginDexElementsField = pluginPathList.getClass().getDeclaredField("dexElements");
pluginDexElementsField.setAccessible(true);
Object pluginDexElements = pluginDexElementsField.get(pluginPathList);
第三步:创建出新的 dexElements 对象
//获取宿主 dexElements 跟插件 dexElements 的长度
int mainLength = Array.getLength(mainDexElements);
int pluginLength = Array.getLength(pluginDexElements);
int newLength = mainLength + pluginLength;
//创建新的数组
Object newDexElements = Array.newInstance(mainDexElements.getClass().getComponentType(), newLength);
第四步:新的 dexElements = 宿主的dexElements + 插件的dexElements
for (int i = 0; i < newLength; i++) {
if (i < mainLength) {
Array.set(newDexElements, i, Array.get(mainDexElements, i));
} else {
Array.set(newDexElements, i, Array.get(pluginDexElements, i - mainLength));
}
}
第五步:新的 dexElements设置到宿主当中去
mainDexElementsField.set(mainPathList, newDexElements);
通过上面五个步骤,我们就把宿主和插件的 dexElements 数组给融合好了,这时候我们宿主APP就可以加载插件apk的class文件了。
4.2加载插件的资源
融合好 dexElements数组后,我们开始加载插件的资源了,加载资源比较简单,上一篇文章中也有提到,这里就直接贴出代码:
public void loadPluginResource(final Handler handler, final String path) {
try {
//加载插件的资源文件
//1、获取插件的AssetManager
assetManager = AssetManager.class.newInstance();
Method addAssetPath = AssetManager.class.getMethod("addAssetPath", String.class);
addAssetPath.setAccessible(true);
addAssetPath.invoke(assetManager, path);
//2、获取宿主的Resources
Resources appResources = context.getResources();
//实例化插件的Resources
pluginResource = new Resources(assetManager, appResources.getDisplayMetrics(), appResources.getConfiguration());
if (dexClassLoader != null && pluginResource != null) {
handler.sendEmptyMessage(666);
} else {
handler.sendEmptyMessage(0);
}
} catch (Exception e) {
e.printStackTrace();
handler.sendEmptyMessage(0);
}
}
到这里我们的hook式插件化框架已经写完了,我们看一下运行效果:
HookManager.class
public class HookManager {
private Context context;
private static HookManager instance;
private HookManager(Context context) {
this.context = context;
}
public static HookManager getInstance(Context context) {
if (instance == null) {
synchronized (PluginManager.class) {
if (instance == null) {
instance = new HookManager(context);
}
}
}
return instance;
}
public void hookAMSAction() throws Exception {
//动态代理
Class<?> mIActivityManagerClass;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
mIActivityManagerClass = Class.forName("android.app.IActivityTaskManager");
} else {
mIActivityManagerClass = Class.forName("android.app.IActivityManager");
}
//获取 ActivityManager 或 ActivityManagerNative 或 ActivityTaskManager
Class<?> mActivityManagerClass;
Method getActivityManagerMethod;
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1) {
mActivityManagerClass = Class.forName("android.app.ActivityManagerNative");
getActivityManagerMethod = mActivityManagerClass.getDeclaredMethod("getDefault");
} else if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1 && Build.VERSION.SDK_INT < Build.VERSION_CODES.Q){
mActivityManagerClass = Class.forName("android.app.ActivityManager");
getActivityManagerMethod = mActivityManagerClass.getDeclaredMethod("getService");
} else {
mActivityManagerClass = Class.forName("android.app.ActivityTaskManager");
getActivityManagerMethod = mActivityManagerClass.getDeclaredMethod("getService");
}
getActivityManagerMethod.setAccessible(true);
//这个实例本质是 IActivityManager或者IActivityTaskManager
final Object IActivityManager = getActivityManagerMethod.invoke(null);
//创建动态代理
Object mActivityManagerProxy = Proxy.newProxyInstance(
context.getClassLoader(),
new Class[]{mIActivityManagerClass},//要监听的回调接口
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("startActivity".equals(method.getName())) {
//做自己的业务逻辑
//换成可以通过AMS检测的Activity
Intent intent = new Intent(context, ProxyActivity.class);
intent.putExtra("actonIntent", (Intent)args[2]);
args[2] = intent;
}
//让程序继续能够执行下去
return method.invoke(IActivityManager, args);
}
}
);
//获取 IActivityTaskManagerSingleton 或者 IActivityManagerSingleton 或者 gDefault 属性
Field mSingletonField;
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1) {
mSingletonField = mActivityManagerClass.getDeclaredField("gDefault");
} else if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1 && Build.VERSION.SDK_INT < Build.VERSION_CODES.Q){
mSingletonField = mActivityManagerClass.getDeclaredField("IActivityManagerSingleton");
} else {
mSingletonField = mActivityManagerClass.getDeclaredField("IActivityTaskManagerSingleton");
}
mSingletonField.setAccessible(true);
Object mSingleton = mSingletonField.get(null);
//替换点
Class<?> mSingletonClass = Class.forName(Constans.SINGLETON);
Field mInstanceField = mSingletonClass.getDeclaredField("mInstance");
mInstanceField.setAccessible(true);
//将我们创建的动态代理设置到 mInstance 属性当中
mInstanceField.set(mSingleton, mActivityManagerProxy);
}
public void hookLaunchActivity() throws Exception{
//获取 ActivityThread 类
Class<?> mActivityThreadClass = Class.forName("android.app.ActivityThread");
//获取 ActivityThread 的 currentActivityThread() 方法
Method currentActivityThread = mActivityThreadClass.getDeclaredMethod("currentActivityThread");
currentActivityThread.setAccessible(true);
//获取 ActivityThread 实例
Object mActivityThread = currentActivityThread.invoke(null);
//获取 ActivityThread 的 mH 属性
Field mHField = mActivityThreadClass.getDeclaredField("mH");
mHField.setAccessible(true);
Handler mH = (Handler) mHField.get(mActivityThread);
//获取 Handler 的 mCallback 属性
Field mCallbackField = Handler.class.getDeclaredField("mCallback");
mCallbackField.setAccessible(true);
//设置我们自定义的 CallBack
mCallbackField.set(mH, new MyCallBack());
}
class MyCallBack implements Handler.Callback{
@Override
public boolean handleMessage(@NonNull Message msg) {
if (msg.what == Constans.EXECUTE_TRANSACTION) {
try {
Field mActivityCallbacksField = msg.obj.getClass().getDeclaredField("mActivityCallbacks");
mActivityCallbacksField.setAccessible(true);
List<Object> mActivityCallbacks = (List<Object>) mActivityCallbacksField.get(msg.obj);
if (mActivityCallbacks != null && mActivityCallbacks.size() > 0) {
Object mClientTransactionItem = mActivityCallbacks.get(0);
Class<?> mLaunchActivityItemClass = Class.forName("android.app.servertransaction.LaunchActivityItem");
if (mLaunchActivityItemClass.isInstance(mClientTransactionItem)) {
//获取 LaunchActivityItem 的 mIntent 属性
Field mIntentField = mClientTransactionItem.getClass().getDeclaredField("mIntent");
mIntentField.setAccessible(true);
Intent intent = (Intent) mIntentField.get(mClientTransactionItem);
//取出我们传递的值
Intent actonIntent = intent.getParcelableExtra("actonIntent");
if (actonIntent != null) {
//替换掉原来的intent属性的值
mIntentField.set(mClientTransactionItem, actonIntent);
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
} else if (msg.what == Constans.LAUNCH_ACTIVITY) {
/*
7.0以下代码
case LAUNCH_ACTIVITY: {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
r.packageInfo = getPackageInfoNoCheck(
r.activityInfo.applicationInfo, r.compatInfo);
handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
} break;
*/
try {
//获取 ActivityClientRecord 的 intent 属性
Field intentField = msg.obj.getClass().getDeclaredField("intent");
intentField.setAccessible(true);
Intent intent = (Intent) intentField.get(msg.obj);
//取出我们传递的值
Intent actonIntent = intent.getParcelableExtra("actonIntent");
if (actonIntent != null) {
//替换掉原来的intent属性的值
intentField.set(msg.obj, actonIntent);
}
} catch (Exception e) {
e.printStackTrace();
}
}
return false;
}
}
}
PluginManager.class
public class PluginManager {
private static final String TAG = PluginManager.class.getSimpleName();
private static PluginManager instance;
private Context context;
private DexClassLoader dexClassLoader;
private AssetManager assetManager;
private Resources pluginResource;
private PluginManager(Context context) {
this.context = context;
}
public static PluginManager getInstance(Context context) {
if (instance == null) {
synchronized (PluginManager.class) {
if (instance == null) {
instance = new PluginManager(context);
}
}
}
return instance;
}
public void pluginToApp(final Handler handler, final String path) {
new Thread(new Runnable() {
@Override
public void run() {
try {
//第一步:找到宿主的 dexElements 对象
//获取 BaseDexClassLoader 类
Class<?> mainBaseDexClassLoaderClass = Class.forName("dalvik.system.BaseDexClassLoader");
//获取 pathList 属性
Field mainPathListField = mainBaseDexClassLoaderClass.getDeclaredField("pathList");
mainPathListField.setAccessible(true);
//获取 PathClassLoader
PathClassLoader mainClassLoader = (PathClassLoader) context.getClassLoader();
//获取 pathList 属性值
Object mainPathList = mainPathListField.get(mainClassLoader);
//获取 dexElements
Field mainDexElementsField = mainPathList.getClass().getDeclaredField("dexElements");
mainDexElementsField.setAccessible(true);
Object mainDexElements = mainDexElementsField.get(mainPathList);
//第二步:找到插件的dexElements 对象
//获取 BaseDexClassLoader 类
Class<?> pluginBaseDexClassLoaderClass = Class.forName("dalvik.system.BaseDexClassLoader");
//获取 pathList 属性
Field pluginPathListField = pluginBaseDexClassLoaderClass.getDeclaredField("pathList");
pluginPathListField.setAccessible(true);
//获取 DexClassLoader
File file = new File(path);
if (!file.exists()) {
Log.e(TAG, "插件不存在");
return;
}
File pluginDir = context.getDir("plugin", Context.MODE_PRIVATE);
//加载插件的class
dexClassLoader = new DexClassLoader(path, pluginDir.getAbsolutePath(), null, context.getClassLoader());
//获取 pathList 属性值
Object pluginPathList = pluginPathListField.get(dexClassLoader);
//获取 dexElements
Field pluginDexElementsField = pluginPathList.getClass().getDeclaredField("dexElements");
pluginDexElementsField.setAccessible(true);
Object pluginDexElements = pluginDexElementsField.get(pluginPathList);
//第三步:创建出新的 dexElements 对象
//获取宿主 dexElements 跟插件 dexElements 的长度
int mainLength = Array.getLength(mainDexElements);
int pluginLength = Array.getLength(pluginDexElements);
int newLength = mainLength + pluginLength;
//创建新的数组
Object newDexElements = Array.newInstance(mainDexElements.getClass().getComponentType(), newLength);
//第四步:新的 dexElements = 宿主的dexElements + 插件的dexElements
for (int i = 0; i < newLength; i++) {
if (i < mainLength) {
Array.set(newDexElements, i, Array.get(mainDexElements, i));
} else {
Array.set(newDexElements, i, Array.get(pluginDexElements, i - mainLength));
}
}
//第五步:新的 dexElements设置到宿主当中去
mainDexElementsField.set(mainPathList, newDexElements);
//加载插件的资源
loadPluginResource(handler, path);
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
public void loadPluginResource(final Handler handler, final String path) {
try {
//加载插件的资源文件
//1、获取插件的AssetManager
assetManager = AssetManager.class.newInstance();
Method addAssetPath = AssetManager.class.getMethod("addAssetPath", String.class);
addAssetPath.setAccessible(true);
addAssetPath.invoke(assetManager, path);
//2、获取宿主的Resources
Resources appResources = context.getResources();
//实例化插件的Resources
pluginResource = new Resources(assetManager, appResources.getDisplayMetrics(), appResources.getConfiguration());
if (dexClassLoader != null && pluginResource != null) {
handler.sendEmptyMessage(666);
} else {
handler.sendEmptyMessage(0);
}
} catch (Exception e) {
e.printStackTrace();
handler.sendEmptyMessage(0);
}
}
public Resources getResource() {
return pluginResource;
}
public DexClassLoader getClassLoader() {
return dexClassLoader;
}
public AssetManager getAssetManager() {
return assetManager;
}
}
MyApplication.class
public class MyApplication extends Application {
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
Reflection.unseal(base);
}
@Override
public void onCreate() {
super.onCreate();
try {
//先hook AMS检查
HookManager.getInstance(this).hookAMSAction();
//hook ActivityThread
HookManager.getInstance(this).hookLaunchActivity();
} catch (Exception e) {
}
}
@Override
public Resources getResources() {
return PluginManager.getInstance(this).getResource() == null ? super.getResources() : PluginManager.getInstance(this).getResource();
}
@Override
public AssetManager getAssets() {
return PluginManager.getInstance(this).getAssetManager() == null ? super.getAssets() : PluginManager.getInstance(this).getAssetManager();
}
}
MainActivity.class
public class MainActivity extends AppCompatActivity {
private ProgressDialog dialog;
//是否加载完成
private boolean isLoadSuccess = false;
private String path = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "plugin2.apk";
@SuppressLint("HandlerLeak")
private Handler handler = new Handler() {
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
if (msg.what == 666) {
isLoadSuccess = true;
if (dialog != null && dialog.isShowing()) {
dialog.dismiss();
}
Toast.makeText(MainActivity.this, "加载插件成功!", Toast.LENGTH_SHORT).show();
} else if (msg.what == 0) {
if (dialog != null && dialog.isShowing()) {
dialog.dismiss();
}
Toast.makeText(MainActivity.this, "加载插件失败,请检查插件是否存在!", Toast.LENGTH_SHORT).show();
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@RequiresApi(api = Build.VERSION_CODES.M)
public void loadPlugin(View view) {
//判断是否已经赋予权限
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
requestPermissions(new String[]{ Manifest.permission.WRITE_EXTERNAL_STORAGE}, 666);
} else {
showProgress();
PluginManager.getInstance(this).pluginToApp(handler, path);
}
} else {
showProgress();
PluginManager.getInstance(this).pluginToApp(handler, path);
}
}
public void jumpPlugin(View view) {
if (!isLoadSuccess) {
Toast.makeText(this, "请先加载插件", Toast.LENGTH_SHORT).show();
} else {
Intent intent = new Intent();
intent.setComponent(new ComponentName("com.yuong.plugin", "com.yuong.plugin.PluginActivity"));
startActivity(intent);
}
}
private void showProgress() {
if (dialog == null) {
dialog = new ProgressDialog(this);
}
dialog.setTitle("加载插件中,请稍后。。。");
dialog.setCancelable(false);
dialog.show();
}
}
插件的BaseActivity.class
public abstract class BaseActivity extends Activity {
@Override
public Resources getResources() {
if (getApplication() != null && getApplication().getResources() != null) {
//如果不为空,就说明已经被添加到了宿主当中
return getApplication().getResources();
}
return super.getResources();
}
@Override
public AssetManager getAssets() {
if (getApplication() != null && getApplication().getAssets() != null) {
//如果不为空,就说明已经被添加到了宿主当中
return getApplication().getAssets();
}
return super.getAssets();
}
@Override
public Resources.Theme getTheme() {
if (getApplication() != null && getApplication().getTheme() != null) {
//如果不为空,就说明已经被添加到了宿主当中
return getApplication().getTheme();
}
return super.getTheme();
}
}
项目地址:hook式插件化