Android-Framework-05-AMS-04-ATMS-ActivityClientRecord与ActivityRecord“

26 阅读7分钟

转载自 牛晓伟

ActivityClientRecord

App进程内Activity的记录者

为了把ActivityRecord与ActivityClientRecord之间的关系展示的更清楚,我特意绘制了一幅图:

图片

图解

ActivityRecord是存在于systemserver进程,它是归ATMS管理,Android系统中每一个被启动的Activity都会被ActivityRecord记录下来。而ActivityClientRecord是存在于每一个App进程内,它的作用是记录App进程内启动的Activity,App进程内启动的Activity是存放在ArrayMap数据结构中,它的key值是类型为IBinder的token对象,它的value值就是ActivityClientRecord对象,ActivityClientRecord的属性中activity指向真正的启动Activity实例。

下面表格是ActivityClientRecord的主要属性。”

属性说明
token:IBinder正如它的名字令牌,它可是ActivityRecord与ActivityClientRecord之间的连接器
intent:Intent启动一个Activity是需要把启动信息放入Intent中的,而这个intent就是它了
state:Bundle若该Activity保存了数据,则ATMS会把保存的数据下发给state
activity:Activity真正启动的Activity实例
window:Window每个Activity都有一个Window对象,而它的类型是PhoneWindow,window就是指向PhoneWindow实例
paused:booleanactivity是否进入paused状态
stopped:booleanactivity是否进入stopped状态
activityInfo:ActivityInfo该信息是ATMS下发下来的,代表了在AndroidManifest文件中配置的Activity信息
mLifecycleState:intactivity的状态对应activity执行了哪些生命周期方法,它的值有PRE_ON_CREATE、ON_CREATE、ON_START、ON_RESUME、ON_PAUSE、ON_STOP、ON_DESTROY

App进程内所有启动的Activity

App进程内所有启动的Activity是存储在ActivityThread的属性中的,如下代码:

//ActivityThread

//保存了所有启动的Activity
final ArrayMap<IBinder, ActivityClientRecord> mActivities = new ArrayMap<>();

如上代码,mActivities保存了所有启动的Activity,其中IBinder作为key值,它是从ATMS传递过来的,每个Activity都对应自己的IBinder对象,ActivityClientRecord对象则保存了Activity的相关信息。关于IBinder在后面知识点中会介绍,它可是ActivityRecord和ActivityClientRecord一一映射的关键。

ActivityClientRecord何时初始化

我同样也绘制了一幅启动Activity的流程图,如下

图片

图解

上图展示了App1进程启动App2进程中的Activity的流程,这个过程中要经过两次binder通信:

  1. App1进程调用startActivity方法,启动Activity的信息是在Intent对象中存储着
  2. ATMS收到启动Activity的信息,经过各种处理后,给App2进程发送启动Activity的信息
  3. App2进程收到Activity的启动信息intent:Intent、info:ActivityInfo、state:Bundle等

而ActivityClientRecord初始化的时机就是在App进程收到ATMS发送的启动Activity的信息后,ATMS会把启动Activity的各种信息都传递过来初始化ActivityClientRecord对象,开始App进程内Activity的启动过程。

总结

ActivityClientRecord的作用是记录App进程内启动的Activity,它与位于systemserver进程内的ActivityRecord是一一对应关系,而这个对应关系的关键连接器是类型为IBinder的token

Activity与ActivityRecord“互认”

“Activity与ActivityRecord之间“互认”可是ATMS管理控制Activity的关键,这也是我上一篇文章留下的一个知识点,那咱们这节就把它消化了,先从IBinder对象特性说起吧。”

注:ATMS是ActivityTaskManagerService的简称

IBinder对象特性

我绘制了一幅图,来展示IBinder对象在进程之间的传递过程:

图片

image

图注:BinderBinderProxy类都是IBinder类的子类,A进程和B进程都是App类型进程,Binder-A对象和BinderProxy-B对象代表任意的Binder和BinderProxy对象。

结合上图来介绍下IBinder对象的特性:

  1. A进程中的一个Binder-A对象被传递给B进程时,该Binder对象在binder驱动层会被转换,因此B进程收到的是BinderProxy-B对象
  2. Binder-A对象从A进程传递到B进程传递多次,B进程获取的依然是Binder-B对象。即一个进程中的同一Binder对象多次传递相同的其他进程,则其他进程获取的BinderProxy对象是不变的。
  3. B进程的BinderProxy-B对象被传递给A进程,A进程得到的是Binder-A对象。即可以通过BinderProxy对象反解出原先的Binder对象

大牛:“IBinder对象特性可是实现“互认”的基础,既然介绍完毕IBinder对象特性,那咱们进入正题。”

类型为IBinder的token从何而来

