android插件化(Activity篇)

来源:互联网 发布:wifi网络连接受限 编辑:程序博客网 时间:2024/06/14 16:10

本文承接上一篇博客,先贴出Activity启动关键步骤:

Context.startActivity --> ContextImpl.startActivity --> mMainThread.getInstrumentation().execStartActivity() -->ActivityManagerNative.getDefault().startActivity() --> ActivityManagerService.startActivity() --> startActivityAsUser --> ActivityStackSupervisor(android高版本对AMS管理进行的重构.startActivityMayWait -->  ... --> ApplicationThread.scheduleLaunchActivity() --> ActivityThread.handleLaunchActivity --> performLaunchActivity()

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    ...
    mActivities.put(r.token, r);
    ...
    return activity;}

Demo关键代码:

@Overrideprotected void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentView(R.layout.activity_main);    try {        hookAms();        setHandlerCallback();    } catch (Exception e) {        e.printStackTrace();    }    startActivity(new Intent(MainActivity.this,DestActivity.class));}

private void hookAms() throws Exception {    Class<?> activityManagerNativeClass = Class.forName("android.app.ActivityManagerNative");    Field gDefaultField = activityManagerNativeClass.getDeclaredField("gDefault");    gDefaultField.setAccessible(true);    Object gDefault = gDefaultField.get(null);    /**     * 4.x以上的gDefault是一个 android.util.Singleton对象,我们取出这个单例里面的字段     */    Class<?> singleton = Class.forName("android.util.Singleton");    Field mInstanceField = singleton.getDeclaredField("mInstance");    mInstanceField.setAccessible(true);    /**     * ActivityManagerNative的gDefault对象里面原始的IActivityManager对象     */    Object rawIActivityManager = mInstanceField.get(gDefault);    /**     * 创建一个这个对象的代理对象,然后替换这个字段,让我们的代理对象帮忙干活     */    Class<?> iActivityManagerInterface = Class.forName("android.app.IActivityManager");    Object proxy = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),            new Class<?>[]{iActivityManagerInterface}, new IActivityManagerHandler(rawIActivityManager, getPackageName()));    mInstanceField.set(gDefault, proxy);}

class IActivityManagerHandler implements InvocationHandler {    private Object mBase;    private String packageName;    IActivityManagerHandler(Object base, String packageName) {        this.mBase = base;        this.packageName = packageName;    }    /**     * 更改Intent参数,替换原始Activity启动别的Activity     */    @TargetApi(Build.VERSION_CODES.HONEYCOMB)    @Override    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {        if ("startActivity".equals(method.getName())) {            Intent originalIntent;            int originalIntentPos = 0;            Intent newIntent = new Intent();            for (int i = 0; i < args.length; i++) {                if (args[i] instanceof Intent) {                    originalIntentPos = i;                    break;                }            }            originalIntent = (Intent) args[originalIntentPos];            /**             * 把真实要启动的Activity临时替换为StubActivity             */            ComponentName componentName = new ComponentName(packageName, StubActivity.class.getCanonicalName());            newIntent.setComponent(componentName);            newIntent.putExtra("src_intent", originalIntent); //保存原始的Intent            args[originalIntentPos] = newIntent;            return method.invoke(mBase, args);        }        return method.invoke(mBase, args);    }}
上面代码中,IActivityManagerHandler主要用于替换ActivityManagerNative的gDefault变量里的mInstance变量,由于AMS和PMS在android中比较常用,系统为我们维护了一个gDefault单例模式,其中ActivityManagerNative实例就保存在其中的mInstance变量中,在上面的代码中,我们hook住了ActivityManagerNative的startActivity方法,将Intent中真正要启动的Activity替换为我们事先注册好的StubActivity,进而达到欺骗系统的目的

