Android 12 Token 机制

一、前言

在 android framework 框架中 activity 和 window 是相互关联的,而他们的管理者 AMS 和 WMS 是怎么来实现这种关联关系的,答案就是通过 token。

首先大家需要了解一下 LayoutParams,当然属性很多,简单了解即可:

base/core/java/android/view/WindowManager.java

java 复制代码
...
        //窗口类型
        //有3种主要类型如下:
        //ApplicationWindows取值在FIRST_APPLICATION_WINDOW与LAST_APPLICATION_WINDOW之间,是常用的顶层应用程序窗口,须将token设置成Activity的token;
        //SubWindows取值在FIRST_SUB_WINDOW和LAST_SUB_WINDOW之间,与顶层窗口相关联,需将token设置成它所附着宿主窗口的token;
        //SystemWindows取值在FIRST_SYSTEM_WINDOW和LAST_SYSTEM_WINDOW之间,不能用于应用程序,使用时需要有特殊权限,它是特定的系统功能才能使用;
        public int type;

        //WindowType:开始应用程序窗口
        public static final int FIRST_APPLICATION_WINDOW = 1;
        //WindowType:所有程序窗口的base窗口,其他应用程序窗口都显示在它上面
        public static final int TYPE_BASE_APPLICATION   = 1;
        //WindowType:普通应用程序窗口,token必须设置为Activity的token来指定窗口属于谁
        public static final int TYPE_APPLICATION        = 2;
        //WindowType:应用程序启动时所显示的窗口,应用自己不要使用这种类型,它被系统用来显示一些信息,直到应用程序可以开启自己的窗口为止
        public static final int TYPE_APPLICATION_STARTING = 3;
        //WindowType:结束应用程序窗口
        public static final int LAST_APPLICATION_WINDOW = 99;

        //WindowType:SubWindows子窗口,子窗口的Z序和坐标空间都依赖于他们的宿主窗口
        public static final int FIRST_SUB_WINDOW        = 1000;
        //WindowType: 面板窗口,显示于宿主窗口的上层
        public static final int TYPE_APPLICATION_PANEL  = FIRST_SUB_WINDOW;
        //WindowType:媒体窗口(例如视频),显示于宿主窗口下层
        public static final int TYPE_APPLICATION_MEDIA  = FIRST_SUB_WINDOW+1;
        //WindowType:应用程序窗口的子面板,显示于所有面板窗口的上层
        public static final int TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW+2;
        //WindowType:对话框,类似于面板窗口,绘制类似于顶层窗口,而不是宿主的子窗口
        public static final int TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW+3;
        //WindowType:媒体信息,显示在媒体层和程序窗口之间,需要实现半透明效果
        public static final int TYPE_APPLICATION_MEDIA_OVERLAY  = FIRST_SUB_WINDOW+4;
        //WindowType:子窗口结束
        public static final int LAST_SUB_WINDOW         = 1999;

        //WindowType:系统窗口,非应用程序创建
        public static final int FIRST_SYSTEM_WINDOW     = 2000;
        //WindowType:状态栏,只能有一个状态栏,位于屏幕顶端,其他窗口都位于它下方
        public static final int TYPE_STATUS_BAR         = FIRST_SYSTEM_WINDOW;
        //WindowType:搜索栏,只能有一个搜索栏,位于屏幕上方
        public static final int TYPE_SEARCH_BAR         = FIRST_SYSTEM_WINDOW+1;
        //WindowType:电话窗口,它用于电话交互(特别是呼入),置于所有应用程序之上,状态栏之下
        public static final int TYPE_PHONE              = FIRST_SYSTEM_WINDOW+2;
        //WindowType:系统提示,出现在应用程序窗口之上
        public static final int TYPE_SYSTEM_ALERT       = FIRST_SYSTEM_WINDOW+3;
        //WindowType:锁屏窗口
        public static final int TYPE_KEYGUARD           = FIRST_SYSTEM_WINDOW+4;
        //WindowType:信息窗口,用于显示Toast
        public static final int TYPE_TOAST              = FIRST_SYSTEM_WINDOW+5;
        //WindowType:系统顶层窗口,显示在其他一切内容之上,此窗口不能获得输入焦点,否则影响锁屏
        public static final int TYPE_SYSTEM_OVERLAY     = FIRST_SYSTEM_WINDOW+6;
        //WindowType:电话优先,当锁屏时显示,此窗口不能获得输入焦点,否则影响锁屏
        public static final int TYPE_PRIORITY_PHONE     = FIRST_SYSTEM_WINDOW+7;
        //WindowType:系统对话框
        public static final int TYPE_SYSTEM_DIALOG      = FIRST_SYSTEM_WINDOW+8;
        //WindowType:锁屏时显示的对话框
        public static final int TYPE_KEYGUARD_DIALOG    = FIRST_SYSTEM_WINDOW+9;
        //WindowType:系统内部错误提示,显示于所有内容之上
        public static final int TYPE_SYSTEM_ERROR       = FIRST_SYSTEM_WINDOW+10;
        //WindowType:内部输入法窗口,显示于普通UI之上,应用程序可重新布局以免被此窗口覆盖
        public static final int TYPE_INPUT_METHOD       = FIRST_SYSTEM_WINDOW+11;
        //WindowType:内部输入法对话框,显示于当前输入法窗口之上
        public static final int TYPE_INPUT_METHOD_DIALOG= FIRST_SYSTEM_WINDOW+12;
        //WindowType:墙纸窗口
        public static final int TYPE_WALLPAPER          = FIRST_SYSTEM_WINDOW+13;
        //WindowType:状态栏的滑动面板
        public static final int TYPE_STATUS_BAR_PANEL   = FIRST_SYSTEM_WINDOW+14;
        //WindowType:安全系统覆盖窗口,这些窗户必须不带输入焦点,否则会干扰键盘
        public static final int TYPE_SECURE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW+15;
        //WindowType:拖放伪窗口,只有一个阻力层(最多),它被放置在所有其他窗口上面
        public static final int TYPE_DRAG               = FIRST_SYSTEM_WINDOW+16;
        //WindowType:状态栏下拉面板
        public static final int TYPE_STATUS_BAR_SUB_PANEL = FIRST_SYSTEM_WINDOW+17;
        //WindowType:鼠标指针
        public static final int TYPE_POINTER = FIRST_SYSTEM_WINDOW+18;
        //WindowType:导航栏(有别于状态栏时)
        public static final int TYPE_NAVIGATION_BAR = FIRST_SYSTEM_WINDOW+19;
        //WindowType:音量级别的覆盖对话框,显示当用户更改系统音量大小
        public static final int TYPE_VOLUME_OVERLAY = FIRST_SYSTEM_WINDOW+20;
        //WindowType:起机进度框,在一切之上
        public static final int TYPE_BOOT_PROGRESS = FIRST_SYSTEM_WINDOW+21;
        //WindowType:假窗,消费导航栏隐藏时触摸事件
        public static final int TYPE_HIDDEN_NAV_CONSUMER = FIRST_SYSTEM_WINDOW+22;
        //WindowType:梦想(屏保)窗口,略高于键盘
        public static final int TYPE_DREAM = FIRST_SYSTEM_WINDOW+23;
        //WindowType:导航栏面板(不同于状态栏的导航栏)
        public static final int TYPE_NAVIGATION_BAR_PANEL = FIRST_SYSTEM_WINDOW+24;
        //WindowType:universe背后真正的窗户
        public static final int TYPE_UNIVERSE_BACKGROUND = FIRST_SYSTEM_WINDOW+25;
        //WindowType:显示窗口覆盖,用于模拟辅助显示设备
        public static final int TYPE_DISPLAY_OVERLAY = FIRST_SYSTEM_WINDOW+26;
        //WindowType:放大窗口覆盖,用于突出显示的放大部分可访问性放大时启用
        public static final int TYPE_MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW+27;
        //WindowType:......
        public static final int TYPE_KEYGUARD_SCRIM           = FIRST_SYSTEM_WINDOW+29;
        public static final int TYPE_PRIVATE_PRESENTATION = FIRST_SYSTEM_WINDOW+30;
        public static final int TYPE_VOICE_INTERACTION = FIRST_SYSTEM_WINDOW+31;
        public static final int TYPE_ACCESSIBILITY_OVERLAY = FIRST_SYSTEM_WINDOW+32;
        //WindowType:系统窗口结束
        public static final int LAST_SYSTEM_WINDOW      = 2999;
        ......
    }

