插件化中Activity的加载
Posted 加冰雪碧
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了插件化中Activity的加载相关的知识,希望对你有一定的参考价值。
插件化中Activity的加载
前面一系列的文章中我们介绍了android系统资源加载流程,最后引出插件化中资源加载的方法,完成了『资源动态加载』这一大块的介绍。本系列文章将重点介绍『代码动态加载』,拿最熟悉的Activity来开刀。
基础知识
Activity虽然是一个Java对象,可以使用ClassLoader加载出它的实体,但是由于Activity生命周期管理是由系统框架完成的,为了更好的分析如何加载插件中的Activity以及让它“活”起来,我们有必要了解两方面的知识:
- Activity启动流程
- Activity生命周期回调流程
Activity启动流程
Activity的启动流程大致分三个阶段:
- 应用程序进程内启动Activity操作
- AMS进程内部处理
- 应用程序进程内创建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的加载的主要内容,如果未能解决你的问题,请参考以下文章
Android 插件化Hook 插件化框架 ( Hook Activity 启动过程 | 静态代理 )
Android 插件化插件化原理 ( JVM 内存数据 | 类加载流程 )
Android 插件化Hook 插件化框架 ( Hook Activity 启动流程 | 主线程创建 Activity 实例之前使用插件 Activity 类替换占位的组件 )