关闭

结合源码探讨WMS与AMS建立连接的桥梁—appWindowToken

418人阅读 评论(0) 收藏 举报
分类:

本文的分析是基于Android 5.0.0 Google原生代码的。(安卓5.1.1源码大致不变。)

我们都知道,ActivityManagerService(AMS)和WindowsManagerService(WMS)都是安卓系统中最关键的服务。它们共同协作完成了系统运行过程中众多Activity和窗口的管理。本文旨在探讨以下这两个问题:

1. WMS在内部建立一个appToken唯一对应的appWindowToken对象的过程。

2. 在Android系统中,token扮演了什么角色。

从问题1说起:当应用向AMS申请启动一个新的Activity的时候,最终会辗转调用ActivityStack.java的startActivityLocked方法。

    Step 1. ActivityStack.startActivityLocked:

    final void startActivityLocked(ActivityRecord r, boolean newTask,
            boolean doResume, boolean keepCurTransition, Bundle options) {
        TaskRecord rTask = r.task;
        final int taskId = rTask.taskId;
        // mLaunchTaskBehind tasks get placed at the back of the task stack.
        if (!r.mLaunchTaskBehind && (taskForIdLocked(taskId) == null || newTask)) {
            // Last activity in task had been removed or ActivityManagerService is reusing task.
            // Insert or replace.
            // Might not even be in.
            insertTaskAtTop(rTask);
            mWindowManager.moveTaskToTop(taskId);
        }
        TaskRecord task = null;
        if (!newTask) {
            // If starting in an existing task, find where that is...
            boolean startIt = true;
            for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
                task = mTaskHistory.get(taskNdx);
                if (task.getTopActivity() == null) {
                    // All activities in task are finishing.
                    continue;
                }
                if (task == r.task) {
                    // Here it is!  Now, if this is not yet visible to the
                    // user, then just add it without starting; it will
                    // get started when the user navigates back to it.
                    if (!startIt) {
                        if (DEBUG_ADD_REMOVE) Slog.i(TAG, "Adding activity " + r + " to task "
                                + task, new RuntimeException("here").fillInStackTrace());
                        task.addActivityToTop(r);
                        r.putInHistory();
                        mWindowManager.addAppToken(task.mActivities.indexOf(r), r.appToken,
                                r.task.taskId, mStackId, r.info.screenOrientation, r.fullscreen,
                                (r.info.flags & ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN) != 0,
                                r.userId, r.info.configChanges, task.voiceSession != null,
                                r.mLaunchTaskBehind);
                        if (VALIDATE_TOKENS) {
                            validateAppTokensLocked();
                        }
                        ActivityOptions.abort(options);
                        return;
                    }
                    break;
                } else if (task.numFullscreen > 0) {
                    startIt = false;
                }
            }
        }

        // Place a new activity at top of stack, so it is next to interact
        // with the user.

        // If we are not placing the new activity frontmost, we do not want
        // to deliver the onUserLeaving callback to the actual frontmost
        // activity
        if (task == r.task && mTaskHistory.indexOf(task) != (mTaskHistory.size() - 1)) {
            mStackSupervisor.mUserLeaving = false;
            if (DEBUG_USER_LEAVING) Slog.v(TAG,
                    "startActivity() behind front, mUserLeaving=false");
        }

        task = r.task;

        // Slot the activity into the history stack and proceed
        if (DEBUG_ADD_REMOVE) Slog.i(TAG, "Adding activity " + r + " to stack to task " + task,
                new RuntimeException("here").fillInStackTrace());
        task.addActivityToTop(r);
        task.setFrontOfTask();

        r.putInHistory();
        if (!isHomeStack() || numActivities() > 0) {
            // We want to show the starting preview window if we are
            // switching to a new task, or the next activity's process is
            // not currently running.
            boolean showStartingIcon = newTask;
            ProcessRecord proc = r.app;
            if (proc == null) {
                proc = mService.mProcessNames.get(r.processName, r.info.applicationInfo.uid);
            }
            if (proc == null || proc.thread == null) {
                showStartingIcon = true;
            }
            if (DEBUG_TRANSITION) Slog.v(TAG,
                    "Prepare open transition: starting " + r);
            if ((r.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) {
                mWindowManager.prepareAppTransition(AppTransition.TRANSIT_NONE, keepCurTransition);
                mNoAnimActivities.add(r);
            } else {
                mWindowManager.prepareAppTransition(newTask
                        ? r.mLaunchTaskBehind
                                ? AppTransition.TRANSIT_TASK_OPEN_BEHIND
                                : AppTransition.TRANSIT_TASK_OPEN
                        : AppTransition.TRANSIT_ACTIVITY_OPEN, keepCurTransition);
                mNoAnimActivities.remove(r);
            }
            //mWindowManger在ActivityStack的成员变量中声明为WindowsManagerService。故接下来这一步调用的是WMS的addAppToken方法
            mWindowManager.addAppToken(task.mActivities.indexOf(r),
                    r.appToken, r.task.taskId, mStackId, r.info.screenOrientation, r.fullscreen,
                    (r.info.flags & ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN) != 0, r.userId,
                    r.info.configChanges, task.voiceSession != null, r.mLaunchTaskBehind);
            boolean doShow = true;
            if (newTask) {
                // Even though this activity is starting fresh, we still need
                // to reset it to make sure we apply affinities to move any
                // existing activities from other tasks in to it.
                // If the caller has requested that the target task be
                // reset, then do so.
                if ((r.intent.getFlags() & Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {
                    resetTaskIfNeededLocked(r, r);
                    doShow = topRunningNonDelayedActivityLocked(null) == r;
                }
            } else if (options != null && new ActivityOptions(options).getAnimationType()
                    == ActivityOptions.ANIM_SCENE_TRANSITION) {
                doShow = false;
            }
            if (r.mLaunchTaskBehind) {
                // Don't do a starting window for mLaunchTaskBehind. More importantly make sure we
                // tell WindowManager that r is visible even though it is at the back of the stack.
                mWindowManager.setAppVisibility(r.appToken, true);
                ensureActivitiesVisibleLocked(null, 0);
            } else if (SHOW_APP_STARTING_PREVIEW && doShow) {
                // Figure out if we are transitioning from another activity that is
                // "has the same starting icon" as the next one.  This allows the
                // window manager to keep the previous window it had previously
                // created, if it still had one.
                ActivityRecord prev = mResumedActivity;
                if (prev != null) {
                    // We don't want to reuse the previous starting preview if:
                    // (1) The current activity is in a different task.
                    if (prev.task != r.task) {
                        prev = null;
                    }
                    // (2) The current activity is already displayed.
                    else if (prev.nowVisible) {
                        prev = null;
                    }
                }
                mWindowManager.setAppStartingWindow(
                        r.appToken, r.packageName, r.theme,
                        mService.compatibilityInfoForPackageLocked(
                                r.info.applicationInfo), r.nonLocalizedLabel,
                        r.labelRes, r.icon, r.logo, r.windowFlags,
                        prev != null ? prev.appToken : null, showStartingIcon);
                r.mStartingWindowShown = true;
            }
        } else {
            // If this is the first activity, don't do any fancy animations,
            // because there is nothing for it to animate on top of.
            mWindowManager.addAppToken(task.mActivities.indexOf(r), r.appToken,
                    r.task.taskId, mStackId, r.info.screenOrientation, r.fullscreen,
                    (r.info.flags & ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN) != 0, r.userId,
                    r.info.configChanges, task.voiceSession != null, r.mLaunchTaskBehind);
            ActivityOptions.abort(options);
            options = null;
        }
        if (VALIDATE_TOKENS) {
            validateAppTokensLocked();
        }

        if (doResume) {
            mStackSupervisor.resumeTopActivitiesLocked(this, r, options);
        }
    }

ActivityStack.java文件位于frameworks/base/services/core/java/com/android/server/am/目录当中。

    在步骤一中,主要完成了两个关键的操作:一是尝试为新启动的Activity插入合适的TaskRecord中。二是调用WMS的addAppToken方法,将Activity所对应的ActivityRecord中的appToken传递到WMS中。WindowManagerService.java文件位于frameworks/base/services/core/java/com/android/server/wm/目录中。

参数r是一个ActivityRecord对象,用来描述的是要启动的Activity组件。

参数newTask是一个布尔变量,用来描述要启动的Activity组件是否是在新任务中启动的。

参数doResume也是一个布尔变量,用来描述是否需要马上将Activity组件启动起来。       

ActivityStack类的成员变量mWindowManager指向的是系统的WindowManagerService,另外一个成员变量mTaskHistory是一个<TaskRecord>数组列表,用来描述系统的Activity组件堆栈。其在文件中的定义为:

    /**
     * The back history of all previous (and possibly still
     * running) activities.  It contains #TaskRecord objects.
     */
    private ArrayList<TaskRecord> mTaskHistory = new ArrayList<TaskRecord>();
    从此定义可以看出,这是一个存放所有启动过的activity记录的数组。

    当参数newTask的值等于false时,就说明参数r所描述的Activity组件是在已有的一个任务中启动的,因此,这时候ActivityStack类的成员函数startActivityLocked就会从上到下遍历保存成员变量mTaskHistory,找到一个已有的且存放activity数量不为空的TaskRecord,它与参数r所描述的Activity组件是属于同一个任务的——即它们的成员变量task的值相等。若在找到这样一个TaskRecord之前,先遍历到一个更上层的(index次序较大)且存在着FullScreen属性的TaskRecord,就会将startIt这个布尔变量设为false。这意味着,当前启动的Activity还不能对用户可见,于是在找到与r.task相等的TaskRecord后并进入(!startIt)的逻辑判断时,就会依次执行Activity插入此TaskRecord中,将TaskRecord重新插入到mTaskRecord中,并调用成员变量mWindowManager所描述的WindowManagerService服务的成员函数addAppToken来为它创建一个AppWindowToken对象,然后马上返回,即不会执行下面的代码来启动参数r所描述的Activity组件。这相当于是延迟到参数r所描述的Activity组件可见时,才将它启动起来。这其实很容易理解:如果当前参数r所描述的Activity并不是从最顶层的task启动的,他自然就是对用户不可见的,因此也不会马上启动它。

    如果参数r对应的ActivityRecord是系统中第一个启动的Activity组件,那么ActivityStack类的成员函数startActivityLocked就只是简单地调用WindowManagerService服务的成员函数addAppToken来为它创建一个AppWindowToken对象就完事了。如果不是系统系统中第一个启动的Activity组件,那么ActivityStack类的成员函数startActivityLocked除了会调用WindowManagerService服务的成员函数addAppToken来为它创建一个AppWindowToken对象之外,还会为它创建一些启动动画等,我们忽略这些代码。

       从上面的分析就可以看出,无论如何,ActivityStack类的成员函数startActivityLocked都会调用WindowManagerService服务的成员函数addAppToken为正在启动的Activity组件创建一个AppWindowToken对象。创建完成这个AppWindowToken对象之后,如果参数doResume的值等于true,那么ActivityStack类的成员函数startActivityLocked就会继续调用另外一个成员函数resumeTopActivityLocked来继续执行启动参数r所描述的一个Activity组件。

      接下来,我们就继续分析WindowManagerService类的成员函数addAppToken的实现,以便可以了解WindowManagerService服务是如何为一个Activity组件创建一个AppWindowToken对象的。

    Step 2.WindowManagerService.addAppToken

    @Override
    public void addAppToken(int addPos, IApplicationToken token, int taskId, int stackId,
            int requestedOrientation, boolean fullscreen, boolean showWhenLocked, int userId,
            int configChanges, boolean voiceInteraction, boolean launchTaskBehind) {
        if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
                "addAppToken()")) {
            throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
        }

        // Get the dispatching timeout here while we are not holding any locks so that it
        // can be cached by the AppWindowToken.  The timeout value is used later by the
        // input dispatcher in code that does hold locks.  If we did not cache the value
        // here we would run the chance of introducing a deadlock between the window manager
        // (which holds locks while updating the input dispatcher state) and the activity manager
        // (which holds locks while querying the application token).
        long inputDispatchingTimeoutNanos;
        try {
            inputDispatchingTimeoutNanos = token.getKeyDispatchingTimeout() * 1000000L;
        } catch (RemoteException ex) {
            Slog.w(TAG, "Could not get dispatching timeout.", ex);
            inputDispatchingTimeoutNanos = DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
        }

        synchronized(mWindowMap) {
            AppWindowToken atoken = findAppWindowToken(token.asBinder());
            //如果已经为其新建过AppWindowToken就不再继续走下面的流程
            if (atoken != null) {
                Slog.w(TAG, "Attempted to add existing app token: " + token);
                return;
            }
            atoken = new AppWindowToken(this, token, voiceInteraction);
            atoken.inputDispatchingTimeoutNanos = inputDispatchingTimeoutNanos;
            atoken.groupId = taskId;
            atoken.appFullscreen = fullscreen;
            atoken.showWhenLocked = showWhenLocked;
            atoken.requestedOrientation = requestedOrientation;
            atoken.layoutConfigChanges = (configChanges &
                    (ActivityInfo.CONFIG_SCREEN_SIZE | ActivityInfo.CONFIG_ORIENTATION)) != 0;
            atoken.mLaunchTaskBehind = launchTaskBehind;
            if (DEBUG_TOKEN_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG, "addAppToken: " + atoken
                    + " to stack=" + stackId + " task=" + taskId + " at " + addPos);

            Task task = mTaskIdToTask.get(taskId);
            if (task == null) {
                createTask(taskId, stackId, userId, atoken);
            } else {
                task.addAppToken(addPos, atoken);
            }
            //以binder为key
            mTokenMap.put(token.asBinder(), atoken);

            // Application tokens start out hidden.
            atoken.hidden = true;
            atoken.hiddenRequested = true;

            //dump();
        }
    }