    void setHandlerCallback() throws Exception {        Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");        Field currentActivityThreadField = activityThreadClass.getDeclaredField("sCurrentActivityThread");        currentActivityThreadField.setAccessible(true);        Object currentActivityThread = currentActivityThreadField.get(null);        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 ActivityThreadHandlerCallback(mH));    }}class ActivityThreadHandlerCallback implements Handler.Callback {    private Handler mBase;    ActivityThreadHandlerCallback(Handler base) {        mBase = base;    }    @Override    public boolean handleMessage(Message msg) {        switch (msg.what) {            // ActivityThread里面 "LAUNCH_ACTIVITY" 这个字段的值是100,避免麻烦直接硬编码            case 100:                try {                    handleLaunchActivity(msg);                } catch (Exception e) {                    e.printStackTrace();                }                break;        }        mBase.handleMessage(msg);        return true;    }    private void handleLaunchActivity(Message msg) throws Exception {        //恢复真身        Field intent = msg.obj.getClass().getDeclaredField("intent");        intent.setAccessible(true);        Intent raw = (Intent) intent.get(msg.obj);        Intent target = raw.getParcelableExtra("src_intent");        raw.setComponent(target.getComponent());    }}

上面这段代码主要用于设置ActivityThread的Handler变量的Callback值,用于屏蔽真正的处理ACTIVITY_LAUNCH的逻辑,由于我们给系统传递了StubActivity,在返回到客户端处理逻辑时(ApplicationThread通过handler机制转移到ActivityThread中),我们要恢复真正要启动的Activity,在这里,可有有人会问,我们是把系统欺骗了,可是系统识别这个Activity吗,答案是yes,我们以onDestroy为例简要分析一下:

从Activity的finish方法开始跟踪,最终会通过ActivityManagerNative到AMS然后接着通过ApplicationThread到ActivityThread,然后通过Handler转发消息到ActivityThread的handleDestroyActivity,接着这个方法把任务交给performDestroyActivity完成,继续分析performDestroyActivity,关键代码如下:

private ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing,int configChanges, boolean getNonConfigInstance) {    ActivityClientRecord r = mActivities.get(token);    Class<? extends Activity> activityClass = null;    if (r != null) {        activityClass = r.activity.getClass();
        ...
    }
    ...
}
在demo中,r.activity是DestActivity还是StubActivity呢,按理说,由于我们欺骗了AMS,AMS应该只知道StubActivity的存在,它压根儿就不知道TargetActivity是什么,为什么它能正确完成对TargetActivity生命周期的回调呢?一切的秘密在token里面,AMS与ActivityThread之间对于Activity的生命周期的交互,并没有直接使用Activity对象进行交互,而是使用一个token来标识,这个token是binder对象,因此可以方便地跨进程传递,Activity里面有一个成员变量mToken代表的就是它,token可以唯一地标识一个Activity对象,它在Activity的attach方法里面初始化;在AMS处理Activity的任务栈的时候,使用这个token标记Activity,因此在我们的demo里面,AMS进程里面的token对应的是StubActivity,也就是AMS还在傻乎乎地操作StubActivity(关于这一点,你可以dump出任务栈的信息,可以观察到dump出的确实是StubActivity),但是在我们App进程里面,token对应的却是TargetActivity,因此,在ActivityThread执行回调的时候,能正确地回调到TargetActivity相应的方法,回到代码,ActivityClientRecord是在mActivities里面取出来的,确实是根据token取;那么这个token是什么时候添加进去的呢?我们看performLaunchActivity就完成明白了:它通过classloader加载了TargetActivity,然后完成一切操作之后把这个activity添加进了mActivities!另外,在这个方法里面我们还能看到对Ativity中attach方法的调用,它传递给了新创建的Activity一个token对象,而这个token是在ActivityClientRecord构造函数里面初始化的(参考文章:Android 插件化原理解析-Activity生命周期管理)

总结起来一句话:启动Activity是通过ActivityClientRecord里的Intent标识Activity,AMS会返回对应的token,期间对Activity的操作都会依据ActivityClientRecord里面的token值,我们更改了Intent,却没有更改token值,因此,AMS和ActivityThread可以对应处理不同的Activity

0 0