这里需要我们知道的是 WindowManager.LayoutParams 有三种窗口 type,分别对应为:

  • 应用窗口程序:type 值在 FIRST_APPLICATION_WINDOW ~ LAST_APPLICATION_WINDOW,必须将 token 设置为 Activity 的 token。
  • 子窗口: type 值在 FIRST_SUB_WINDOW ~ LAST_SUB_WINDOW SubWindows,必须将 token 设置为 Activity 的 token。比如 PopupWindow。
  • 系统窗口: type 值在 FIRST_SYSTEM_WINDOW ~ LAST_SYSTEM_WINDOW,使用需要权限,属于特定的系统功能。比如 Toast。

这里就说到了 token 的问题,应用窗口程序和子窗口均需要获取到 Activity 的 token。那么 token 是什么呢?

二、Token 是什么?

Token 是 ActivityRecord 的静态内部类,我们先来看下 Token 的继承关系,Token 继承于IApplicationToken.Stub,从 IApplicationToken.Stub 类进行继承,根据 Binder 的机制可以知道 Token 是一个匿名 Binder 实体类,这个匿名 Binder 实体会传递给其他进程,其他进程会拿到 Token 的代理端。

我们知道匿名 Binder 有两个比较重要的用途,一个是拿到 Binder 代理端后可跨 Binder 调用实体端的函数接口,另一个作用便是在多个进程中标识同一个对象。往往这两个作用是同时存在的,比如我们这里研究的 Token 就同时存在这两个作用,但最重要的便是后者,Token 标识了一个 ActivityRecord 对象,即间接标识了一个 Activity。

