插件化中Activity的加载

Posted 加冰雪碧

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了插件化中Activity的加载相关的知识,希望对你有一定的参考价值。

插件化中Activity的加载

前面一系列的文章中我们介绍了android系统资源加载流程,最后引出插件化中资源加载的方法,完成了『资源动态加载』这一大块的介绍。本系列文章将重点介绍『代码动态加载』,拿最熟悉的Activity来开刀。

基础知识

Activity虽然是一个Java对象,可以使用ClassLoader加载出它的实体,但是由于Activity生命周期管理是由系统框架完成的,为了更好的分析如何加载插件中的Activity以及让它“活”起来,我们有必要了解两方面的知识:

  1. Activity启动流程
  2. Activity生命周期回调流程

Activity启动流程

Activity的启动流程大致分三个阶段:

  1. 应用程序进程内启动Activity操作
  2. AMS进程内部处理
  3. 应用程序进程内创建Activity(可能是一个新的进程)

我们将按照这个顺序来分析Activity的启动。

在应用程序进程内启动Activity

以我们最常见的startActivity作为分析的起点,它最终将调用到startActivityForResult。

1.Activity.startActivityForResult

public void startActivityForResult(Intent intent, int requestCode, @Nullable Bundle options) 
  ...
    Instrumentation.ActivityResult ar = mInstrumentation.execStartActivity(this, mMainThread.getApplicationThread(), mToken, this, intent, requestCode, options);
  ...

startActivityForResult方法只是简单调用了Instrumentation类的execStartActivity方法。但是方法参数要说明一下:

  • mMainThread.getApplicationThread。得到的是一个类型为ApplicationThread的Binder本地对象,在后面的操作中会将它传给AMS,以便AMS进程和应用程序进程通信。
  • mToken。它是一个IBinder类型的Binder代理对象,指向AMS中类型为ActivityRecord的Binder本地对象。由于AMS中使用ActivityRecord来描述一个Activity,将mToken传给AMS后,AMS就知道由谁启动了新的Activity。

2.Instrumentation.execStartActivity

public ActivityResult execStartActivity(
            Context who, IBinder contextThread, IBinder token, Activity target,
            Intent intent, int requestCode, Bundle options) 
  ...
  int result = ActivityManagerNative.getDefault()
               .startActivity(whoThread, who.getBasePackageName(), intent,
               intent.resolveTypeIfNeeded(who.getContentResolver()),
               token, target != null ? target.mEmbeddedID : null,
               requestCode, 0, null, options);
  ...

ActivityManagerNative的getDefault方法获得的是AMS的一个代理对象,其startActivity方法最终将调用到AMS的startActivityAsUser方法。

AMS进程内部处理

启动Activity的核心逻辑都在AMS进程内进行,整个逻辑流程十分复杂。但在研究插件化框架原理时,我们不用细致的研究每一块逻辑,因为即使可以看明白所有流程,也无法Hook AMS进程完成操作。所以在这里只是对AMS内处理流程进行简单介绍,如果后面有涉及具体某块逻辑时再详细介绍。

先看一下整个过程的时序图:

简要说明每一步作用。

1.ActivityManagerService.startActivityAsUser

这里我们不去分析用户相关逻辑,这里简单调用ActivityStarter的方法。

2.ActivityStarter.startActivityMayWait

得到代表Intent信息的ResolveInfo对象,代表Activity信息的ActivityInfo对象。调用startActivityLock进行下一步操作。

3.ActivityStarter.startActivityLocked

得到代表调用者进程的ProcessRecord对象,代表调用者Activity的ActivityRecord对象。做权限检查的校验,并创建待启动Activity的ActivityRecord对象。调用startActivityUnchecked进一步处理。

4.ActivityStarter.startActivityUnchecked

获取到任务栈相关的信息,然后调用代表任务栈的ActivityStack的startActivityLocked方法。

5.ActivityStack.startActivityLocked

确定了待启动Activity所属的任务栈,并且将其放入任务栈。然后返回到ActivityStarter的startActivityUnchecked方法中,调用ActivityStackSupervisor的resumeFocusedStackTopActivityLocked方法进一步操作。