WindowManagerService.java文件位于frameworks/base/services/core/java/com/android/server/wm/目录当中。

从这个函数的整体逻辑来看,它完成了三件重要的事情。一是获取appToken的输入事件派发超时时间,二是调用AppWindowToken的构造函数,构造一个与来自AMS的appToken对象所对应的的WMS中appWindowToken对象,并做必要的赋值。三是,把这组一一对应的关系添加到mTokenMap这个HaspMap类型参数中。

mTokenMap这个参数的定义如下:

    /**
     * Mapping from a token IBinder to a WindowToken object.
     */
    final HashMap<IBinder, WindowToken> mTokenMap = new HashMap<IBinder, WindowToken>();
我们发现,mTokenMap.put(token.asBinder(),atoken) 这一语句中,是以一个Binder对象的IBinder接口为键值的。同时value值的类型应为WindowToken对象,但atoken是一个AppWindowToken类型对象。显然,这当中存在着继承关系:WindowToken是AppWindowToken的父类。

在创建AppWindowToken对象后并进行必要的赋值后,会根据传进来的taskId参数查找Task类型的变量。如果找到了,就执行Task.addAppToken()方法。如果没找到,就以taskId,atoken等作为参数,构建一个新的Task。

    private Task createTask(int taskId, int stackId, int userId, AppWindowToken atoken) {
        if (DEBUG_STACK) Slog.i(TAG, "createTask: taskId=" + taskId + " stackId=" + stackId
                + " atoken=" + atoken);
        final TaskStack stack = mStackIdToStack.get(stackId);
        if (stack == null) {
            throw new IllegalArgumentException("addAppToken: invalid stackId=" + stackId);
        }
        EventLog.writeEvent(EventLogTags.WM_TASK_CREATED, taskId, stackId);
        //调用Task类的构造函数,构造一个新的Task
        Task task = new Task(atoken, stack, userId);
        mTaskIdToTask.put(taskId, task);
        stack.addTask(task, !atoken.mLaunchTaskBehind /* toTop */);
        return task;
    }