base/service/core/java/com/android/server/wm/ActivityRecord.java

java 复制代码
static class Token extends IApplicationToken.Stub {
        private WeakReference<ActivityRecord> weakActivity;
        private final String name;
        private final String tokenString;

        Token(Intent intent) {
            name = intent.getComponent().flattenToShortString();
            tokenString = 
            "Token{" + Integer.toHexString(System.identityHashCode(this)) + "}";
        }

        private void attach(ActivityRecord activity) {
            if (weakActivity != null) {
                throw new IllegalStateException("Already attached..." + this);
            }
            weakActivity = new WeakReference<>(activity);
        }

        private static @Nullable ActivityRecord tokenToActivityRecordLocked(Token token) {
            if (token == null) {
                return null;
            }
            ActivityRecord r = token.weakActivity.get();
            if (r == null || r.getRootTask() == null) {
                return null;
            }
            return r;
        }

        @Override
        public String toString() {
            StringBuilder sb = new StringBuilder(128);
            sb.append("Token{");
            sb.append(Integer.toHexString(System.identityHashCode(this)));
            sb.append(' ');
            if (weakActivity != null) {
                sb.append(weakActivity.get());
            }
            sb.append('}');
            return sb.toString();
        }

        @Override
        public String getName() {
            return name;
        }
    }

三、Token 的创建

