WindowManagerService架构剖析之token分析

WindowManagerService工作方式

《WindowManagerService架构剖析之addWindow流程》
《WindowManagerService架构剖析之窗口分组与分层》
《WindowManagerService架构剖析之token分析》

学习过AMS、WMS就知道,两个模块中都有token,这个token代表什么意思?两个模块内的token又有什么联系呢?这种联系可以保证AMS与WMS实现什么样的功能?
在《WindowManagerService架构剖析之addWindow流程》https://www.jianshu.com/p/effaff9ab9f2 文章中说到了addWindow的详细流程,当时留了一个问题:

WindowManagerService.java
public int addWindow(Session session, IWindow client, int seq,
            WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
            Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
            InputChannel outInputChannel) {
//......
        synchronized(mWindowMap) {
//......
                AppWindowToken atoken = null;
                final boolean hasParent = parentWindow != null;
                WindowToken token = displayContent.getWindowToken(hasParent ? parentWindow.mAttrs.token : attrs.token);
                if (token == null) {
                //这儿如果符合window type的条件,直接return,表明当前addWindow过程失败
//......
                }
//......
        }
}

这里的函数displayContent.getWindowToken(...)究竟是什么意思?深入看看:

DisplayContent.java

// Mapping from a token IBinder to a WindowToken object on this display.
    private final HashMap mTokenMap = new HashMap();
    WindowToken getWindowToken(IBinder binder) {
        return mTokenMap.get(binder);
    }

原来是一个hashmap,存储了一个IBinder和windowToken数据,既然有get方法,那么这个hashmap是什么情况下存储的?再深入看看:


WindowManagerService架构剖析之token分析_第1张图片
WMS-token传递过程.jpg

ActivityStarter->startActivity(...)是startActivity(...)过程中的一个步骤,从这个传递过程来看,很明显,WMS-DisplayContent中的mTokenMap中的token,就是AMS中传入的appToken,这就说明一个Activity对应一个ActivityRecord,一个ActivityRecord对应一个appToken,一个appToken对应WMS的token,而WMS的token对应WMS的Window(这儿强调一下,不是一个token仅仅对应一个window,这是一对多的关系,下面会讲),这样的对应真的很和谐,很好的联系了AMS和WMS两大模块,通过token我们可以直接在两大模块中check当前的window和当前的activity,接下来我们需要通过源码来证实token的具体应用。
启动一个Activity的时候,token起了什么作用?
直接到Activity Launch的地方看一下:

ActivityThread.java
private class ApplicationThread extends IApplicationThread.Stub {
        public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
                ActivityInfo info, Configuration curConfig, Configuration overrideConfig,
                CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor,
                int procState, Bundle state, PersistableBundle persistentState,
                List pendingResults, List pendingNewIntents,
                boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) {
//......
                    ActivityClientRecord r = new ActivityClientRecord();
                    r.token = token;
//......
                    sendMessage(H.LAUNCH_ACTIVITY, r);
        }
}

这儿赋值的token肯定也是上层传递下来的,只要分析一下上层的传递流程,就知道肯定是ActivityRecord构造的时候创建的Token对象。也就是上文说的appToken,和WMS的token是一样的。
继续执行:

ActivityThread.java
public final class ActivityThread {
      private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
//......
            Activity a = performLaunchActivity(r, customIntent);
//......
      }

      private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
//......
            if (activity != null) {
//......
                  activity.attach(appContext, this, getInstrumentation(), r.token,
                        r.ident, app, r.intent, r.activityInfo, title, r.parent,
                        r.embeddedID, r.lastNonConfigurationInstances, config,
//......
            }
//......
      }
}

attach主要就创建Window作为Activity显示的容器,看一下具体执行的代码:

Activity.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) {
  //......
        mWindow = new PhoneWindow(this, window, activityConfigCallback);
        mWindow.setWindowControllerCallback(this);
        mWindow.setCallback(this);
        mWindow.setOnWindowDismissedCallback(this);
  //......
        mToken = token;
  //......
        mWindow.setWindowManager(
                (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
                mToken, mComponent.flattenToString(),
                (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
        if (mParent != null) {
            mWindow.setContainer(mParent.getWindow());
        }
        mWindowManager = mWindow.getWindowManager();
  //......
    }

attach中主要是创建一个window对象,然后设置一些有关window的回调接口等等,之后将AMS或者activity相关的一些参数传入window中,这些参数中当然也包括appToken参数,这样的传递保证了两个功能:
1.将AMS中创建的ActivityRecord和Window挂钩,当前的window明确知道自己是哪一个Activity创建的。
2.Window和AMS有联系,同时又和WMS有关系,appToken则保证了这种同步机制。

Window.java
public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
            boolean hardwareAccelerated) {
        mAppToken = appToken;
        mAppName = appName;
        mHardwareAccelerated = hardwareAccelerated
                || SystemProperties.getBoolean(PROPERTY_HARDWARE_UI, false);
        if (wm == null) {
            wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
        }
        mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
    }

说了这么多Token,那么这个Token到底是个什么鬼?Token具体是定义在ActivityRecord.java中的。它的定义代码是:

ActivityRecord.java
static class Token extends IApplicationToken.Stub {
        private final WeakReference weakActivity;

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

        private static ActivityRecord tokenToActivityRecordLocked(Token token) {
            if (token == null) {
                return null;
            }
            ActivityRecord r = token.weakActivity.get();
            if (r == null || r.getStack() == 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(' ');
            sb.append(weakActivity.get());
            sb.append('}');
            return sb.toString();
        }
    }

token持有一个ActivityRecord对象,从任何角度来讲,这个token都是唯一标识Activity的对象,那么这个token在代码中有什么具体的应用。直观一点,下面挑选几个地方分析一下。
1.启动一个activity的地方,通过token唯一标识特定的ActivityClientRecord,就知道启动哪一个Activity。

ActivityThread.java
final ArrayMap mActivities = new ArrayMap<>();
final void handleResumeActivity(IBinder token,
            boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
        ActivityClientRecord r = mActivities.get(token);
//......
}

2.当只知道token的情况下,也可以通过token来获取当前的ActivityRecord

ActivityRecord.java
static ActivityRecord forTokenLocked(IBinder token) {
        try {
            return Token.tokenToActivityRecordLocked((Token)token);
        } catch (ClassCastException e) {
            Slog.w(TAG, "Bad activity token: " + token, e);
            return null;
        }
    }

现在还要确认一下,addWindow中attrs.token是什么鬼?什么地方赋值的?本文刚开始的地方贴了一段代码,现在不明白的可以上去看一下。
WindowToken token = displayContent.getWindowToken(hasParent ? parentWindow.mAttrs.token : attrs.token);
我们现在知道这个displayContent.getWindowToken(...)实际上就是mTokenMap,这个hashmap就是AMS传入的值put进去的,但是这个函数的参数attrs.token怎么来的?这个问题还是需要从源码中获取,在《WindowManagerService架构剖析之addWindow流程》https://www.jianshu.com/p/effaff9ab9f2 文章对addWindow的过程详细分析了,下面是attrs.token赋值的过程:

WindowManagerGlobal.java
public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
//......
        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
        if (parentWindow != null) {
            parentWindow.adjustLayoutParamsForSubWindow(wparams);
        }
//......
}
Window.java
void adjustLayoutParamsForSubWindow(WindowManager.LayoutParams wp) {
//......
        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();
                }
            }
//......
        } else if (wp.type >= WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW &&
//......
        } else {
            if (wp.token == null) {
                wp.token = mContainer == null ? mAppToken : mContainer.mAppToken;
            }
//......
        }
//......
    }

Window中的mAppToken恰恰就是之前Activity.attach创建Window对象的时候传入的。现在可以非常肯定了AMS中的token,WMS中的token,连传入addWindow的attrs.token,都是一个token,都是ActivityRecord构造函数中创建的Token对象。重要的事情说三遍。为什么要这么辛苦在不同的对象中维护这么多的token了,其实还是为了校验使用,为了维护Activity-window一致性的判断,理解了这个,那么WMS中很多判断的地方都好理解了,就是为了校验当前的window是否是正确的window或者说是否是符合activity规则的window。
WMS中还有一个重要的变量也持有token,就是WindowState,这个类之于WMS就像ActivityRecord之于AMS,这个WindowState对象在WMS中就相当于一个window,但是熟悉代码的都知道,同一个token可以创建对个window,这涉及到window的分组,token就是window分组的依据,有兴趣的可以看一下WindowState的源码,本文不展开讲了。或者参考一下文章《WindowManagerService架构剖析之窗口分组与分层》

你可能感兴趣的:(WindowManagerService架构剖析之token分析)