createTask()方法同样位于WindowManagerService.java(frameworks/base/services/core/java/com/android/server/wm/)中。

那么这个Task类型的对象是何方神圣呢? 从Task.java的源码入手。

/*
 * ..........
 */

package com.android.server.wm;

import static com.android.server.wm.WindowManagerService.TAG;

import android.util.EventLog;
import android.util.Slog;

class Task {
    TaskStack mStack;
    final AppTokenList mAppTokens = new AppTokenList();
    final int taskId;
    final int mUserId;
    boolean mDeferRemoval = false;

    Task(AppWindowToken wtoken, TaskStack stack, int userId) {
        taskId = wtoken.groupId;
        mAppTokens.add(wtoken);
        mStack = stack;
        mUserId = userId;
    }

    ...................//省略其他函数

    void addAppToken(int addPos, AppWindowToken wtoken) {
        final int lastPos = mAppTokens.size();
        for (int pos = 0; pos < lastPos && pos < addPos; ++pos) {
            if (mAppTokens.get(pos).removed) {
                // addPos assumes removed tokens are actually gone.
                ++addPos;
            }
        }
        mAppTokens.add(addPos, wtoken);
        mDeferRemoval = false;
    }    <pre name="code" class="java">    ...................//省略其他函数

}


该文件目录位于frameworks/base/services/core/java/com/android/server/wm/Task.java。

