Android插件化之Activity生命周期处理

来源:互联网 发布:java爬动态网页 编辑:程序博客网 时间:2024/05/18 20:47
Android插件化之Activity生命周期处理
一.概述
Android里的类比如Activity,service,application等相对于普通的Java类的最大的区别是它们时“活”的,它们具有生命周期。就拿Activity来说吧,它的生命周期是受到系统的严格控制的,具体来说就是通过SystemServer进程里的AMS通过IBinder控制应用进程来进行Activity的生命周期的回调。所以,如何处理好插件类的Activity的生命周期是插件化的一个重要问题。


二.Activity的启动流程
在讨论如何处理Activity的生命周期前,先对Activity的启动流程进行了解,理解好原理才能做写好代码,关于Activity的启动流程最好的学习方法是根据Android 源码一步一步跟踪。
Activity的启动流程从全局来看可以分为3个流程 如下:
APP进程->SystemServer进程(AMS)->APP进程
 
1.APP进程
1.1Activity内
当我们调用startActivity方法时,首先是在应用本地进程里进行各种配置处理。一开始是在Activity内部,因为Activity是继承Context的,并且重写了其startActivity方法,所以不需要
添加FLAG_ACTIVITY_NEW_TASK的标记(非Activity的如service就需要)
 
@Override    public void startActivity(Intent intent, Bundle options) {        warnIfCallingFromSystemProcess();        if ((intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {            throw new AndroidRuntimeException(                    "Calling startActivity() from outside of an Activity "                    + " context requires the FLAG_ACTIVITY_NEW_TASK flag."                    + " Is this really what you want?");        }        mMainThread.getInstrumentation().execStartActivity(            getOuterContext(), mMainThread.getApplicationThread(), null,            (Activity)null, intent, -1, options);    }


好了,几经周转最后会到了Activity的startActivityForResult方法,该方法如下
 
public void startActivityForResult(Intent intent, int requestCode, @Nullable Bundle options) {        if (mParent == null) {            Instrumentation.ActivityResult ar =                mInstrumentation.execStartActivity(                    this, mMainThread.getApplicationThread(), mToken, this,                    intent, requestCode, options);


在这里会调用Instrumentation的execStartActivity方法,下面就进入Instrumentation跟踪


1.2Instrumentation
Instrumentation的execStartActivity方法里最重要的是下面这段代码
 
int result = ActivityManagerNative.getDefault()                .startActivity(whoThread, who.getBasePackageName(), intent,                        intent.resolveTypeIfNeeded(who.getContentResolver()),                        token, target != null ? target.mEmbeddedID : null,                        requestCode, 0, null, options);            checkStartActivityResult(result, intent);


其中ActivityManagerNative.getDefault()得到的是一个IActivityManager对象,它是AMS在应用进程本地的一个代理(IBinder实现)。应用进程与SystemServer进程的AMS通信都是通过它来进行的。接下来就分析AMS那边了。


2.SystemServer进程(AMS)
AMS里先后会调用startActivity,startActivityAsUser,接着调用ActivityStackSupervisor的startActivityMayWait方法,ActivityStackSupervisor这个类主要是对Activity栈进行管理,各种权限验证,manifest是否注册了该Activity了等。AMS做了很多事情,不过AMS是系统的,我们hook不了,也就不需要写太多了。
SystemServer这边最后会调用ActivityStackSupervisor的realStartActivityLocked方法,接着调用ActivityThread的scheduleLaunchActivity方法,把控制权重新交给应用进程。
(其中app.thread是应用的ActivityThread在SystemServer的代理(IBinder实现))
 
app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,                    System.identityHashCode(r), r.info, new Configuration(mService.mConfiguration),                    r.compat, r.task.voiceInteractor, app.repProcState, r.icicle, r.persistentState,                    results, newIntents, !andResume, mService.isNextTransitionForward(),                    profilerInfo);



3.APP进程
好了,又回到了应用本地进程了。来看看ActivityThead的scheduleLaunchActivity方法,该方法就是发送一个启动Activity的异步消息给ActivityThread内部的Handler(mH)
 
  sendMessage(H.LAUNCH_ACTIVITY, r);


Activity的生命周期回调和许多关键方法都是通过Handler来进行异步处理的。看看Handler内部是如何处理该消息的
 
 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);                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);                } break;


里面调用了handlerLaunchActivity方法,接着又调用了performLaunchActivity方法,该方法很关键,主要做了下面几件事。
3.1调用Instrumentation的newActivity方法,使用应用类加载器ClassLoader加载Activity并创建Activity对象
 
