几乎所有的插件化都会要的一个需求,启动一个未注册的Activiy,即加载插件包中的Activity,并且主应用并不知道插件应用中会有什么Activity,这是各个插件化框架主力解决的问题之一。
今天我们学习一下占坑式插件化框架的启动Activity原理。
关于动态代理的知识,了解过Retrofit的源码的或者看过Java设计模式之代理模式的高级使用的,应该都了解了。本章不做介绍,主介绍hook+反射。
Hook是什么?
Hook直白点说就是拦截方法,自己对其参数等进行修改,或者替换返回值,达到自己不可告人的目的的一件事。
寻找Hook点
对于启动Activity
,老实说光startActivity
便有很多要说,很多文章会带着你一直追到ActivityManagerService
中的若干个方法,最后再调用本地的ActivityThread
里面的方法去启动本进程的Activity
。
所以光上面的流程我们看出,我们把要启动的Activity
信息发给AMS,其做了各种检查各种操作后真正让Activity
启动的还是我们的ActivityThread
startActivity流程
我们startActivity
是context
的方法,去找Context
实现类class ContextImpl extends Context。
看到最后调用的是mMainThread.getInstrumentation().execStartActivitiesAsUser
方法,不用着急,直接ctrl鼠标左击进去。是Instrumentation
类。
这边我们看到了,是调用ActivityManagerNative
的方法启动activity了。进去这个类我们只能看到一堆的binder通信,调用AMS的方法,不过此时我们不用关心了,因为我们知道接下来是
不过这边我们需要注意,ActivityManagerNative
居然是个单类,那么我们hook它会安全很多,毕竟这个对象是单类。
说是说AMS的事情不用关心,但是我们得关心AMS什么时候回调回来,让我们启动Activity。
去ActivityThread
看,一搜里面有个handleLaunchActivity
方法,是在Handler里面被调用的,而且ActivityThread
也是我们喜欢的对象,因为这个对象存在于整个应用生命周期中。
看了这么多,我们可算是知道启动Activity的入口和出口了,下面我们需要进行欺骗。
实现欺骗
欺骗系统就欺骗两个地方,我们在AndroidManifest里面申明一个假Activity
,然后在启动真实Activity
的地方,将Intent
里面的Activity
替换成我们已经注册过的。再在ActivityThread
launch Activity的时候,替换成我们需要启动的便实现了启动一个未注册过的Activity
的效果。
代码实现
写一个占坑
Activity
,在AndroidManifest注册
/**
* 占坑专用
*/
public class TmpActivity extends Activity { @Override
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);
setContentView(R.layout.activity_tmp);
}
}
<!--占坑专用Activity--> <activity android:name=".TmpActivity"/>
在attachBaseContext中欺骗应用
上面的代码,我们先反射拿到ActivityManagerNative
,然后动态代理IActivityManager
,Hook其startActivity
方法,在里面替换掉intent,并将真实的Intent存放在假Intent的参数里面。
在系统最后调用打开假Intent
的时候,我们从Intent
中取出参数,并打开真正想打开的Activity
。
打开Activity
startActivity
就能打开我们未注册的Activity
了。Demo路径:https://github.com/Jerey-Jobs/AppPluginDemos
总结
上面只是一个Demo,不能支持support包的AppCompatActivity,真正的完整的插件化库任务是艰巨的!
还要支持其他组件,都是很麻烦的事情。
精彩评论