其实,类似于AM中appToken和WM中的appWindowToken的对应关系,Task.java对应于frameworks/base/services/core/java/com/android/server/am/TaskRecord.java.

在Task.java文件中,需要关注的是mAppTokens这个参数。它是一个AppTokenList类型变量,实际上是一个ArrayList<>的变量。具体定义如下:

class AppTokenList extends ArrayList<AppWindowToken> {
}
该定义位于文件frameworks/base/services/core/java/com/android/server/wm/AppWindowToken.java当中。

        稍微往前回顾一点,无论是在WindowManagerService.addAppToken()-->Task.addAppToken(),或者是在WindowManagerService.addAppToken-->WindowManagerService.createTask()这一过程中,都执行了一个往mAppTokens这个列表中插入元素的操作。

        有读者会问,WMS.mTokenMap不就已经保存了所有AppWindowToken对象了吗,为什么还需要这一个ArrayList?存在即合理,老罗对这一点做出了很好的解释,我在这里引用下:

成员变量mAppTokens指向的也是一个ArrayList,不过它里面保存的是一系列AppWindowToken对象,每一个AppWindowToken对象都是用来描述一个Activity组件的窗口的,而这些AppWindowToken对象是以它们描述的窗口的Z轴坐标由小到大保存在这个ArrayList中的,这样我们就可以通过这个ArrayList来从上到下或者从下到上地遍历系统中的所有Activity组件窗口。由于这些AppWindowToken对象所描述的Activity组件窗口也是一个窗口,并且AppWindowToken类是从WindowToken继承下来的,因此,这些AppWindowToken对象还会同时被保存在成员变量mTokenMap所指向的一个HashMap