token是ATMS下发的,看到token这个单词有没有想到在进行网络请求之前,需要从服务器取token信息,而后的网络请求客户端都需要拼接上token信息,以供服务器验证使用者的信息。

而ATMS下发的token也有类似的作用,每一个ActivityRecord对象的类型为IBinder的token属性,而该token属性就是被下发的token,那该token对象到达是什么类型呢?

请看如下代码:

//ActivityRecord类

//Token就是保证它们连接的类
private static class Token extends Binder {
        @NonNull WeakReference<ActivityRecord> mActivityRef;

        @Override
        public String toString() {
            return "Token{" + Integer.toHexString(System.identityHashCode(this)) + " "
                    + mActivityRef.get() + "}";
        }
    }

上面的Token类就是token的类型,Token类是不是非常简单啊,它只是继承了Binder了,并且它的mActivityRef属性指向ActivityRecord。

何时下发IBinder类型的token

当需要启动App进程中的Activity时,ATMS就会下发Activity的各种信息,其中就包含了类型为Token类的token对象,

如下相关代码:

//ActivityTaskSupervisor

boolean realStartActivityLocked(ActivityRecord r, WindowProcessController proc,
            boolean andResume, boolean checkConfig) throws RemoteException {
            
    省略代码······
    //使用IApplicationThread和token来创建ClientTransaction,它可是会被传递到App进程
    final ClientTransaction clientTransaction = ClientTransaction.obtain(
                        proc.getThread(), r.token);
      省略代码······
                        
}

App进程收到IBinder类型的token

一分钟掌握一个知识点--Activity系列之ActivityClientRecord一文中介绍了ActivityClientRecord,从ATMS下发下来的Activity包括token信息都会被存放在ActivityClientRecord对象中,依据上面介绍的IBinder对象特性,这时候的token的类型已经不是Token了,而是BinderProxy类型

在如下方法中,会把App进程内的Activity实例及token进行存放:

//ActivityThread
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    省略代码······
    synchronized (mResourcesManager) {
        //这时候App进程内的Activity实例已经初始化完成,并且存放在ActivityClientRecord对象中。 key值为token,r为ActivityClientRecord,mActivities存放了所有App进程内启动的Activity
        mActivities.put(r.token, r);
    }
    省略代码······
}

App进程会把类型为BinderProxy的token作为key值,ActivityClientRecord对象作为value值存放在mActivities属性中,以这种方式来存放App进程内所有启动的Activity。

Activity发送消息给ActivityRecord

有了上面的基础后,如果Activity要想发送消息给ActivityRecord,比如发送pause、stop消息,相关的代码如下:

//ActivityClient
//发送pause消息给ActivityRecord,其中参数token就是保存在ActivityClientRecord对象中的token
public void activityPaused(IBinder token) {
        try {
            getActivityClientController().activityPaused(token);
        } catch (RemoteException e) {
            e.rethrowFromSystemServer();
        }
    }

ATMS收到以上消息会调用以下方法,对token转换为ActivityRecord:

//ActivityRecord类

 //该方法从IBinder中找到ActivityRecord
 static @Nullable ActivityRecord forTokenLocked(IBinder token) {
        if (token == nullreturn null;
        final Token activityToken;
        try {
            activityToken = (Token) token;
        } catch (ClassCastException e) {
            Slog.w(TAG, "Bad activity token: " + token, e);
            return null;
        }
        final ActivityRecord r = activityToken.mActivityRef.get();
        return r == null || r.getRootTask() == null ? null : r;
    }

凡是Activity有消息发送给ActivityRecord,都需要携带上token,根据IBinder对象特性,该token到达systemserver进程后会被转换为Token类型,而调用上面的forTokenLocked方法可以把token转换为ActivityRecord。

ActivityRecord发送消息给Activity

ActivityRecord发送消息给Activity也会携带token,如下例子:

//ActivityRecord
boolean makeActiveIfNeeded(ActivityRecord activeActivity) {
        省略代码······
        //其中token就是要发送给App进程的,下面方法通知Activity调用它的onPause方法
        mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), token,
                        PauseActivityItem.obtain(finishing, false /* userLeaving */,
                                configChangeFlags, false /* dontReport */));
        省略代码······
    }

App进程收到上面的消息,会从getActivityClient方法中根据token获取到对应的ActivityClientRecord,而ActivityClientRecord中包含了Activity实例,进而就可以调用Activity实例的相应方法了,getActivityClient方法如下:

//ActivityThread

public ActivityClientRecord getActivityClient(IBinder token) {
    //根据token,从mActivities中获取ActivityClientRecord
    return mActivities.get(token);
}

总结

ActivityRecord中定义的Token类是实现Activity与ActivityRecord“互认”核心类,它是一个Binder子类,充分使用IBinder对象的特性就可以做到ActivityRecord与App进程中的Activity“互认”的目的。