在了解Android插件化原理之前,我们需要对Hook机制有一定的了解,具体可以阅读下面几篇文章:
1.Hook机制之动态代理
2.Hook机制之Binder Hook
3.Hook机制之AMS&PMS
我们知道,要启动一个Activity,这个Activity必须在AndroidManifest里面注册,如果Activity没有注册,是会抛android.content.ActivityNotFoundException异常的。我们不可能把插件中的每个Activity都在AndroidManifest里面注册,这样也违背了插件化的原理。
了解Activity的启动原理后,其实我们可以这么做:我们可以在宿主的AndroidManifest里面中注册一个通用的Activity,假设是ProxyActivity,在启动插件Activity时先启动这个通用的Activity,绕过AMS后,再启动目标Activity,假设是TargetActivity。
Activity的启动过程
Activity的启动是通过startActivity(Intent intent) 方法,该方法是调用startActivityForResult方法,最后我们发现其实调用的是Instrumentation的execStartActivities方法,该方法调用的是execStartActivitiesAsUser方法,下面摘出execStartActivitiesAsUser方法主要代码如下:
public void execStartActivitiesAsUser(Context who, IBinder contextThread,
IBinder token, Activity target, Intent[] intents, Bundle options,
int userId) {
IApplicationThread whoThread = (IApplicationThread) contextThread;
......
int result = ActivityManagerNative.getDefault()
.startActivities(whoThread, who.getBasePackageName(), intents, resolvedTypes,
token, options, userId);
checkStartActivityResult(result, intents[0]);
} catch (RemoteException e) {
throw new RuntimeException("Failure from system", e);
}
}
看函数ActivityManagerNative.getDefault() .startActivities方法,有两点值得注意,一个是ActivityManagerNative.getDefault(),另一处是参数ApplicationThread。
ApplicationThread是APP进程与AMS进程通信的桥梁,是真正创建Activity对象并且启动Activity的一个类。
ApplicationThread通过调用ActivityThread的scheduleLaunchActivity方法包装一个参数并通过Handler发送了一个消息,该消息主要是用来执行handleLaunchActivity方法,handleLaunchActivity方法里面有一句Activity a = performLaunchActivity(r, customIntent)
用来创建Activity,后面通过Instrumentation去执行Activity的生命周期方法。
Hook解析
这里先给出AndroidManifest代码,如下:
<application
android:name=".PluginApplication"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".ProxyActivity" />
</application>
ProxyActivity是在AndroidManifest里面注册的,TargetActivity没有注册, 在MainActivity里面,我们通过如下语句来启动TargetActivity。
Intent intent = new Intent(this, TargetActivity.class);
startActivity(intent);
知道命题后,了解Activity的启动过程后,我们需要做以下两件事。
- Hook掉ActivityManagerNative,代理ActivityManagerNative的ActivityManagerNative.getDefault()返回的IActivityManager对象,并把启动TargetActivity的Component替换成启动ProxyActivity的Component。
public static void hookActivityManagerNative() throws ClassNotFoundException,
NoSuchMethodException, InvocationTargetException,
IllegalAccessException, NoSuchFieldException {
//
Class<?> activityManagerNative = Class.forName("android.app.ActivityManagerNative");
Field gDefaultField = activityManagerNative.getDeclaredField("gDefault");
gDefaultField.setAccessible(true);
Object gDefault = gDefaultField.get(null);
//gDefault是一个 android.util.Singleton对象; 我们取出这个单例里面的字段
Class<?> singleton = Class.forName("android.util.Singleton");
Field mInstanceField = singleton.getDeclaredField("mInstance");
mInstanceField.setAccessible(true);
// 得到ActivityManagerNative的gDefault对象,即IActivityManager对象
Object activityManager = mInstanceField.get(gDefault);
// 创建即IActivityManager对象的代理对象, 然后替换这个字段
Class<?> iActivityManagerInterface = Class.forName("android.app.IActivityManager");
Object proxy = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
new Class<?>[] { iActivityManagerInterface }, new IActivityManagerHandler(activityManager));
mInstanceField.set(gDefault, proxy);
}
- Hook掉ActivityThreadHandler,即ActivityThread的一个Handler.Callback变量,即mH对象,通过替换mH对象,在handleLaunchActivity里面替换掉ProxyActivity的参数,设置成TargetActivity的参数。
public static void hookActivityThreadHandler() throws Exception {
// 先获当前的ActivityThread对象
Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");
Field currentActivityThreadField = activityThreadClass.getDeclaredField("sCurrentActivityThread");
currentActivityThreadField.setAccessible(true);
Object currentActivityThread = currentActivityThreadField.get(null);
// ActivityThread一个进程只有一个,我们获取这个对象的mH
Field mHField = activityThreadClass.getDeclaredField("mH");
mHField.setAccessible(true);
Handler mH = (Handler) mHField.get(currentActivityThread);
Field mCallBackField = Handler.class.getDeclaredField("mCallback");
mCallBackField.setAccessible(true);
mCallBackField.set(mH, new ActivityThreadCallback(mH));
}
其中IActivityManagerHandler和ActivityThreadCallback的具体实现大家可以参见github的项目地址: Android插件化原理-Activity生命周期。
结语
关于Android插件化,这篇文章仅仅是讲述了如何启动一个没在AndroidManifest里面注册的Activity,关于Android插件化的东西很多。大家可以关注下github的一个开源项目:understand-plugin-framework,对于学习Android插件话的帮助很大。
网友评论