java.lang.ClassLoader cl = r.packageInfo.getClassLoader();            activity = mInstrumentation.newActivity(                    cl, component.getClassName(), r.intent);


(如果发现Application还没有启动,则在启动Activity之前要先创建并启动Application)


3.2为该Activity创建对应的Context,并调用Activity的attach方法把Context等东西附加到Activity对象里
 
Context appContext = createBaseContextForActivity(r, activity);                CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());                Configuration config = new Configuration(mCompatConfiguration);                if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "                        + r.activityInfo.name + " with config " + config);                activity.attach(appContext, this, getInstrumentation(), r.token,                        r.ident, app, r.intent, r.activityInfo, title, r.parent,                        r.embeddedID, r.lastNonConfigurationInstances, config,                        r.voiceInteractor);



3.3回调Activity的onCreate等生命周期
Activity的生命周期的回调在应用进程本地最终都是通过Instrumentation里的方法调用的,名字一般为callActivityXXX()
 




三.实现方式
已经明白了Activity的启动流程以及其中的一些关键点了,现在要做的就是找出hook点,最好的方式是让系统为我们回调Activity的生命周期,而不是我们自己手动触发。之前看过DL插件化,里面采用的是动态代理,也就是创建一个插件Activity对象,并启动一个傀儡Activity,在其生命周期里调用插件Activity对象对应的生命周期函数,这种方法我个人觉得太麻烦的,而且还有许多局限性。那这样才能启动一个不在manifest里注册的插件Activity,并且让系统为我们回调其生命周期呢?这里就要用到一种移花接木思想了。


1.经过上面Activity启动流程的分析发现Activity的启动是要经过AMS进行验证的,要判断其是否在manifest里注册过。所以我们可以事先在manifest里注册写备有Activity,然后在应用进程传递给AMS信息是把里面的Intent掉包,把启动的目标Activity信息替换为manifest里的Activity。欺骗AMS。
具体的实现过程如下:
因为在应用进程本地启动Activity最终是调用ActivityManagerNative里的gDefault里的IActivityManager对象的startActivity方法来把信息交给AMS的,使用我们可以把这个IActivityManager对象给hook掉,替换为我们自己的代理对象,然后修改startActivity方法。


1.1如我们是这样启动Activity的:
 
Intent t = new Intent();t.setComponent(new ComponentName("com.ss.testdialog", "com.ss.testdialog.MainActivity"));startActivity(t);



1.2然后获取ActivityThreadNative里的gDefault对象,创建一个IActivityManager代理,并替换掉原来的IActivityManager对象
 
Class<?> activityManagerNative = Class.forName("android.app.ActivityManagerNative");Field gDefaultField = activityManagerNative.getDeclaredField("gDefault");gDefaultField.setAccessible(true);//获取gDefault对象Object gDefaultObj = gDefaultField.get(null);//获取IActivityManager对象Class<?> singleton = Class.forName("android.util.Singleton");Field mInstance = singleton.getDeclaredField("mInstance");mInstance.setAccessible(true);Object rawIActivityManager = mInstance.get(gDefaultObj);//使用反射里的代理创建我们的IActivityManager对象Class<?> iActivityManagerInterface = Class.forName("android.app.IActivityManager");Object proxy = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),        new Class<?>[] { iActivityManagerInterface }, new IActivityManagerHandler(rawIActivityManager));//替换掉应用本身的IActivityManager为我们自己的对象mInstance.set(gDefaultObj, proxy);



1.3代理IActivityManagerHandler里处理startActivity方法
 
