众所周知,一个Activity要想启动,必须在AndroidManifest里面注册,否则会在跳转到当前Activity的时候崩溃并报错:have you declared this activity in your AndroidManifest.xml?
而我们要做的插件化就是要跳过注册这一步,同时保证当前Activity能够正常加载。如何做到这一步呢?
下面我们基于API28去查看源码:
当我们调用startActivity方法时,ActivityManagerService(AMS)会去检测,当前要启动的Activity是否注册了
在Instrumentation.java中搜索execStartActivity方法,在方法里有一行关键代码:ActivityManager.getService()
int result = ActivityManager.getService()
.startActivity(whoThread, who.getBasePackageName(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()),
token, target != null ? target.mEmbeddedID : null,
requestCode, 0, null, options);
ActivityManager.getService()(旧的挨批使用ActivityManager.getDefault() )返回的是IActivityManager,这是AIDL里的一个接口,这里会调用到AMS去检测当前activity是否注册,如果没注册就会崩溃,所以我们要在调用AMS之前去hook住.
public static IActivityManager getService() {
return IActivityManagerSingleton.get();
}
返回的是单例,调用了get方法。
private static final Singleton<IActivityManager> IActivityManagerSingleton =
new Singleton<IActivityManager>() {
@Override
protected IActivityManager create() {
final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
final IActivityManager am = IActivityManager.Stub.asInterface(b);
return am;
}
};
可以进入Singleton这个类里面看到具体的get方法。
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;
}
}
}
这里迭代get里面调用了一个抽象的create方法,而这个create方法在上面的 IActivityManagerSingleton中被实现了。
在create方法中,通过IBinder 获取到了当前activity并返回了IActivityManager,这样这种抽象方法中的mInstance就被赋值为IActivityManager了。我们就需要把mInstance替换掉(动态代理)。
思路有了,下面我们通过代码去实现它:
1.创建一个application:HookApplication 基础系统的Application。
2.在oncreate方法中加入一个方法hookAmsAction,目的是:要在执行 AMS之前,替换可用的 Activity,替换在AndroidManifest里面配置的Activity
动态代理:由于执行startActivity之前,我们需要先执行我们的代码(把未注册的TestActivity 替换成 已经注册的 Activity)
3.ASM检查过后,要把这个代理Activity换回来 --> TestActivity
整体流程如下:
startActivity ---> TestActivity -- (Hook ProxyActivity)(AMS)检测,当前要启动的Activity是否注册了)ok ---》 ActivityThread(即将加载启动Activity)----(要把这个ProxyActivity 换回来 --> TestActivity)