java 复制代码
private ActivityRecord(ActivityTaskManagerService _service, WindowProcessController _caller,
            int _launchedFromPid, int _launchedFromUid, String _launchedFromPackage,
            @Nullable String _launchedFromFeature, Intent _intent, String _resolvedType,
            ActivityInfo aInfo, Configuration _configuration, ActivityRecord _resultTo,
            String _resultWho, int _reqCode, boolean _componentSpecified,
            boolean _rootVoiceInteraction, ActivityTaskSupervisor supervisor,
            ActivityOptions options, ActivityRecord sourceRecord, PersistableBundle persistentState,
            TaskDescription _taskDescription, long _createTime) {
        super(_service.mWindowManager, new Token(_intent).asBinder(), TYPE_APPLICATION, true,
                null /* displayContent */, false /* ownerCanManageAppTokens */);

        mAtmService = _service;
        appToken = (Token) token;
        info = aInfo;
        mUserId = UserHandle.getUserId(info.applicationInfo.uid);
        packageName = info.applicationInfo.packageName;
        intent = _intent;
        。。。。。。

在 ActivityRecord 的构造方法中创建这个 token ,并赋值给成员变量 appToken。标识着当前这个ActivityRecord,即间接代表着一个Activity。

而创建 ActivityRecord 的地方在 ActivityStarter.java 的 executeRequest 方法中,可以参考 Android四大组件系列2 Activity启动流程(上)

ActivityStarter.java

java 复制代码
private int executeRequest(Request request) {
     ......
     final ActivityRecord r = new ActivityRecord.Builder(mService)
                .setCaller(callerApp)
                .setLaunchedFromPid(callingPid)
                .setLaunchedFromUid(callingUid)
                .setLaunchedFromPackage(callingPackage)
                .setLaunchedFromFeature(callingFeatureId)
                .setIntent(intent)
                .setResolvedType(resolvedType)
                .setActivityInfo(aInfo)
                .setConfiguration(mService.getGlobalConfiguration())
                .setResultTo(resultRecord)
                .setResultWho(resultWho)
                .setRequestCode(requestCode)
                .setComponentSpecified(request.componentSpecified)
                .setRootVoiceInteraction(voiceSession != null)
                .setActivityOptions(checkedOptions)
                .setSourceRecord(sourceRecord)
                .build();
    ......
}

ActivityRecord.java

java 复制代码
private ActivityRecord(ActivityTaskManagerService _service, WindowProcessController _caller,
            int _launchedFromPid, int _launchedFromUid, String _launchedFromPackage,
            @Nullable String _launchedFromFeature, Intent _intent, String _resolvedType,
            ActivityInfo aInfo, Configuration _configuration, ActivityRecord _resultTo,
            String _resultWho, int _reqCode, boolean _componentSpecified,
            boolean _rootVoiceInteraction, ActivityTaskSupervisor supervisor,
            ActivityOptions options, ActivityRecord sourceRecord, PersistableBundle persistentState,
            TaskDescription _taskDescription, long _createTime) {
        super(_service.mWindowManager, new Token(_intent).asBinder(), TYPE_APPLICATION, true,
                null /* displayContent */, false /* ownerCanManageAppTokens */);
                // 可知调用父类也就是 WindowToken 的构造方法并new了一个 token 作为参数传入

        mAtmService = _service;
        appToken = (Token) token;
        info = aInfo;
        mUserId = UserHandle.getUserId(info.applicationInfo.uid);
        packageName = info.applicationInfo.packageName;
        intent = _intent;
}

WindowToken.java

java 复制代码
protected WindowToken(WindowManagerService service, IBinder _token, int type,
            boolean persistOnEmpty, DisplayContent dc, boolean ownerCanManageAppTokens) {
        this(service, _token, type, persistOnEmpty, dc, ownerCanManageAppTokens,
                false /* roundedCornerOverlay */, false /* fromClientToken */, null /* options */);
    
    }


protected WindowToken(WindowManagerService service, IBinder _token, int type,
            boolean persistOnEmpty, DisplayContent dc, boolean ownerCanManageAppTokens,
            boolean roundedCornerOverlay, boolean fromClientToken, @Nullable Bundle options) {
        super(service);
        token = _token;
        windowType = type;
        mOptions = options;
        mPersistOnEmpty = persistOnEmpty;
        mOwnerCanManageAppTokens = ownerCanManageAppTokens;
        mRoundedCornerOverlay = roundedCornerOverlay;
        mFromClientToken = fromClientToken;
        if (dc != null) {
            dc.addWindowToken(token, this);
        }
    }

WindowToken 是 ActivityRecord 的父类,最终赋值给 WindowToken 的 token 变量 。

java 复制代码
class WindowToken extends WindowContainer<WindowState> {
    private static final String TAG = TAG_WITH_CLASS_NAME ? "WindowToken" : TAG_WM;

    /** The actual token */
    final IBinder token;

    /** The type of window this token is for, as per {@link WindowManager.LayoutParams} */
    final int windowType;
    ......
}

以上 Token 的创建均在 AMS 服务端完成。

四、Token 怎么传递给客户端?

可以参考 Android四大组件系列3 Activity启动流程(下)

ActivityTaskSupervisor.realStartActivityLocked

java 复制代码
boolean realStartActivityLocked(ActivityRecord r, WindowProcessController proc,
            boolean andResume, boolean checkConfig) throws RemoteException {
......
 // Create activity launch transaction.
                final ClientTransaction clientTransaction = ClientTransaction.obtain(
                        proc.getThread(), r.appToken); 
                        // 这里的 r.appToken 就是 ActivityRecord 的 token
......

// Schedule transaction.
                mService.getLifecycleManager().scheduleTransaction(clientTransaction);
                // 回传给客户端进程,执行 ClientTransaction
}

app 端通过 TransactionExecutor 来执行 ClientTransaction

java 复制代码
public void execute(ClientTransaction transaction) {
        if (DEBUG_RESOLVER) Slog.d(TAG, tId(transaction) + "Start resolving transaction");

        final IBinder token = transaction.getActivityToken(); // 获取AMS传递过来的token
        ......

最后执行 LaunchActivityItem 的 execute

LaunchActivityItem.java

java 复制代码
public void preExecute(ClientTransactionHandler client, IBinder token) {
        ActivityClientRecord r = new ActivityClientRecord(token, mIntent, mIdent, mInfo,
                mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor, mState, mPersistentState,
                mPendingResults, mPendingNewIntents, mActivityOptions, mIsForward, mProfilerInfo,
                client, mAssistToken, mFixedRotationAdjustments, mShareableActivityToken,
                mLaunchedFromBubble);
                // 通过 token 创建 ActivityClientRecord ,至此绑定成功
        client.addLaunchingActivity(token, r);
        client.updateProcessState(mProcState, false);
        client.updatePendingConfiguration(mCurConfig);
        if (mActivityClientController != null) {
            ActivityClient.setActivityClientController(mActivityClientController);
        }
    }

随后调用 Activity 的 attach 方法,会传入 token

Activity.java

java 复制代码
final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config, String referrer, IVoiceInteractor voiceInteractor,
            Window window, ActivityConfigCallback activityConfigCallback, IBinder assistToken,
            IBinder shareableActivityToken) {
        attachBaseContext(context);

        mFragments.attachHost(null /*parent*/);

        mWindow = new PhoneWindow(this, window, activityConfigCallback);
        mWindow.setWindowControllerCallback(mWindowControllerCallback);
        mWindow.setCallback(this);
        mWindow.setOnWindowDismissedCallback(this);
        mWindow.getLayoutInflater().setPrivateFactory(this);
        if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
            mWindow.setSoftInputMode(info.softInputMode);
        }
        if (info.uiOptions != 0) {
            mWindow.setUiOptions(info.uiOptions);
        }
        mUiThread = Thread.currentThread();

        mMainThread = aThread;
        mInstrumentation = instr;
        mToken = token; // token 保存到 activity 中
        ......
        mWindow.setWindowManager(
                (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
                mToken, mComponent.flattenToString(),
                (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
                // 关键方法,把 mToken 传递给 PhoneWindow 的 mAppToken

        mWindowManager = mWindow.getWindowManager(); 
        // window 的WindowManager 赋值给了activity
        ......

通过 mWindow.setWindowManager 把 mToken 传递给 PhoneWindow 的成员变量 mAppToken。

这样就建立了 AMS 中的 ActivityRecord 与 客户端进程 Activity 以及对应的 PhoneWindow 之间的一一对应关系。

我们接下来看下 Window 的 setWindowManager 方法:

Window.java

java 复制代码
public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
            boolean hardwareAccelerated) {
        mAppToken = appToken; // 赋值 token
        mAppName = appName;
        mHardwareAccelerated = hardwareAccelerated;
        if (wm == null) {
            wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
        }
        mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
    }

在 setWindowManager 方法中,token 被赋值到 Window 的 mAppToken 属性上,同时在当前 Window 上创建了 WindowManager 。

继续看 WindowManagerImpl 的 createLocalWindowManager,其中把当前 Window 作为参数传入:

WindowManagerImpl.java

java 复制代码
public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
        return new WindowManagerImpl(mContext, parentWindow, mWindowContextToken);
}

private WindowManagerImpl(Context context, Window parentWindow,
            @Nullable IBinder windowContextToken) {
        mContext = context;
        mParentWindow = parentWindow; // 新建的 PhoneWindow 赋值给了 mParentWindow
        mWindowContextToken = windowContextToken;
}

从以上可知 WindowManagerImpl 的 mParentWindow 参数非空,并且值是新建的 PhoneWindow。

这个非常重要,在下面窗口添加的时候会判断这个变量,然后来决定 token 的赋值操作。

五、通过添加窗口 Token 传递到 WMS

Activity.java

java 复制代码
void makeVisible() {
        if (!mWindowAdded) {
            ViewManager wm = getWindowManager();
            wm.addView(mDecor, getWindow().getAttributes());
            mWindowAdded = true;
        }
        mDecor.setVisibility(View.VISIBLE);
    }

关注这个 WindowManager ,是通过 getWindowManager 获得的。

Activity.java

java 复制代码
public WindowManager getWindowManager() {
        return mWindowManager;
    }

我们知道这个 mWindowManager ,其实是 Window 的 WindowManager

Window.java

java 复制代码
 mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);

也就是说这个 WindowManagerImpl 的 mParentWindow 不为空,并且就是之前 new 的 PhoneWindow。

接下来继续看窗口的添加代码:

添加窗口的操作在 WindowManagerGlobal 的 addView 方法中如下:

WindowManagerGlobal.java

java 复制代码
public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow, int userId) {
        if (view == null) {
            throw new IllegalArgumentException("view must not be null");
        }
        if (display == null) {
            throw new IllegalArgumentException("display must not be null");
        }
        if (!(params instanceof WindowManager.LayoutParams)) {
            throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
        }

        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
        if (parentWindow != null) { // parentWindow 不为空
            parentWindow.adjustLayoutParamsForSubWindow(wparams);
        } else {
            // If there's no parent, then hardware acceleration for this view is
            // set from the application's hardware acceleration setting.
            final Context context = view.getContext();
            if (context != null
                    && (context.getApplicationInfo().flags
                            & ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) {
                wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
            }
        }
        ......
}

由前面的 parentWindow 赋值情况我们知道,对于 Activity 启动流程来说,走到这里,parentWindow一定是不为 null 的。

其实:只有系统窗口,parentWindow 才会为 null。

以上可知 parentWindow 不为空,然后走 Window 的 adjustLayoutParamsForSubWindow 函数确定 token值。

Window.java

java 复制代码
void adjustLayoutParamsForSubWindow(WindowManager.LayoutParams wp) {
        CharSequence curTitle = wp.getTitle();
        if (wp.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
                wp.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
            if (wp.token == null) {
                View decor = peekDecorView();
                if (decor != null) {
                    wp.token = decor.getWindowToken();// 如果是子窗口,则把父窗口的token赋值
                }
            }
            ......
        } else if (wp.type >= WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW &&
                wp.type <= WindowManager.LayoutParams.LAST_SYSTEM_WINDOW) {
            ......
        } else {
            if (wp.token == null) {
                wp.token = mContainer == null ? mAppToken : mContainer.mAppToken;
                // 把当前窗口的 mAppToken 赋值给 wp.token
            }
            ......
        }
        if (wp.packageName == null) {
            wp.packageName = mContext.getPackageName();
        }
        if (mHardwareAccelerated ||
                (mWindowAttributes.flags & FLAG_HARDWARE_ACCELERATED) != 0) {
            wp.flags |= FLAG_HARDWARE_ACCELERATED;
        }
    }

这里会判断窗口类型,设置 token。获取到 Token 后就保存在了 LayoutParams 里面,可知这个 token 来自窗口的 mAppToken。

之后被传递到 ViewRootImpl.setView 中去。

java 复制代码
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            if (mView == null) {
                ...
                mWindowAttributes.copyFrom(attrs);
                // 拷贝含有 token 的 attrs 到 mWindowAttributes
                ...
                 res = mWindowSession.addToDisplayAsUser(mWindow, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(), userId,
                            mInsetsController.getRequestedVisibilities(), inputChannel, mTempInsets,
                            mTempControls);
                            // 传递 mWindowAttributes 到 WMS 
            }
        }