同时,老罗也对WindowManagerService.addAppToken()方法中的其余变量也做了很到位的解释:


1. inputDispatchingTimeoutNanos,表示Activity组件窗口收到了一个IO输入事件之后,如果没有在规定的时间内处理完成该事件,那么系统就认为超时。这个超时值可以由Activity组件本身来指定,即可以通过调用一个对应的ActivityRecord对象的成员函数getKeyDispatchingTimeout来获得。假如Activity组件没有指定的话,那么就会使用默认值DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS,即5000 * 1000000纳秒。

2. groupId,表示Activity组件所属的任务的ID。每一个Activity组件都是属于某一个任务的,而每一个任务都用来描述一组相关的Activity组件的,这些Activity组件用来完成用户的某一个操作。

3. appFullscreen,表示Activity组件的窗口是否是全屏的。如果一个Activity组件的窗口是全屏的,那么它就会将位于它下面的所有窗口都挡住,这样就可以在渲染系统UI时进行优化,即不用渲染位于全屏窗口以下的其它窗口。

4. requestedOrientation,表示Activity组件所请求的方向。这个方向可以是横的(LANDSCAPE),也可以是竖的(PORTRAIT)。

5. hidden,表示Activity组件是否是处于不可见状态。

6. hiddenRequested,与hidden差不多,也是表示Activity组件是否是处于不可见状态。两者的区别在于,在设置目标Activity组件的可见状态时,如果系统等待执行Activity组件切换操作,那么目标Activity组件的可见状态不会马上被设置,即它的hidden值不会马上被设置,而只是设置它的hiddenRequested值,表示它的可见性状态正在等待被设置。等到系统执行完成Activity组件切换操作之后,两者的值就会一致了。


    Step 3. new AppWindowToken()

/**
 * Version of WindowToken that is specifically for a particular application (or
 * really activity) that is displaying windows.
 */
class AppWindowToken extends WindowToken {
    // Non-null only for application tokens.
    // 一些成员变量的声明
    final IApplicationToken appToken;
    ......
    //AppWindowToken唯一的构造函数
    AppWindowToken(WindowManagerService _service, IApplicationToken _token,
            boolean _voiceInteraction) {
        super(_service, _token.asBinder(),
                WindowManager.LayoutParams.TYPE_APPLICATION, true);
        appWindowToken = this;
        appToken = _token;
        voiceInteraction = _voiceInteraction;
        mInputApplicationHandle = new InputApplicationHandle(this);
        mAnimator = service.mAnimator;
        mAppAnimator = new AppWindowAnimator(this);
    }
     