6.ActivityStackSupervisor.resumeFocusedStackTopActivityLocked

直接将操作委托给ActivityStack执行。

7.ActivityStack.resumeTopActivityUncheckedLocked

调用自身resumeTopActivityInnerLocked方法。

8.ActivityStack.resumeTopActivityInnerLocked

这个方法中执行了两个主要的操作:

  • 调用startPausingLocked方法完成前一个Activity的onPause生命周期调用。
  • 调用ActivityStackSupervisor的startSpecificActivityLocked方法完成启动Activity。

我们在分析Activity生命周期回调时再去分析startPausingLocked方法,现在继续分析启动流程。

9.ActivityStackSupervisor.startSpecificActivityLocked

这个方法中判断待启动Activity所要求的进程是否存在,如果不存在就创建进程。这里我们不分析创建进程流程,继续向下看存在进程情况下的启动流程。

10.ActivityStackSupervisor.realStartActivityLocked

调用应用程序进程内ApplicationThread的scheduleLaunchActivity方法来完成Activity创建和启动。

到这里AMS进程内的操作流程就简单介绍完了,下面来看在在应用程序进程内是如何创建Activity的。

应用程序进程内创建Activity

1.ApplicationThread.scheduleLaunchActivity

public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
                ActivityInfo info, ...) 

            updateProcessState(procState, false);

            ActivityClientRecord r = new ActivityClientRecord();

            r.token = token;
            r.activityInfo = info;
            ...

            sendMessage(H.LAUNCH_ACTIVITY, r);
 

首先创建出一个在应用程序进程内表示Activity的对象。与之对应的是在AMS进程内表示Activity的ActivityRecord对象。然后将一些参数赋值给ActivityClientRecord。这里有两个属性需要注意:

  • token。是一个类型为IBinder的代理对象,指向了AMS中一个ActivityRecord。
  • activityInfo。表示待启动Activity信息。

然后调用sendMessage方法,这个方法执行逻辑在ActivityThread类的属性mH上。mH是一个类型为H的Handler对象,其将操作转到应用程序进程主线程内执行。来看一下类H处理逻辑。

2.H.handleMessage

 public void handleMessage(Message msg) 
            switch (msg.what) 
                case LAUNCH_ACTIVITY: 

                    final ActivityClientRecord r = (ActivityClientRecord) msg.obj;

                    r.packageInfo = getPackageInfoNoCheck(
                            r.activityInfo.applicationInfo, r.compatInfo);
                    handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
                 break;
            
 

调用getPackageInfoNoCheck方法得到PackageInfo对象,然后调用handleLaunchActivity方法启动Activity。getPackageInfoNoCheck方法很重要,暂时先不去管,等到分析插件化处理时再来仔细研究它。继续Activity启动流程,下面是handleLaunchActivity方法。

3.ActivityThread.handleLaunchActivity

private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) 
        ...

        Activity a = performLaunchActivity(r, customIntent);

        ...
    

主要是调用performLaunchActivity来启动Activity。

4.ActivityThread.performLaunchActivity

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) 

        ActivityInfo aInfo = r.activityInfo;
        ...
        ComponentName component = r.intent.getComponent();
        ...

        Activity activity = null;
        try 
            java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
            ...
         catch (Exception e) 
            ...
        

        try 
            Application app = r.packageInfo.makeApplication(false, mInstrumentation);

            ...

            if (activity != null) 
                Context appContext = createBaseContextForActivity(r, activity);
                ...
                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);

                ...

                if (r.isPersistable()) 
                    mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
                 else 
                    mInstrumentation.callActivityOnCreate(activity, r.state);
                

                r.activity = activity;
                ...
            
            r.paused = true;

            mActivities.put(r.token, r);

         catch (SuperNotCalledException e) 
            throw e;

         

        return activity;
    

这个方法很重要,它做了以下几个操作:

  • 拿到加载Activity的ClassLoader。这个ClassLoader是从ActivityClientRecord的packageInfo属性中拿到的,如果有印象可以记起来这个packageInfo是调用getPackageInfoNoCheck方法创建出来的。
  • 拿到当前进程中的Application对象。
  • 使用ActivityClientRecord中的token等内容创建ContextImpl对象,并且在内部持有待启动Activity的引用。
  • 调用Activity的attach方法在其内部保存一些ActivityClientRecord内的属性。
  • 调用activity的onCreate方法。
  • 将ActivityClientRecord中的activity属性设置为当前activity。
  • 以ActivityClientRecord中的token为key,mActivities中添加待启动的activity。