这里将包含 token 的 LayoutParams 通过 Session 最终调用到了 WMS 的 addWindow 方法(这些流程前面的章节都提到过,所以这里就简单带过)。

WindowManagerService.java

java 复制代码
public int addWindow(Session session, IWindow client, LayoutParams attrs, int viewVisibility,
            int displayId, int requestUserId, InsetsState requestedVisibility,
            InputChannel outInputChannel, InsetsState outInsetsState,
            InsetsSourceControl[] outActiveControls) {
            
            ...
            // 通过 token 获取到 DisplayContent,来判断是否是非法的显示内容
            final DisplayContent displayContent = getDisplayContentOrCreate(displayId, attrs.token);

            
            ......
            //根据 token 获取 activity,判断所添加的合法性
            WindowToken token = displayContent.getWindowToken(
                    hasParent ? parentWindow.mAttrs.token : attrs.token);
                    // 通过 displayContent 从 mTokenMap 中获取对应的 WindowToken,第一次获取为空
            if (token == null) {
                ......
                    final IBinder binder = attrs.token != null ? attrs.token : client.asBinder();
                    token = new WindowToken.Builder(this, binder, type)
                            .setDisplayContent(displayContent)
                            .setOwnerCanManageAppTokens(session.mCanAddInternalSystemWindow)
                            .setRoundedCornerOverlay(isRoundedCornerOverlay)
                            .build();
                            // 通过这个 token 创建一个新的 WindowToken,
                            // 并会被添加到displayContent的mTokenMap 中
                ......
            }
            ......
            final WindowState win = new WindowState(this, session, client, token, parentWindow,
                    appOp[0], attrs, viewVisibility, session.mUid, userId,
                    session.mCanAddInternalSystemWindow);
                    // 然后以 WindowToken 创建 WindowState 
            
}