    ...... //其余方法
}
        这个函数定义在文件frameworks/base/services/java/com/android/server/AppWindowToken.java中。
AppWindowToken类的构造函数首先调用父类WindowToken的构造函数来执行父类的初始化工作,然后从父类WindowToken继承下来的成员变量appWindowToken以及自己的成员变量appToken的值。参数_token指向的是一个ActivityRecord对象的IBinder接口,因此,AppWindowToken类的成员变量appToken描述的就是一个ActivityRecord对象。

       WindowToken类的构造函数用来初始化token、windowType和explicit这三个成员变量。在我们这个场景中,成员变量token指向的也是一个ActivityRecord对象的IBinder接口,用来标志一个Activity组件的窗口,成员变量windowType用来描述窗口的类型,它的值等于WindowManager.LayoutParams.TYPE_APPLICATION,表示这是一个Activity组件窗口,成员变量explicit用来表示窗口是否是由应用程序进程请求添加的。

       注意,当一个WindowToken对象的成员变量appWindowToken的值不等于null时,就表示它实际描述的是Activity组件窗口,并且这个成员变量指向的就是与该Activity组件所关联的一个AppWindowToken对象。

       至此,我们就分析完成一个AppWindowToken对象的创建过程了,通过这个过程我们就可以知道,每一个Activity组件,在ActivityManagerService服务内部都有一个对应的ActivityRecord对象,并且在WindowManagerService服务内部关联有一个AppWindowToken对象。到此,问题1,已经得到了解决。让我们再次用一个“非正规”的UML时序图简单梳理下WMS建立AppWindowToken对象的3个步骤。


------------------------------------------------分割线------------------------------------------------

        对于问题2,从问题1的分析过程应该能得到一点思路。既然WMS要建立一个与appToken一一对应的appWindowToken,那么显然它应该是要起到一个唯一标识,及沟通连接WMS和AMS的作用。回到最开始传入startActivityLocked方法的参数r,它是一个ActivityRecord类型的变量。

       而r.appToken其实正是ActivityRecrod.java文件中定义的内部类。详见如下代码:

/**
 * An entry in the history stack, representing an activity.
 */
final class ActivityRecord {
    static final String TAG = ActivityManagerService.TAG;
    ......

    static class Token extends IApplicationToken.Stub {
        final WeakReference<ActivityRecord> weakActivity;

        Token(ActivityRecord activity) {
            weakActivity = new WeakReference<ActivityRecord>(activity);
        }
        ......
    }

    ......

    ActivityRecord(ActivityManagerService _service, ProcessRecord _caller,
            int _launchedFromUid, String _launchedFromPackage, Intent _intent, String _resolvedType,
            ActivityInfo aInfo, Configuration _configuration,
            ActivityRecord _resultTo, String _resultWho, int _reqCode,
            boolean _componentSpecified, ActivityStackSupervisor supervisor,
            ActivityContainer container, Bundle options) {
        service = _service;
        //r.appToken的真身就是Token类型的变量
        appToken = new Token(this);

         .......
    }
}
ActivityRecord类,及其内部类Token定义在文件/frameworks/base/services/core/java/com/android/server/am/ActivityRecord.java中。

        Token是ActivityRecord的内部静态类,我们先来看下Token的继承关系,Token extends IApplicationToken.Stub,从IApplicationToken.Stub类进行继承,根据Binder的机制可以知道Token是一个匿名Binder实体类,这个匿名Binder实体会传递给其他进程,其他进程会拿到Token的代理端。
         我们知道匿名Binder有两个比较重要的用途,一个是拿到Binder代理端后可跨Binder调用实体端的函数接口,另一个作用便是在多个进程中标识同一个对象。往往这两个作用是同时存在的,比如我们这里研究的Token就同时存在这两个作用,但最重要的便是后者,Token标识了一个ActivityRecord对象,即间接标识了一个Activity。

图片来源于参考资料1,老罗的文章


参考资料传送门:

1. 《Android应用程序窗口(Activity)与WindowManagerService服务的连接过程分析 》:老罗的经典文章之一。

2. 《深入理解Activity——Token之旅》:分析了Token类的作用,笔者文末的观点来源于此。


2
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:12221次
    • 积分:298
    • 等级:
    • 排名:千里之外
    • 原创:16篇
    • 转载:11篇
    • 译文:0篇
    • 评论:3条
    最新评论