这样Activity的启动流程大致就介绍完了,下面来介绍Activity的生命周期回调流程。

Activity生命周期回调流程

Activity生命周期回调大概分为两种,一种是在创建了Activity后直接回调的,例如onCreate、onResume。另一种是在进行一系列操作以后回调的,如onPause、onStop等。在这里我们重点分析后者,因为后者通常是在AMS进程中发起回调,我们要关系的是AMS是如何找到对应的Activity并执行回调

Activity中onPause生命周期的回调

这里我们拿onPause方法来举例,因为在刚刚分析的Activity启动流程内就涉及了onPause操作。在AMS进程内部处理第八步中我们谈到了,ActivityStack的resumeTopActivityInnerLock方法中调用了startPausingLocked方法完成对前一个Activity的onPause生命周期回调,现在来看一下这个方法。

1.ActivityStack.startPausingLocked

这个方法在AMS进程中运行,不贴代码了,最终调用了以下代码:

prev.app.thread.schedulePauseActivity(prev.appToken, prev.finishing,
                        userLeaving, prev.configChangeFlags, dontWait);

很熟悉的流程,prev.app.thread是前一个Activity所在进程的ApplicationThread。

2.Application.schedulePauseActivity

public final void schedulePauseActivity(IBinder token, boolean finished,
                boolean userLeaving, int configChanges, boolean dontReport) 

            sendMessage(
                    finished ? H.PAUSE_ACTIVITY_FINISHING : H.PAUSE_ACTIVITY,
                    token,
                    (userLeaving ? USER_LEAVING : 0) | (dontReport ? DONT_REPORT : 0),
                    configChanges,
                    seq);

用mH发了消息,看一下消息处理逻辑。

3.mH.handleMessage

public void handleMessage(Message msg) 

            switch (msg.what) 
                ...
                case PAUSE_ACTIVITY: 

                    SomeArgs args = (SomeArgs) msg.obj;
                    handlePauseActivity((IBinder) args.arg1, false,
                            (args.argi1 & USER_LEAVING) != 0, args.argi2,
                            (args.argi1 & DONT_REPORT) != 0, args.argi3);

                 break;
            

调用handlePauseActivity完成onPause回调。

4.ActivityThread.handlePauseActivity

private void handlePauseActivity(IBinder token, boolean finished,
            boolean userLeaving, int configChanges, boolean dontReport, int seq) 
        ActivityClientRecord r = mActivities.get(token);
        ...
        if (r != null) 
            ...
            performPauseActivity(token, finished, r.isPreHoneycomb(), "handlePauseActivity");

            ...
        
    

方法很简单,调用了performPauseActivity。

5.ActivityThread.performPauseActivity

 final Bundle performPauseActivity(ActivityClientRecord r, boolean finished,
            boolean saveState, String reason) 
        ...

        if (!r.activity.mFinished && saveState) 
            callCallActivityOnSaveInstanceState(r);
        

        performPauseActivityIfNeeded(r, reason);

        ...

方法先调用Activity的onSaveInstanceState保存一下状态,然后调用onPause方法。

这样Activity启动流程和生命周期回调流程都简单分析完了,接下来将结合两种不同类型的插件化框架分析插件化中Activity的加载和启动。

以上是关于插件化中Activity的加载的主要内容,如果未能解决你的问题,请参考以下文章

Activity布局流程+资源加载过程+插件化换肤思路

Activity布局流程+资源加载过程+插件化换肤思路

Android 插件化Hook 插件化框架 ( Hook Activity 启动过程 | 静态代理 )

Android 插件化插件化原理 ( JVM 内存数据 | 类加载流程 )

datatables 插件异步加载数据

Android 插件化Hook 插件化框架 ( Hook Activity 启动流程 | 主线程创建 Activity 实例之前使用插件 Activity 类替换占位的组件 )