综上,简单总结一下,每个 Activity 都有一个自己的 token,用于各种校验,而对于 WMS 来说,如果想添加非系统级别的窗口,都需要一个合理的 token。

接下来我们来分析下其他类型窗口的创建,比如 Dialog。未完待续。。。。。。

相关推荐
敲代码敲到头发茂密1 小时前
【大语言模型】LangChain 核心模块介绍(Memorys)
android·语言模型·langchain
H1002 小时前
重构(二)
android·重构
拓端研究室2 小时前
R基于贝叶斯加法回归树BART、MCMC的DLNM分布滞后非线性模型分析母婴PM2.5暴露与出生体重数据及GAM模型对比、关键窗口识别
android·开发语言·kotlin
zhangphil3 小时前
Android简洁缩放Matrix实现图像马赛克,Kotlin
android·kotlin
m0_512744643 小时前
极客大挑战2024-web-wp(详细)
android·前端
lw向北.3 小时前
Qt For Android之环境搭建(Qt 5.12.11 Qt下载SDK的处理方案)
android·开发语言·qt
不爱学习的啊Biao3 小时前
【13】MySQL如何选择合适的索引?
android·数据库·mysql
Clockwiseee4 小时前
PHP伪协议总结
android·开发语言·php
mmsx10 小时前
android sqlite 数据库简单封装示例(java)
android·java·数据库
众拾达人13 小时前
Android自动化测试实战 Java篇 主流工具 框架 脚本
android·java·开发语言