插件化
插件化就是将模块单独打包成一个apk,放在服务器上,需要找个模块的时候再下载下来,加载、
- 插件化的基础就是反射
- class Java编译后的文件,每个类对应一个class文件(内部类啥的也会有一个class文件)
- dex:Dalvik Executable把class打包在一起,一个dex可以包含多个class文件
- odex:Optimized dex针对系统的优化。(例如某个方法的调用指令,会把虚拟的调用转换为使用具体的index,这样在执行的时候就不用再查找了)
- at:Optimized Android file Type。使用AOT策略对dex预先编译(解释)成本地指令,这样在运行阶段就不需要再经历一次解释过程了,程序运行更快
2.插件化原理:动态加载
过自定义ClassLoader来加载dex文件,从而让原本程序中没有的类可以被使用,这就是插件化。
3.打开插件包里面的Activity
Activity正常情况下是需要在清单文件中注册的,插件包里面的Activity肯定是没有在宿主App里面注册的(不然就达不到动态的特性了),那就需要在宿主清单文件里面注册一个占坑的Activity。每次打开插件包里面的Activity时,先打开这个占坑的Activity以达到欺骗AMS的目的,当通过AMS验证之后,再想个办法将占坑的Activity替换成插件包里面的Activity。也就达到了未注册Activity,也能打开Activity的效果。
这里假设待打开的Activity是TargetActivity,占坑的Activity是StubActivity。
hook IActivityManager
直接启动插件里面的Activity是不行的,通不过AMS的检查,所以需要在宿主app里面弄一个占坑的Activity,然后再hook系统流程,把StubActivity拿给AMS去检查,然后在ActivityThread这边,hook H拦截消息,在创建Activity时再替换成TargetActivity进行创建。
hook时需要注意一下。10.0以下都hook IActivityManager,10.0及以上hook IActivityTaskManager,一旦是有人调用startActivity方法,那么判断一下是否在调用插件内的Activity,如果是,那么先把Intent替换成StubActivity,然后继续走。
此时已经到AMS那边去了,AMS检查啥的完成之后,再通知ApplicationThread这边去创建该Activity,ApplicationThread->ActivityThread->H->handleMessage。
通过hook H的mCallback,从而知道是create Activity的时机。(9.0以前message是多个,分开的case,9.0之后是一个EXECUTE_TRANSACTION,Activity生命周期都走这一个case。这个点在处理时也需要单独处理。
关键代码:
const val TARGET_INTENT = "target_intent"
fun hookAMS() {
//此处没有处理Android 10的情况
val defaultSingleton = if (Build.VERSION.SDK_INT >= 26) {
//拿到IActivityManager
val activityManagerClazz = Class.forName("android.app.ActivityManager")
FieldUtil.getFieldValue(activityManagerClazz, null, "IActivityManagerSingleton")
} else {
val activityManagerNativeClazz = Class.forName("android.app.ActivityManagerNative")
//获取ActivityManagerNative中的gDefault字段
FieldUtil.getFieldValue(activityManagerNativeClazz, null, "gDefault")
}
val SingletonClazz = Class.forName("android.util.Singleton")
val mInstanceField = FieldUtil.getField(SingletonClazz, "mInstance")
val IActivityManager = mInstanceField.get(defaultSingleton)
log("获取到了IActivityManager")
//设置动态代理
val IActivityManagerClazz = Class.forName("android.app.IActivityManager")
val proxy =
Proxy.newProxyInstance(Thread.currentThread().contextClassLoader, arrayOf(IActivityManagerClazz), IActivityManagerProxy(IActivityManager))
mInstanceField.set(defaultSingleton, proxy)
log("设置动态代理IActivityManager成功")
}
fun hookHandler() {
//hook ActivityThread里面的H的mCallback,拦截Activity launch事件,将intent参数替换成之前保存在TARGET_INTENT里面的intent
val ActivityThreadClazz = Class.forName("android.app.ActivityThread")
val sCurrentActivityThread = FieldUtil.getFieldValue(ActivityThreadClazz, null, "sCurrentActivityThread")
val mH = FieldUtil.getFieldValue(ActivityThreadClazz, sCurrentActivityThread, "mH") as Handler
val HandlerClazz = Class.forName("android.os.Handler")
FieldUtil.setField(HandlerClazz, mH, "mCallback", HCallback(mH))
}
class IActivityManagerProxy(private val mActivityManager: Any) : InvocationHandler {
override fun invoke(proxy: Any?, method: Method, args: Array<Any>?): Any? {
//如果IActivityManager的startActivity被调用
if ("startActivity" == method.name) {
var intent: Intent? = null
var index = 0
args?.forEachWithIndex { i, any ->
if (any is Intent) {
intent = any
index = i
}
}
//取出参数中的Intent中的包名 判断请求的Activity是否是插件包里面的
intent?.component?.className?.contains("com.xfhy.pluginapkdemo")?.let {
if (it) {
//如果是,那么临时将目标Activity改为占坑Activity StubActivity
val subIntent = Intent()
subIntent.setClassName("com.xfhy.allinone", "com.xfhy.allinone.actual.plugin.StubActivity")
//记录下来,待会儿方便 打开这个Activity
subIntent.putExtra(HookHelper.TARGET_INTENT, intent)
//Intent改好之后,还回去
args?.set(index, subIntent)
}
}
}
if (args != null) {
return method.invoke(mActivityManager, *args)
}
return method.invoke(mActivityManager, null)
}
}
const val LAUNCH_ACTIVITY = 100
class HCallback(private val handler: Handler) : Handler.Callback {
//28以上是EXECUTE_TRANSACTION
override fun handleMessage(msg: Message): Boolean {
if (msg.what == LAUNCH_ACTIVITY) {
val r = msg.obj
try {
//得到消息中的Intent(启动SubActivity的Intent)
val intent = FieldUtil.getFieldValue(r.javaClass, r, "intent") as Intent
//得到此前保存起来的Intent(启动TargetActivity的Intent)
val target = intent.getParcelableExtra<Intent>(HookHelper.TARGET_INTENT)
//为空 则不管,说明不是插件的Activity
target?.let {
//将启动SubActivity的Intent替换成启动TargetActivity的Intent
intent.component = it.component
}
} catch (e: Exception) {
e.printStackTrace()
}
}
handler.handleMessage(msg)
return true
}
}