Android 深入四大组件系列 http://liuwangshu.cn/tags/Android深入四大组件/
Android 解析 AMS 系列 http://liuwangshu.cn/tags/ActivityManagerService/
Android 解析 ClassLoader 系列 http://liuwangshu.cn/tags/ClassLoader/
前言
四大组件的插件化是插件化技术的核心知识点, 而 Activity 插件化更是重中之重, Activity 插件化主要有三种实现方式, 分别是反射实现, 接口实现和 Hook 技术实现. 反射实现会对性能有所影响, 主流的插件化框架没有采用此方式, 关于接口实现可以阅读 dynamic-load-apk 的源码, 这里不做介绍, 目前 Hook 技术实现是主流, 因此本篇文章主要介绍 Hook 技术实现. Hook 技术实现主要有两种解决方案 , 一种是通过 Hook IActivityManager 来实现, 另一种是 Hook Instrumentation 实现. 在讲到这两个解决方案前, 我们需要从整体上了解 Activity 的启动流程.
1.Activity 的启动过程回顾
Activity 的启动过程主要分为两种, 一种是根 Activity 的启动过程, 一种是普通 Activity 的启动过程. 关于根 Activity 的启动过程在在介绍过, 这里来简单回顾下, 如下图所示.
图 1
首先 Launcher 进程向 AMS 请求创建根 Activity,AMS 会判断根 Activity 所需的应用程序进程是否存在并启动, 如果不存在就会请求 Zygote 进程创建应用程序进程. 应用程序进程启动后, AMS 会请求应用程序进程创建并启动根 Activity. 普通 Activity 和根 Activity 的启动过程大同小异, 但是没有这么复杂, 因为不涉及应用程序进程的创建, 跟 Laucher 也没关系, 如下图所示.
图 2
上图抽象的给出了普通 Acticity 的启动过程. 在应用程序进程中的 Activity 向 AMS 请求创建普通 Activity(步骤 1),AMS 会对 这个 Activty 的生命周期管和栈进行管理, 校验 Activity 等等. 如果 Activity 满足 AMS 的校验, AMS 就会请求应用程序进程中的 ActivityThread 去创建并启动普通 Activity(步骤 2).
2.Hook IActivityManager 方案实现
AMS 是在 SystemServer 进程中, 我们无法直接进行修改, 只能在应用程序进程中做文章. 可以采用预先占坑的方式来解决没有在 AndroidManifest.xml 中显示声明的问题, 具体做法就是在上图步骤 1 之前使用一个在 AndroidManifest.xml 中注册的 Activity 来进行占坑, 用来通过 AMS 的校验. 接着在步骤 2 之后用插件 Activity 替换占坑的 Activity, 接下来根据这个解决方案我们来实践一下.
2.1 注册 Activity 进行占坑
为了更好的讲解启动插件 Activity 的原理, 这里省略了插件 Activity 的加载逻辑, 直接创建一个 TargetActivity 来代表已经加载进来的插件 Activity, 接着我们再创建一个 SubActivity 用来占坑. 在 AndroidManifest.xml 中注册 SubActivity, 如下所示. AndroidManifest.xml
- <?xml version="1.0" encoding="utf-8"?>
- <manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.example.liuwangshu.pluginactivity">S
- <application
- ...
- <activity android:name=".StubActivity"/>
- </application>
- </manifest>
TargetActivity 用来代表已经加载进来的插件 Activity, 因此不需要在 AndroidManifest.xml 进行注册. 如果我们直接在 MainActivity 中启动 TargetActivity 肯定会报错 (android.content.ActivityNotFoundException 异常).
2.2 使用占坑 Activity 通过 AMS 验证
为了防止报错, 需要将启动的 TargetActivity 替换为 SubActivity, 用 SubActivity 来通过 AMS 的验证. 在第六章时讲过 Android 8.0 与 7.0 的 AMS 家族有一些差别, 主要是 Android 8.0 去掉了 AMS 的代理 ActivityManagerProxy, 代替它的是 IActivityManager, 直接采用 AIDL 来进行进程间通信. Android7.0 的 Activity 的启动会调用 ActivityManagerNative 的 getDefault 方法, 如下所示. frameworks/base/core/java/android/app/ActivityManagerNative.java
- static public IActivityManager getDefault() {
- return gDefault.get();
- }
- private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
- protected IActivityManager create() {
- IBinder b = ServiceManager.getService("activity");
- if (false) {
- Log.v("ActivityManager", "default service binder =" + b);
- }
- IActivityManager am = asInterface(b);
- if (false) {
- Log.v("ActivityManager", "default service =" + am);
- }
- return am;
- }
- };
getDefault 方法返回了 IActivityManager 类型的对象, IActivityManager 借助了 Singleton 类来实现单例, 而且 gDefault 又是静态的, 因此 IActivityManager 是一个比较好的 Hook 点. Android8.0 的 Activity 的启动会调用 ActivityManager 的 getService 方法, 如下所示. frameworks/base/core/java/android/app/ActivityManager.java
- public static IActivityManager getService() {
- return IActivityManagerSingleton.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;
- }
- };
同样的, getService 方法返回了了 IActivityManager 类型的对象, 并且 IActivityManager 借助了 Singleton 类来实现单例, 确定了无论是 Android7.0 还是 Android8.0,IActivityManager 都是比较好的 Hook 点. Singleton 类如下所示, 后面会用到. frameworks/base/core/java/android/util/Singleton.java
- 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;
- }
- }
- }
由于 Hook 需要多次对字段进行反射操作, 先写一个字段工具类 FieldUtil: FieldUtil.java
- public class FieldUtil {
- public static Object getField(Class clazz, Object target, String name) throws Exception {
- Field field = clazz.getDeclaredField(name);
- field.setAccessible(true);
- return field.get(target);
- }
- public static Field getField(Class clazz, String name) throws Exception{
- Field field = clazz.getDeclaredField(name);
- field.setAccessible(true);
- return field;
- }
- public static void setField(Class clazz, Object target, String name, Object value) throws Exception {
- Field field = clazz.getDeclaredField(name);
- field.setAccessible(true);
- field.set(target, value);
- }
其中 setField 方法不会马上用到, 接着定义替换 IActivityManager 的代理类 IActivityManagerProxy, 如下所示.
- public class IActivityManagerProxy implements InvocationHandler {
- private Object mActivityManager;
- private static final String TAG = "IActivityManagerProxy";
- public IActivityManagerProxy(Object activityManager) {
- this.mActivityManager = activityManager;
- }
- @Override
- public Object invoke(Object o, Method method, Object[] args) throws Throwable {
- if ("startActivity".equals(method.getName())) {//1
- Intent intent = null;
- int index = 0;
- for (int i = 0; i <args.length; i++) {
- if (args[i] instanceof Intent) {
- index = i;
- break;
- }
- }
- intent = (Intent) args[index];
- Intent subIntent = new Intent();//2
- String packageName = "com.example.liuwangshu.pluginactivity";
- subIntent.setClassName(packageName,packageName+".StubActivity");//3
- subIntent.putExtra(HookHelper.TARGET_INTENT, intent);//4
- args[index] = subIntent;//5
- }
- return method.invoke(mActivityManager, args);
- }
- }
Hook 点 IActivityManager 是一个接口, 建议采用动态代理. 在注释 1 处拦截 startActivity 方法, 接着获取参数 args 中第一个 Intent 对象, 它是原本要启动插件 TargetActivity 的 Intent. 注释 2,3 处新建一个 subIntent 用来启动的 StubActivity, 注释 4 处将这个 TargetActivity 的 Intent 保存到 subIntent 中, 便于以后还原 TargetActivity. 注释 5 处用 subIntent 赋值给参数 args, 这样启动的目标就变为了 StubActivity, 用来通过 AMS 的校验. 最后用代理类 IActivityManagerProxy 来替换 IActivityManager, 如下所示.
- HookHelper.java
- public class HookHelper {
- public static final String TARGET_INTENT = "target_intent";
- public static void hookAMS() throws Exception {
- Object defaultSingleton=null;
- if (Build.VERSION.SDK_INT>= 26) {//1
- Class<?> activityManageClazz = Class.forName("android.app.ActivityManager");
- // 获取 activityManager 中的 IActivityManagerSingleton 字段
- defaultSingleton= FieldUtil.getField(activityManageClazz ,null,"IActivityManagerSingleton");
- } else {
- Class<?> activityManagerNativeClazz = Class.forName("android.app.ActivityManagerNative");
- // 获取 ActivityManagerNative 中的 gDefault 字段
- defaultSingleton= FieldUtil.getField(activityManagerNativeClazz,null,"gDefault");
- }
- Class<?> singletonClazz = Class.forName("android.util.Singleton");
- Field mInstanceField= FieldUtil.getField(singletonClazz ,"mInstance");//2
- // 获取 iActivityManager
- Object iActivityManager = mInstanceField.get(defaultSingleton);//3
- Class<?> iActivityManagerClazz = Class.forName("android.app.IActivityManager");
- Object proxy = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
- new Class<?>[] { iActivityManagerClazz }, new IActivityManagerProxy(iActivityManager));
- mInstanceField.set(defaultSingleton, proxy);
- }
- }
首先在注释 1 处对系统版本进行区分, 最终获取的是
Singleton<IActivityManager>
类型的 IActivityManagerSingleton 或者 gDefault 字段. 注释 2 处获取 Singleton 类中的 mInstance 字段, 从前面 Singleton 类的代码可以得知 mInstance 字段的类型为 T, 在注释 3 处得到 IActivityManagerSingleton 或者 gDefault 字段中的 T 的类型, T 的类型为 IActivityManager. 最后动态创建代理类 IActivityManagerProxy, 用 IActivityManagerProxy 来替换 IActivityManager. 自定义一个 Application, 在其中调用 HookHelper 的 hookAMS 方法, 如下所示. MyApplication.java
- public class MyApplication extends Application {
- @Override
- public void attachBaseContext(Context base) {
- super.attachBaseContext(base);
- try {
- HookHelper.hookAMS();
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
在 MainActivity 中启动 TargetActivity, 如下所示. MainActivity.java
- public class MainActivity extends Activity {
- private Button bt_hook;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- bt_hook = (Button) this.findViewById(R.id.bt_hook);
- bt_hook.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- Intent intent = new Intent(MainActivity.this, TargetActivity.class);
- startActivity(intent);
- }
- });
- }
- }
点击 Button 时, 启动的并不是 TargetActivity 而是 SubActivity, 同时 Log 中打印了 "hook 成功", 说明我们已经成功用 SubActivity 通过了 AMS 的校验.
2.3 还原插件 Activity
前面用占坑 Activity 通过了 AMS 的校验, 但是我们要启动的是插件 TargetActivity, 还需要用插件 TargetActivity 来替换占坑的 SubActivity, 这一替换的时机就在图 2 的步骤 2 之后. ActivityThread 启动 Activity 的过程, 如图 3 所示.
图 3
ActivityThread 会通过 H 将代码的逻辑切换到主线程中, H 类是 ActivityThread 的内部类并继承自 Handler, 如下所示. frameworks/base/core/java/android/app/ActivityThread.java
- private class H extends Handler {
- public static final int LAUNCH_ACTIVITY = 100;
- public static final int PAUSE_ACTIVITY = 101;
- ...
- public void handleMessage(Message msg) {
- if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling:" + codeToString(msg.what));
- switch (msg.what) {
- case LAUNCH_ACTIVITY: {
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
- final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
- r.packageInfo = getPackageInfoNoCheck(
- r.activityInfo.applicationInfo, r.compatInfo);
- handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
- Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
- } break;
- ...
- }
- ...
- }
H 中重写的 handleMessage 方法会对 LAUNCH_ACTIVITY 类型的消息进行处理, 最终会调用 Activity 的 onCreate 方法. 那么在哪进行替换呢? 接着来看 Handler 的 dispatchMessage 方法: frameworks/base/core/java/android/os/Handler.java
- public void dispatchMessage(Message msg) {
- if (msg.callback != null) {
- handleCallback(msg);
- } else {
- if (mCallback != null) {
- if (mCallback.handleMessage(msg)) {
- return;
- }
- }
- handleMessage(msg);
- }
- }
Handler 的 dispatchMessage 用于处理消息, 看到如果 Handler 的 Callback 类型的 mCallback 不为 null, 就会执行 mCallback 的 handleMessage 方法. 因此, mCallback 可以作为 Hook 点, 我们可以用自定义的 Callback 来替换 mCallback, 自定义的 Callback 如下所示. HCallback.java
- public class HCallback implements Handler.Callback{
- public static final int LAUNCH_ACTIVITY = 100;
- Handler mHandler;
- public HCallback(Handler handler) {
- mHandler = handler;
- }
- @Override
- public boolean handleMessage(Message msg) {
- if (msg.what == LAUNCH_ACTIVITY) {
- Object r = msg.obj;
- try {
- // 得到消息中的 Intent(启动 SubActivity 的 Intent)
- Intent intent = (Intent) FieldUtil.getField(r.getClass(), r, "intent");
- // 得到此前保存起来的 Intent(启动 TargetActivity 的 Intent)
- Intent target = intent.getParcelableExtra(HookHelper.TARGET_INTENT);
- // 将启动 SubActivity 的 Intent 替换为启动 TargetActivity 的 Intent
- intent.setComponent(target.getComponent());
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- mHandler.handleMessage(msg);
- return true;
- }
- }
HCallback 实现了 Handler.Callback, 并重写了 handleMessage 方法, 当收到消息的类型为 LAUNCH_ACTIVITY 时, 将启动 SubActivity 的 Intent 替换为启动 TargetActivity 的 Intent. 接着我们在 HookHelper 中定义一个 hookHandler 方法如下所示.
- HookHelper.java
- public static void hookHandler() throws Exception {
- Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");
- Object currentActivityThread= FieldUtil.getField(activityThreadClass ,null,"sCurrentActivityThread");//1
- Field mHField = FieldUtil.getField(activityThread,"mH");//2
- Handler mH = (Handler) mHField.get(currentActivityThread);//3
- FieldUtil.setField(Handler.class,mH,"mCallback",new HCallback(mH));
- }
ActivityThread 类中有一个静态变量 sCurrentActivityThread, 用于表示当前的 ActivityThread 对象, 因此在注释 1 处获取 ActivityThread 中定义的 sCurrentActivityThread 对象. 注释 2 处获取 ActivityThread 类的 mH 字段, 接着在注释 3 处获取当前 ActivityThread 对象中的 mH 对象, 最后用 HCallback 来替换 mH 中的 mCallback. 在 MyApplication 的 attachBaseContext 方法中调用 HookHelper 的 hookHandler 方法, 运行程序, 当我们点击启动插件按钮, 发现启动的是插件 TargetActivity.
2.4 插件 Activity 的生命周期
插件 TargetActivity 确实启动了, 但是它有生命周期吗? 这里从源码角度来进行分析, Activity 的 finish 方法可以触发 Activity 的生命周期变化, 和 Activity 的启动过程类似, finish 方法如下所示. frameworks/base/core/java/android/app/Activity.java
- public void finish() {
- finish(DONT_FINISH_TASK_WITH_ACTIVITY);
- }
- private void finish(int finishTask) {
- if (mParent == null) {
- int resultCode;
- Intent resultData;
- synchronized (this) {
- resultCode = mResultCode;
- resultData = mResultData;
- }
- if (false) Log.v(TAG, "Finishing self: token=" + mToken);
- try {
- if (resultData != null) {
- resultData.prepareToLeaveProcess(this);
- }
- if (ActivityManager.getService()
- .finishActivity(mToken, resultCode, resultData, finishTask)) {//1
- mFinished = true;
- }
- } catch (RemoteException e) {
- // Empty
- }
- } else {
- mParent.finishFromChild(this);
- }
- }
finish 方法的调用链和 Activity 的启动过程是类似的, 注释 1 处会调用的 AMS 的 finishActivity 方法, 接着是 AMS 通过 ApplicationThread 调用 ActivityThread,ActivityThread 向 H 发送 DESTROY_ACTIVITY 类型的消息, H 接收到这个消息会执行 handleDestroyActivity 方法, handleDestroyActivity 方法又调用了 performDestroyActivity 方法, 如下所示. frameworks/base/core/java/android/app/ActivityThread.java
- private ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing,
- int configChanges, boolean getNonConfigInstance) {
- ActivityClientRecord r = mActivities.get(token);//1
- ...
- try {
- r.activity.mCalled = false;
- mInstrumentation.callActivityOnDestroy(r.activity);//2
- ...
- } catch (SuperNotCalledException e) {
- ...
- }
- }
- mActivities.remove(token);
- StrictMode.decrementExpectedActivityCount(activityClass);
- return r;
注释 1 处通过 IBinder 类型的 token 来获取 ActivityClientRecord,ActivityClientRecord 用于描述应用进程中的 Activity. 在注释 2 处调用 Instrumentation 的 callActivityOnDestroy 方法来调用 Activity 的 OnDestroy 方法, 并传入了 r.activity. 前面的例子我们用 SubActivity 替换了 TargetActivity 通过了 AMS 的校验, 这样 AMS 只知道 SubActivity 的存在, 那么 AMS 是如何能控制 TargetActivity 生命周期的回调的? 我们接着往下看, 启动 Activity 时会调用 ActivityThread 的 performLaunchActivity 方法, 如下所示. frameworks/base/core/java/android/app/ActivityThread.java
- private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
- ...
- java.lang.ClassLoader cl = appContext.getClassLoader();
- activity = mInstrumentation.newActivity(
- cl, component.getClassName(), r.intent);//1
- ...
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
r.referrer, r.voiceInteractor, window, r.configCallback);
- ...
- mActivities.put(r.token, r);//2
- ...
- return activity;
- }
注释 1 处根据 Activity 的类名用 ClassLoader 加载 Acitivty, 接着调用 Activity 的 attach 方法, 将 r.token 赋值给 Activity 的成员变量 mToken. 在注释 2 处将 ActivityClientRecord 根据 r.token 存在 mActivities 中 (
mActivities 类型为 ArrayMap<IBinder, ActivityClientRecord>
), 再结合 Activity 的 finish 方法的注释 1 处, 可以得出结论: AMS 和 ActivityThread 之间的通信采用了 token 来对 Activity 进行标识, 并且此后的 Activity 的生命周期处理也是根据 token 来对 Activity 进行标识的. 回到我们这个例子来, 我们在 Activity 启动时用插件 TargetActivity 替换占坑 SubActivity, 这一过程在 performLaunchActivity 方法调用之前, 因此注释 2 处的 r.token 指向的是 TargetActivity, 在 performDestroyActivity 的注释 1 处获取的就是代表 TargetActivity 的 ActivityClientRecord, 可见 TargetActivity 是具有生命周期的.
3.Hook Instrumentation 方案实现
Hook Instrumentation 实现要比 Hook IActivityManager 实现要简洁一些, 示例代码会和 Hook IActivityManager 实现有重复, 重复的部分这里不再赘述. Hook Instrumentation 实现同样也需要用到占坑 Activity, 与 Hook IActivityManager 实现不同的是, 用占坑 Activity 替换插件 Activity 以及还原插件 Activity 的地方不同. Acitivty 的 startActivity 方法调用时序图如图 4 所示.
图 4
从图 4 可以发现, 在 Activity 通过 AMS 校验前, 会调用 Activity 的 startActivityForResult 方法: frameworks/base/core/java/android/app/Activity.java
- public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
- @Nullable Bundle options) {
- if (mParent == null) {
- options = transferSpringboardActivityOptions(options);
- Instrumentation.ActivityResult ar =
- mInstrumentation.execStartActivity(
this, mMainThread.getApplicationThread(), mToken, this,
- intent, requestCode, options);
- ...
- } else {
- ...
- }
- }
startActivityForResult 方法中调用了 Instrumentation 的 execStartActivity 方法来激活 Activity 的生命周期. 如图 3 所示, ActivityThread 启动 Activity 的过程中会调用 ActivityThread 的 performLaunchActivity 方法, 如下所示. frameworks/base/core/java/android/app/ActivityThread.java
- private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
- ...
- // 创建要启动 Activity 的上下文环境
- ContextImpl appContext = createBaseContextForActivity(r);
- Activity activity = null;
- try {
- java.lang.ClassLoader cl = appContext.getClassLoader();
- // 用类加载器来创建 Activity 的实例
- activity = mInstrumentation.newActivity(
- cl, component.getClassName(), r.intent);//1
- ...
- } catch (Exception e) {
- ...
- }
- ...
- return activity;
- }
注释 1 处调用了 mInstrumentation 的 newActivity 方法, 其内部会用类加载器来创建 Activity 的实例. 看到这里我们可以得到方案, 就是在 Instrumentation 的 execStartActivity 方法中用占坑 SubActivity 来通过 AMS 的验证, 在 Instrumentation 的 newActivity 方法中还原 TargetActivity, 这两部操作都和 Instrumentation 有关, 因此我们可以用自定义的 Instrumentation 来替换掉 mInstrumentation. 首先我们自定义一个 Instrumentation, 在 execStartActivity 方法中将启动的 TargetActivity 替换为 SubActivity, 如下所示. InstrumentationProxy.java
- public class InstrumentationProxy extends Instrumentation {
- private Instrumentation mInstrumentation;
- private PackageManager mPackageManager;
- public InstrumentationProxy(Instrumentation instrumentation, PackageManager packageManager) {
- mInstrumentation = instrumentation;
- mPackageManager = packageManager;
- }
- public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, Activity target,
Intent intent, int requestCode, Bundle options) {
- List<ResolveInfo> infos = mPackageManager.queryIntentActivities(intent, PackageManager.MATCH_ALL);
- if (infos == null || infos.size() == 0) {
- intent.putExtra(HookHelper.TARGET_INTENsT_NAME, intent.getComponent().getClassName());//1
- intent.setClassName(who, "com.example.liuwangshu.pluginactivity.StubActivity");//2
- }
- try {
- Method execMethod = Instrumentation.class.getDeclaredMethod("execStartActivity",
Context.class, IBinder.class, IBinder.class, Activity.class, Intent.class, int.class, Bundle.class);
return (ActivityResult) execMethod.invoke(mInstrumentation, who, contextThread, token,
target, intent, requestCode, options);
- } catch (Exception e) {
- e.printStackTrace();
- }
- return null;
- }
- }
首先查找要启动的 Activity 是否已经在 AndroidManifest.xml 中注册了, 如果没有就在注释 1 处将要启动的 Activity(TargetActivity) 的 ClassName 保存起来用于后面还原 TargetActivity, 接着在注释 2 处替换要启动的 Activity 为 StubActivity, 最后通过反射调用 execStartActivity 方法, 这样就可以用 StubActivity 通过 AMS 的验证. 在 InstrumentationProxy 的 newActivity 方法还原 TargetActivity, 如下所示. InstrumentationProxy.java
- public Activity newActivity(ClassLoader cl, String className, Intent intent) throws InstantiationException,
- IllegalAccessException, ClassNotFoundException {
- String intentName = intent.getStringExtra(HookHelper.TARGET_INTENT_NAME);
- if (!TextUtils.isEmpty(intentName)) {
- return super.newActivity(cl, intentName, intent);
- }
- return super.newActivity(cl, className, intent);
- }
newActivity 方法中创建了此前保存的 TargetActivity, 完成了还原 TargetActivity. 编写 hookInstrumentation 方法, 用 InstrumentationProxy 替换 mInstrumentation: HookHelper.java
- public static void hookInstrumentation(Context context) throws Exception {
- Class<?> contextImplClass = Class.forName("android.app.ContextImpl");
- Field mMainThreadField =FieldUtil.getField(contextImplClass,"mMainThread");//1
- Object activityThread = mMainThreadField.get(context);//2
- Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");
- Field mInstrumentationField=FieldUtil.getField(activityThreadClass,"mInstrumentation");//3
- FieldUtil.setField(activityThreadClass,activityThread,"mInstrumentation",new InstrumentationProxy((Instrumentation) mInstrumentationField.get(activityThread),
- context.getPackageManager()));
- }
注释 1 处获取 ContextImpl 类的 ActivityThread 类型的 mMainThread 字段, 注释 2 出获取当前上下文环境的 ActivityThread 对象. 注释 3 出获取 ActivityThread 类中的 mInstrumentation 字段, 最后用 InstrumentationProxy 来替换 mInstrumentation. 在 MyApplication 的 attachBaseContext 方法中调用 HookHelper 的 hookInstrumentation 方法, 运行程序, 当我们点击启动插件按钮, 发现启动的是插件 TargetActivity.
4. 总结
这一节我们学到了启动插件 Activity 的原理, 主要的方案就是先用一个在 AndroidManifest.xml 中注册的 Activity 来进行占坑, 用来通过 AMS 的校验, 接着在合适的时机用插件 Activity 替换占坑的 Activity. 为了更好的讲解启动插件 Activity 的原理, 本小节省略了插件 Activity 的加载逻辑, 直接创建一个 TargetActivity 来代表已经加载进来的插件 Activity. 同时这一节使我们更好的理解了 Activity 的启动过程. 更多的 Android 插件化原理请查看即将要出版的Android 进阶之光续作.
来源: https://juejin.im/post/5b0c35996fb9a009d070c081