在不同版本下通过Hook来启动插件activity的流程

不论哪个版本在启动插件Activity之前都需要通过DexPathLoader来进行相应的加载,这个部分不在此次内容中。
版本区分的话这里是根据Activity的启动流程差异来进行相应的替换,基本思路一致,版本划分的话主要是8.0以下,10.0以下,10.0及以上版本。

1.Hook替换Intent

    private const val TARGET_INTENT = "target_intent"
    fun hookAMS() {
        try {
            LogE(Build.VERSION.SDK_INT.toString())
            //1。获取Singleton对象
            val singletonFieldParams =
                when {
                    (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) -> {
                        //8.0以下
                        "android.app.ActivityManagerNative" to "gDefault"
                    }
                    (Build.VERSION.SDK_INT<Build.VERSION_CODES.Q)->{
                        //10.0以下
                        "android.app.ActivityManager" to "IActivityManagerSingleton"
                    }
                    else -> {
                        //10.0及以上
                        "android.app.ActivityTaskManager" to "IActivityTaskManagerSingleton"
                    }
                }
            val aClass = Class.forName(singletonFieldParams.first)
            val singletonField = aClass.getDeclaredField(singletonFieldParams.second)
            singletonField.isAccessible = true
            val singleton = singletonField[null]
            val singletonClazz = Class.forName("android.util.Singleton")
            val mInstanceFiled = singletonClazz.getDeclaredField("mInstance")
            mInstanceFiled.isAccessible = true
            val mInstance = mInstanceFiled[singleton]

            //2。获取Singleton中的IActivityTaskManager对象,区分版本10以下
            val proxyClass =
                if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
                    Class.forName("android.app.IActivityManager")
                } else {
                    Class.forName("android.app.IActivityTaskManager")
                }
            //3.创建动态代理
            val newProxyInstance = Proxy.newProxyInstance(
                Thread.currentThread().contextClassLoader,
                arrayOf(proxyClass)
            ) { proxy, method, args -> //这里已经代理了IActivityTaskManagerSingleton
                // 对象,那么它的方法执行都会到这里,那么我们只要要获取获取对应的方法,那么就可以获取方法中的参数
                //参数当中是有Intent,那么这里就可以修改为宿主中Activity来执行
                //只需要处理startActivities的方法
                //这里代理的对象方法是在android8以后的版本
                try {
                    var index = -1
                    if ("startActivity" == method.name) {
                        index = args.indexOfFirst { it is Intent }
                        val defIntent = args[index] as Intent
                        val proxyIntent = Intent()
                        proxyIntent.setClassName(
                            "com.arm.arount", "com" +
                                    ".arm.arount.ProxyActivity"
                        )
                        proxyIntent.putExtra(TARGET_INTENT, defIntent)
                        args[index] = proxyIntent
                    }
                    method.invoke(mInstance, *args.orEmpty())
                } catch (e: Exception) {
                    e.printStackTrace()
                }
            }
            //4.修改singleton的代理对象
            mInstanceFiled[singleton] = newProxyInstance
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

到上面一步我们已经完成了hook startActivity的流程,并且将启动插件的Intent替换为了宿主代理的Intent,并在其中保存了原始的Intent。那么下一步我们就需要在合适的实际替换回原始的Intent。

2.替换回原始Intent

先上代码:

fun hookHandleIntent() {
        try {
            //获取ActivityThread
            val aClass = Class.forName("android.app.ActivityThread")
            //获取它的静态实例对象
            val sCurrentActivityThread = aClass.getDeclaredField("sCurrentActivityThread")
            sCurrentActivityThread.isAccessible = true
            val activityThread = sCurrentActivityThread[null]
            val mHField = aClass.getDeclaredField("mH")
            mHField.isAccessible = true
            val handler = mHField[activityThread] as Handler
            val mCallbackField = Handler::class.java.getDeclaredField("mCallback")
            mCallbackField.isAccessible = true
            val callback = Handler.Callback { msg ->
                try {
                    when (msg.what) {
                        //Android8.0以下的处理的LaunchActivity
                        100 -> {
                            val intentField = msg.obj.javaClass.getDeclaredField("intent")
                            intentField.isAccessible = true
                            val proxyIntent: Intent = intentField.get(msg.obj) as Intent
                            val intent = proxyIntent.getParcelableExtra<Intent?>(TARGET_INTENT)
                            intent?.let {
                                intentField.set(msg.obj, it)
                            }
                        }
                        //8.0以上的生命周期入口
                        159 -> {
                            val mActivityCallbacksField = msg.obj.javaClass.getDeclaredField(
                                "mActivityCallbacks"
                            )
                            mActivityCallbacksField.isAccessible = true
                            val mActivityCallbacks = mActivityCallbacksField[msg.obj] as List<*>
                            val launchActivityItem = mActivityCallbacks.firstOrNull {
                                "android.app.servertransaction.LaunchActivityItem" == it?.javaClass?.name
                            }
                            launchActivityItem?.let {
                                val mIntentField = it.javaClass.getDeclaredField("mIntent")
                                mIntentField.isAccessible = true
                                val proxyIntent = mIntentField[launchActivityItem] as Intent
                                val intent =
                                    proxyIntent.getParcelableExtra<Intent>(TARGET_INTENT)
                                if (intent != null) {
                                    mIntentField[launchActivityItem] = intent
                                }
                            }
                        }
                    }
                } catch (e: Exception) {
                    e.printStackTrace()
                }
                //这里一定要返回false不然正常流程的handleMessage就执行不到了
                false
            }
            mCallbackField[handler] = callback
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

属性activity启动流程的因为都发现我们是在ActivityThread的mh的Handler中拦截消息的,Handler中有一个CallBack对象,可以在handleMessage之前拿到msg

    public void dispatchMessage(@NonNull Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                //这里就是一定要返回false的原因
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

在拿到msg之后我们需要处理以下8.0以下以及8.0以上的区分:
· 在Android8.0以下时,是在ActivityThread的Handler中处理
100-109这几个值来处理不同的生命周期的,并且在msg中可以直接获取到Intent对象。
· 而在Android8.0以后,这几个值没有了,统一为159的入口,并通过状态模式来处理,并且相应的参数有封装,我们需要获取一些包装参数之后才能获取到Intent。

到这里插件的Activity就可以启动了,但是你会发现资源布局都没法加载,下面再说插件资源的加载。


Hook的方式来进行插件化开发,很受系统版本的限制,并且随着Android系统版本的升级要做不同的适配。所以还有另外一种插件方案就是通过容器来实现插件化,这种方式不需要hook太多的系统代码,腾讯的Shadow插件就是这个来实现的,宿主的app中有的是容器的activity,编写的业务插件按原来的逻辑继承Activity,但是在编译打包的时候通过插件将其继承改为ShadowActivity,这里其实是改为了普通类,而在代理类中通过调用这些方法,从而实现了生命周期方法的调用,不过这里都避免不了加载插件的过程,所有DexClassLoader还是必须要加载使用的,这也是热修复的基础。