class IActivityManagerHandler implements InvocationHandler {    Object mBase;    public IActivityManagerHandler(Object base) {        mBase = base;    }    @Override    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {        if ("startActivity".equals(method.getName())) {            Intent raw;            int index = 0;            for (int i = 0; i < args.length; i++) {                if (args[i] instanceof Intent) {                    index = i;                    break;                }            }            //获取原来的Intent            raw = (Intent) args[index];            //构造新的Intent            Intent newIntent = new Intent();            String stubPackage = "应用包名";            ComponentName componentName = new ComponentName(stubPackage, StubActivity.class.getName());            newIntent.setComponent(componentName);            //保存原来的Intent,后面恢复要用            newIntent.putExtra("rawIntent", raw);            args[index] = newIntent;            return method.invoke(mBase, args);        }        return method.invoke(mBase, args);    }}


其中stubPackage为应用包名,StubActivity是在manifest里占坑的Activity。



2.上面已经成功把要启动的Activity掉包了,接下来就是在AMS里进行各种处理验证,而后又返回到应用本地进程来了,我们这时候要把真正要启动的Activity恢复过来。
经过上面Activity的启动流程分析,这里会到达ActivityThread里来,然后发送一个异步消息给Handler,我们可以hook掉该Handler,修改它里面启动Activity的方法,把真正要启动的Activity换回去。通过下面的掉包,系统会认为这个Activity是合法的,所以其生命周期也就会由系统帮我们回调了。
实现:
2.1获取ActivityThread对象,再获取它里面的Handler对象,然后修改其处理启动Activity的方法。
 
//获取ActivityThread对象Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");Method currentActivityThreadMethod = activityThreadClass.getDeclaredMethod("currentActivityThread");currentActivityThreadMethod.setAccessible(true);Object currentActivityThread = currentActivityThreadMethod.invoke(null);//获取Handler(mH)对象Field mHField = activityThreadClass.getDeclaredField("mH");mHField.setAccessible(true);Handler mH = (Handler) mHField.get(currentActivityThread);//修改Handler里面处理启动Activity的方法Field mCallBackField = Handler.class.getDeclaredField("mCallback");mCallBackField.setAccessible(true);mCallBackField.set(mH, new ActivityThreadHandlerCallback(mH));



2.2ActivityThreadHandlerCallback类里面修改了Handler处理启动Activity方法
 
class ActivityThreadHandlerCallback implements Handler.Callback {    Handler mBase;    public ActivityThreadHandlerCallback(Handler base) {        mBase = base;    }    @Override    public boolean handleMessage(Message msg) {        switch (msg.what) {            // ActivityThread里面 "LAUNCH_ACTIVITY" 这个字段的值是100            case 100:                handleLaunchActivity(msg);                break;        }        mBase.handleMessage(msg);        return true;    }    private void handleLaunchActivity(Message msg) {        Object obj = msg.obj;        try {            Field intent = obj.getClass().getDeclaredField("intent");            intent.setAccessible(true);            Intent raw = (Intent) intent.get(obj);            Intent target = raw.getParcelableExtra("rawIntent");            //恢复原来启动的插件Activity            raw.setComponent(target.getComponent());            Field activityInfoField = obj.getClass().getDeclaredField("activityInfo");            activityInfoField.setAccessible(true);            ActivityInfo activityInfo = (ActivityInfo) activityInfoField.get(obj);            activityInfo.applicationInfo.packageName = target.getPackage() == null ?                    target.getComponent().getPackageName() : target.getPackage();            hookPackageManager();        } catch (Exception e) {            throw new RuntimeException("hook launch activity failed", e);        }    }


当然,这里要在宿主apk里能加载到数据Activity类,还需要hook应用类加载器ClassLoader,要怎么做要看上一篇文章(Android插件化之插件类的加载)。


2.3其实上面那么做还没完成,还需要hook掉PMS。因为PMS里面会验证启动类的包名是否是安装过的,如果没安装会报错,而我们插件里的Activity这些类都是每安装过的,所以要hook掉它。(上面的hookPackageManager()方法)
private static void hookPackageManager() throws Exception {    Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");    Method currentActivityThreadMethod = activityThreadClass.getDeclaredMethod("currentActivityThread");    currentActivityThreadMethod.setAccessible(true);    Object currentActivityThread = currentActivityThreadMethod.invoke(null);    // 获取ActivityThread里面原始的 sPackageManager    Field sPackageManagerField = activityThreadClass.getDeclaredField("sPackageManager");    sPackageManagerField.setAccessible(true);    Object sPackageManager = sPackageManagerField.get(currentActivityThread);    // 创建代理对象    Class<?> iPackageManagerInterface = Class.forName("android.content.pm.IPackageManager");    Object proxy = Proxy.newProxyInstance(iPackageManagerInterface.getClassLoader(),            new Class<?>[] { iPackageManagerInterface },            new IPackageManagerHookHandler(sPackageManager));    //替换原来的sPackageManager    sPackageManagerField.set(currentActivityThread, proxy);}


 
public class IPackageManagerHookHandler implements InvocationHandler{    private Object mBase;    public IPackageManagerHookHandler(Object base) {        mBase = base;    }    @Override    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {        if (method.getName().equals("getPackageInfo")) {            return new PackageInfo();        }        return method.invoke(mBase, args);    }}



1 0
原创粉丝点击