Android插件化原理-Activity生命周期

在了解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通过调用ActivityThreadscheduleLaunchActivity方法包装一个参数并通过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的启动过程后,我们需要做以下两件事。

  1. 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);

    }
  1. 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));

    }

其中IActivityManagerHandlerActivityThreadCallback的具体实现大家可以参见github的项目地址: Android插件化原理-Activity生命周期

结语

关于Android插件化,这篇文章仅仅是讲述了如何启动一个没在AndroidManifest里面注册的Activity,关于Android插件化的东西很多。大家可以关注下github的一个开源项目:understand-plugin-framework,对于学习Android插件话的帮助很大。

附:
github的项目地址: Android插件化原理-Activity生命周期

    原文作者:湘北南
    原文地址: https://www.jianshu.com/p/7f9ffb5f68c9
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