Preface First, let's look at the layout hierarchy of an Android interface. The most intuitive one is: We can clearly see that this interface is divided into three parts: the top status bar (statusbar), the bottom navigation bar (navigationbar), and the application interface. Off topic: To view the hierarchy of the layout, the tools or approaches can be referred to below. - Android Studio: Tools->Layout Inspector->Select the process you want to view;
- SDK Tools: tools/hierarchyviewer.bat. However, the latest recommendation is to use tools/monitor.bat instead of the separate hierarchyviewer.bat; hierarchyviewer.bat also exists in the project directory prebuilts/devtools/tools
This article mainly introduces the process of creating a window in Activity and adding it to WMS. The second part is a review and conclusion, which first gives a relatively concise summary of the contents of the above two processes. The third part is the process of adding the Activity window. Tracking the source code details the process of creating the Activity window and adding it to WMS. Since the third part is tracing the source code, this process is relatively long, involves a lot, and is relatively boring. So the summary is put in the second part first. In this way, if you have understood the source code or this process, you can only read the second part. If you have not understood it, you can also get a general understanding of it through the second part, and then check the third part. If you encounter a part that is not clear, you can go back to the second part for comparison and understanding. This article is also based on the source code of Android 10. If there is anything wrong or inadequate, please feel free to point it out. Summary The preface has already explained why the summary is placed first. Let’s take a closer look. The second part mainly introduces the following contents: - Window type: Introduction to window types
- Several important classes: some common classes in the process of window creation and adding to WMS, and understand the relationship between them
- Activity creation window added to WMS Overview: A brief summary of the Activity creation process and the process of adding to WMS
- Some structural diagrams in Activity: The whole process, the relationship between some classes/structures associated with Activity, I think it is very necessary to understand this
- Token is passed to WMS: Token is a very important parameter and is involved in the entire process. Here is a separate summary of the Token transmission process involved in this article.
Window Type - //WindowManager.java
- public static class LayoutParams extends ViewGroup.LayoutParams implements Parcelable {
- public static final int FIRST_APPLICATION_WINDOW = 1;
- public static final int LAST_APPLICATION_WINDOW = 99;
-
- public static final int FIRST_SUB_WINDOW = 1000;
- public static final int LAST_SUB_WINDOW = 1999;
-
- public static final int FIRST_SYSTEM_WINDOW = 2000;
- public static final int LAST_SYSTEM_WINDOW = 2999;
- //Status bar
- public static final int TYPE_STATUS_BAR = FIRST_SYSTEM_WINDOW;
- //Search bar
- public static final int TYPE_SEARCH_BAR = FIRST_SYSTEM_WINDOW+1;
- //Caller ID
- public static final int TYPE_PHONE = FIRST_SYSTEM_WINDOW+2;
- //Warning window, common example: low battery warning
- public static final int TYPE_SYSTEM_ALERT = FIRST_SYSTEM_WINDOW+3;
- //Lock screen
- public static final int TYPE_KEYGUARD = FIRST_SYSTEM_WINDOW+4;
- //toast
- public static final int TYPE_TOAST = FIRST_SYSTEM_WINDOW+5;
- public static final int TYPE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW+6; //Display above all windows, covering
- //Incoming calls take priority even when the screen is locked
- public static final int TYPE_PRIORITY_PHONE = FIRST_SYSTEM_WINDOW+7;
- //Input method window
- public static final int TYPE_INPUT_METHOD = FIRST_SYSTEM_WINDOW+11;
- //Wallpaper
- public static final int TYPE_WALLPAPER = FIRST_SYSTEM_WINDOW+13;
- }
- Application window (1 ~ 99): FIRST_APPLICATION_WINDOW ~ LAST_APPLICATION_WINDOW. Corresponding to an Activity, token needs to be set to the Activity's token. For example: Activity.
- Subwindow (1000 ~ 1999): FIRST_SUB_WINDOW ~ LAST_SUB_WINDOW. There must be a parent window, and the token must be set to the parent window's token. For example: PopupWindow, attached to Activity.
- System windows (2000 ~ 2999): FIRST_SYSTEM_WINDOW ~ LAST_SYSTEM_WINDOW. System-level windows do not need to correspond to an Activity or have a parent window. Application processes generally do not have the permission to create them, only system processes can create them. For example, some common system windows are listed above, including the status bar, incoming calls, toast, input method, etc.
Several important categories The following classes are often seen later. Here we mainly look at their direct inheritance relationship, which will be easier to understand later. - public abstract class Window {}
- public class PhoneWindow extends Window implements MenuBuilder.Callback {}
-
- public interface WindowManagerPolicy extends WindowManagerPolicyConstants {}
- public class PhoneWindowManager implements WindowManagerPolicy {}
-
- public interface ViewManager {
- public void addView( View view , ViewGroup.LayoutParams params);
- public void updateViewLayout( View view , ViewGroup.LayoutParams params);
- public void removeView( View view );
- }
- public interface WindowManager extends ViewManager {}
- public final class WindowManagerImpl implements WindowManager {}
-
- /** A window in the window manager. */
- class WindowState extends WindowContainer<WindowState> implements WindowManagerPolicy.WindowState {}
Window is an abstract class. Activity, Toast, Dialog, etc. are all presented by Window. - PhoneWindow is the concrete implementation class of Window (almost the only implementation class).
- WindowManager is an interface that inherits the ViewManager interface (ViewManager defines three operations: add, update, and remove).
- WindowManagerImpl is the implementation class of WindowManager.
However, looking at the three operations of ViewManager in WindowManagerImpl, it can be seen that these three implementations are ultimately completed by WindowManagerGlobal. WindowState maintains all the information of the window. WMS manages the window and saves the state through WindowState. Activity creates a window and adds it to the WMS overview This is the code process of tracking, which is summarized here for easy reference and understanding. The red ones are the main node methods. - //attach()
- -performLaunchActivity()
-
-
-
- //onCreate()
- -setContentView()
-
-
-
-
- //onResume()
- -r.activity.makeVisible()//
-
-
-
-
-
Attach stage: - An Activity creates a PhoneWindow object, and PhoneWindow creates a WindowManagerImpl through setWindowManager().
That is, the Activity corresponds to a PhoneWindow and gets a WindowManager (WindowManagerImpl, created by Window). onCreate stage: - Create a DecorView and add the activity's layout to it.
onResume phase: - ViewRootImpl is created, and finally the session enters the system_server process through setView(). Finally, addWindow is executed to add the window to WMS.
Some structural diagrams in Activity The following is a drawing based on my understanding in the learning summary, which is convenient for you to check at a glance. (If there is anything wrong, thank you for your advice) - public final class WindowManagerImpl implements WindowManager {
- private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
- }
-
- public final class WindowManagerGlobal {
- private static IWindowManager sWindowManagerService; //WMS client,
- private static IWindowSession sWindowSession;//Session
- private final ArrayList< View > mViews = new ArrayList< View >();
- private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
- private final ArrayList<WindowManager.LayoutParams> mParams = new ArrayList<WindowManager.LayoutParams>();
- }
- One Activity corresponds to one PhoneWindow object. That is, each Activity corresponds to one Window (the specific implementation class is PhoneWindow).
- A PhoneWindow holds a DecorView instance. DecorView is actually a FrameLayout, which is the root (top-level View) of all Views in the Activity.
- A PhoneWindow has a WindowManagerImpl. WindowManagerImpl holds a singleton WindowManagerGlobal.
Token is passed to WMS When an Activity is started, AMS will create an ActivityRecord for it. For more information, see: The first startup process of an AMS application. Let's take a look at some codes about token in ActivityRecord: - final class ActivityRecord extends ConfigurationContainer {
- final IApplicationToken.Stub appToken; // window manager token
- // TODO: Remove after unification
- AppWindowToken mAppWindowToken;
-
- ActivityRecord(ActivityTaskManagerService _service, WindowProcessController _caller,...) {
- appToken = new Token(this, _intent);
- }
-
- void createAppWindowToken() {
- mAppWindowToken = createAppWindow(mAtmService.mWindowManager, appToken,...);
- }
-
- static class Token extends IApplicationToken.Stub {
- Token(ActivityRecord activity, Intent intent) {
- weakActivity = new WeakReference<>(activity);
- name = intent.getComponent().flattenToShortString();
- }
- }
- }
The member variable appToken in ActivityRecord is very important and will be involved in many places later. There is an appToken in ActivityRecord, which is an IBinder (the internal class Token inherits the IApplicationToken interface). Token holds a weak reference to the Activity. In ActivityRecord, the AppWindowToken object is created and saved to mAppWindowToken through createAppWindow(). mAppWindowToken: This appToken will be encapsulated in it. Path: ActivityStack.startActivityLocked()->ActivityRecord.createAppWindowToken(). AppWindowToken is a subclass of WindowToken. WindowToken can mark a window. This appToken will be passed to the Activity as a parameter in Activity.attach(). Activity is saved to mToken. Then pass it to Window(PhoneWindow) through Activity.attach()->mWindow.setWindowManager(). Window is saved to mAppToken. WindowManagerGlobal.addView()->Window.adjustLayoutParamsForSubWindow() is saved to the token variable in WindowManager.LayoutParams. Finally, WindowManager.LayoutParams (where token is the appToken in ActivityRecord) is passed as a parameter to ViewRootImpl.setView(). In ViewRootImpl, mWindowAttributes copies WindowManager.LayoutParams and passes it into WMS as a parameter through Session.addToDisplay() for subsequent operations. This is the process in which appToken participates and passes during the entire process of adding a window (to addWindow()). How appToken participates in adding windows can be roughly seen in the comments of "Part 3 2.8: mService.addWindow()", which is quite detailed. Activity window adding process This mainly introduces the creation of the Window corresponding to the Activity and the process of adding the Window to WMS. Activity window creation During the first launch of an AMS application, from clicking the application icon to creating the activity and executing onCreate(). The following is an excerpt from the latter part. If you are not sure, you can refer to the following article. 1.1: handleLaunchActivity() Start here with handleLaunchActivity(). - //ActivityThread.java
- @Override
- public Activity handleLaunchActivity(ActivityClientRecord r,
- PendingTransactionActions pendingActions, Intent customIntent) {
- ...
- WindowManagerGlobal.initialize();
- final Activity a = performLaunchActivity(r, customIntent);
- ...
- }
-
- private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
- ...
- try {
- java.lang.ClassLoader cl = appContext.getClassLoader();
- activity = mInstrumentation.newActivity(
- cl, component.getClassName(), r.intent);
- }
- try {
- Application app = r.packageInfo.makeApplication( false , mInstrumentation);
- if (activity != null ) {
- Window window = null ;
- ...
- //attach(), pay attention to this r.token. Refer to 1.2
- 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, r.configCallback,
- r.assistToken);
- if (r.isPersistable()) {
- //callActivityOnCreate() The onCreate() method of the activity that is finally executed.
- //Refer to 1.4
- mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
- } else {
- mInstrumentation.callActivityOnCreate(activity, r.state);
- }
- }
- }
- ...
- return activity;
- }
WindowManagerGlobal.initialize(); is to obtain the IBinder proxy class of WMS, which is used to communicate with WMS. The code is not listed here. Next we need to look at activity.attach(). Note that the r.token passed as a parameter to attach() is an IBinder from ActivityClientRecord, which simply identifies an Activity. 1.2: activity.attach() - //Activity.java
- final void attach(Context context, ActivityThread aThread,
- Instrumentation instr, IBinder token, ...) {
- ...
- //Create PhoneWindow
- mWindow = new PhoneWindow(this, window, activityConfigCallback);//Create PhoneWindow
- //Set up the soft keyboard
- if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
- mWindow.setSoftInputMode(info.softInputMode);
- }
- //Save token to mToken.
- mToken = token;
- ...
- //mToken is passed to Window, refer to 1.3
- mWindow.setWindowManager(
- (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
- mToken, mComponent.flattenToString(),
- (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
- //mWindowManager is the WindowManagerImpl created in setWindowManager().
- mWindowManager = mWindow.getWindowManager();
- ...
- }
First, create the Window corresponding to Activityd, which is the implementation class of PhoneWindow-Window. Then look at mWindow.setWindowManager(). 1.3: mWindow.setWindowManager() - //Window.java
- public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
- boolean hardwareAccelerated) {
- //ActivityClientRecord.token
- mAppToken = appToken;
- mAppName = appName;
- mHardwareAccelerated = hardwareAccelerated;
- if (wm == null ) {
- wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
- }
- //Created WindowManagerImpl. Note that mParentWindow in WindowManagerImpl is this, not empty
- mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
- }
Here, the WindowManagerImpl object, which is the implementation class of WindowManager, is created, and appToken, appName, and mWindowManager are saved. Through setWindowManager(), WindowManager (WindowManagerImpl) is created for Window (or PhoneWindow). 1.4: setContentView() mInstrumentation.callActivityOnCreate() eventually calls onCreate() of Activity. To customize the Activity and set the layout, setContentView() is executed. Let's take a look at this method directly. - //Activity.java
- public void setContentView(@LayoutRes int layoutResID) {
- getWindow().setContentView(layoutResID);//
- initWindowDecorActionBar();
- }
-
-
- public Window getWindow() {
- return mWindow; //PhoneWindow object
- }
-
- //PhoneWindow.java
- // This is the top - level view of the window, containing the window decor.
- private DecorView mDecor;
- @Override
- public void setContentView( int layoutResID) {
- // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
- // decor, when theme attributes and the like are crystalized. Do not check the feature
- // before this happens.
- if (mContentParent == null ) {
- installDecor();//
- } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
- mContentParent.removeAllViews();
- }
- if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
- final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
- getContext());
- transitionTo(newScene);
- } else {
- mLayoutInflater.inflate(layoutResID, mContentParent);
- }
- ...
- }
-
- private void installDecor() {
- mForceDecorInstall = false ;
- if (mDecor == null ) {
- //Generate DecorView, refer to 1.5
- mDecor = generateDecor(-1);
- ...
- } else {
- mDecor.setWindow(this);
- }
- if (mContentParent == null ) {
- //Add the layout to DecorView, refer to 1.5
- mContentParent = generateLayout(mDecor);
-
- // Set up decor part of UI to ignore fitsSystemWindows if appropriate.
- mDecor.makeOptionalFitsSystemWindows();
-
- final DecorContentParent decorContentParent = (DecorContentParent) mDecor.findViewById(
- R.id.decor_content_parent);
- if (decorContentParent != null ) {
- } else {
- mTitleView = findViewById(R.id.title);
- }
- ...
- }
- }
Here we mainly focus on the following two methods: mDecor = generateDecor(-1); and mContentParent = generateLayout(mDecor);. Let's take a look at their relevant code first: 1.5: generateDecor() and generateLayout() - protected DecorView generateDecor( int featureId) {
- // System process doesn't have application context and in that case we need to directly use
- // the context we have. Otherwise we want the application context, so we don't cling to the
- // activity.
- Context context;
- ...
- return new DecorView(context, featureId, this, getAttributes());//
- }
-
- public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks {
- }
-
- protected ViewGroup generateLayout(DecorView decor) {
- ...
- mDecor.startChanging();
- mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
- ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
- ...
- mDecor.finishChanging();
- return contentParent;
- }
-
- //DecorView.java
- void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
- ...
- mDecorCaptionView = createDecorCaptionView(inflater);
- final View root = inflater.inflate(layoutResource, null );
- if (mDecorCaptionView != null ) {
- if (mDecorCaptionView.getParent() == null ) {
- addView(mDecorCaptionView,
- new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
- }
- mDecorCaptionView.addView(root,
- new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT));
- } else {
-
- // Put it below the color views.
- addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
- }
- mContentRoot = (ViewGroup) root;
- initializeElevation();
- }
A DecorView is created through generateDecor(). DecorView is actually a FrameLayout. Then, through generateLayout(), the activity's layout is finally added to DecorView as a subview (ViewGroup). As can be seen above, from the activity generation to the execution of onCreate(), during this process, the activity generates the associated PhoneWindow, and then creates WindowManagerImpl and DecorView. Let's look at the process of adding a Window to WMS and see how these created objects are connected to form the structural diagram introduced at the beginning. Window added to the WMS process In the first startup process of the AMS application, onCreate is mainly discussed. In fact, onResume is also included. I will not explain it in detail here. I will list the relevant code again: - //ActivityStackSupervisor.java:
- boolean realStartActivityLocked(ActivityRecord r, WindowProcessController proc,
- boolean andResume, boolean checkConfig) throws RemoteException {
- ...
- try {
- ...
- try {
- // Create activity launch transaction .
- final ClientTransaction clientTransaction = ClientTransaction.obtain(
- proc.getThread(), r.appToken);
-
- final DisplayContent dc = r.getDisplay().mDisplayContent;
- clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent),
- ...
- // Set desired final state.
- final ActivityLifecycleItem lifecycleItem;
- if (andResume) {
- lifecycleItem = ResumeActivityItem.obtain(dc.isNextTransitionForward());
- } else {
- lifecycleItem = PauseActivityItem.obtain();
- }
- clientTransaction.setLifecycleStateRequest(lifecycleItem);
-
- // Schedule transaction .
- mService.getLifecycleManager().scheduleTransaction(clientTransaction);
- ...
- }
- }
- ...
- return true ;
- }
The final result of the association with LaunchActivityItem is to create the application's Activity and execute attach() and onCreate(). andResume is true (the parameter passed in is true, you can refer to that code section to see it forward), and the final execution result of the association with ResumeActivityItem is ActivityThread.handleResumeActivity(). Here we look at ActivityThread.handleResumeActivity(). 2.1: ActivityThread.handleResumeActivity() - //ActivityThread.java
- @Override
- public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
- String reason) {
- ...
- // TODO Push resumeArgs into the activity for consideration
- //Execute onStart()->onResume(). Refer to 2.2
- final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
- final Activity a = r.activity;
- if (r.window == null && !a.mFinished && willBeVisible) {
- r.window = r.activity.getWindow();
- View decor = r.window.getDecorView();
- decor.setVisibility( View .INVISIBLE);
- ViewManager wm = a.getWindowManager();
- WindowManager.LayoutParams l = r.window.getAttributes();
- a.mDecor = decor;
- l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
- ...
- }
- ...
- // The window is now visible if it has been added, we are not
- // simply finishing, and we are not starting another activity.
- if (!r.activity.mFinished && willBeVisible && r.activity.mDecor != null && !r.hideForNow) {
- ...
- r.activity.mVisibleFromServer = true ;
- mNumVisibleActivities++;
- if (r.activity.mVisibleFromClient) {
- //Refer to 2.3
- r.activity.makeVisible();
- }
- }
- ...
- }
Note that View decor = r.window.getDecorView(); gets the DecorView object, and finally assigns DecorView to the Activity through a.mDecor = decor;. Here are two of them: - performResumeActivity()
- r.activity.makeVisible();
2.2: performResumeActivity() - //ActivityThread.java
- @VisibleForTesting
- public ActivityClientRecord performResumeActivity(IBinder token, boolean finalStateRequest,
- String reason) {
- final ActivityClientRecord r = mActivities.get(token);
- ...
- try {
- r.activity.onStateNotSaved();
- r.activity.mFragments.noteStateNotSaved();
- ...
- r.activity.performResume(r.startsNotResumed, reason);
-
- r.state = null ;
- r.persistentState = null ;
- r.setState(ON_RESUME);
-
- reportTopResumedActivityChanged(r, r.isTopResumedActivity, "topWhenResuming" );
- }
- return r;
- }
-
- //Activity.java
- final void performResume(boolean followedByPause, String reason) {
- performRestart( true /* start */, reason);
- mInstrumentation.callActivityOnResume(this);
- }
-
- final void performRestart(boolean start, String reason) {
- mInstrumentation.callActivityOnRestart(this);
- }
-
- //Instrumentation.java
- public void callActivityOnRestart(Activity activity) {
- activity.onRestart();
- }
-
- public void callActivityOnResume(Activity activity) {
- activity.mResumed = true ;
- activity.onResume();
- ...
- }
r.activity.performResume() in performResumeActivity() calls back the performResume() method of Activity. Finally, the onResume() method of Activity is executed. Before performing onResume(), performRestart() is called, which ultimately calls the activity's onStart(). It can be seen here that onStart() is executed before onResume(). 2.3: r.activity.makeVisible() - //Activity.java
- void makeVisible() {
- if (!mWindowAdded) {
- ViewManager wm = getWindowManager();
- wm.addView(mDecor, getWindow().getAttributes());
- mWindowAdded = true ;
- }
- mDecor.setVisibility( View .VISIBLE);
- }
-
- //WindowManagerImpl.java
- public final class WindowManagerImpl implements WindowManager {
- @UnsupportedAppUsage
- private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
- @Override
- public void addView(@NonNull View view , @NonNull ViewGroup.LayoutParams params) {
- applyDefaultToken(params);
- //mParentWindow is the window manager that creates WindowManagerImpl. Refer to 2.4
- mGlobal.addView( view , params, mContext.getDisplay(), mParentWindow);
- }
- }
The getWindowManager() here gets the WindowManagerImpl object created by setWindowManager() during attach() mentioned earlier. As mentioned earlier, the three operations such as addView() are defined and implemented in WindowManagerGlobal, which can be seen here. 2.4: WindowManagerGlobal.addView() - //WindowManagerGlobal
- @UnsupportedAppUsage
- private final ArrayList< View > mViews = new ArrayList< View >();
- @UnsupportedAppUsage
- private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
- public void addView( View view , ViewGroup.LayoutParams params,
- Display display, Window parentWindow) {
- ...
- final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
- if (parentWindow != null ) {
- //Adjust the Window parameters. This process sets the token.
- //Refer to 2.4.1
- parentWindow.adjustLayoutParamsForSubWindow(wparams);
- }
- ...
- ViewRootImpl root;
- View panelParentView = null ;
- synchronized (mLock) {
- ...
- root = new ViewRootImpl( view .getContext(), display);
- view .setLayoutParams(wparams);
- mViews.add ( view );
- mRoots.add (root);
- mParams.add (wparams);
- try {
- //Set the view and related parameters to ViewRootImpl. ViewRootImpl will add a new window to WMS, apply for Surface and do drawing work, etc.
- //Refer to 2.6
- root.setView( view , wparams, panelParentView);
- }
- ...
- }
- }
-
- //ViewRootImpl.java
- @UnsupportedAppUsage
- final IWindowSession mWindowSession;
- public ViewRootImpl(Context context, Display display) {
- mContext = context;
- // Created Session(), refer to 2.5
- mWindowSession = WindowManagerGlobal.getWindowSession();
- mDisplay = display;
- mBasePackageName = context.getBasePackageName();
- mThread = Thread.currentThread();
- mTargetSdkVersion = context.getApplicationInfo().targetSdkVersion;
- //The mWindow here is not the PhoneWindow in the previous Activity, it is W extends IWindow.Stub
- mWindow = new W(this);
- mViewVisibility = View .GONE;
- //Create AttachInfo
- mAttachInfo = new View .AttachInfo(mWindowSession, mWindow, display, this, mHandler, this,
- context);
- ...
- }
-
- static class W extends IWindow.Stub {...}
-
- //ViewRootImpl.java
- public final class ViewRootImpl implements ViewParent,
-
- // View .java
- final static class AttachInfo {
- AttachInfo(IWindowSession session, IWindow window, Display display,
- ViewRootImpl viewRootImpl, Handler handler, Callbacks effectPlayer,
- Context context) {
- mSession = session;
- mWindow = window;
- mWindowToken = window.asBinder();
- mDisplay = display;
- mViewRootImpl = viewRootImpl;
- mHandler = handler;
- mRootCallbacks = effectPlayer;
- mTreeObserver = new ViewTreeObserver(context);
- }
- }
Here we can see that the ViewRootImpl object is created. This class implements the necessary protocol between View and WindowManager. Note that mWindow = new W(this); in the creation, this W inherits IWindow.Stub. When creating a ViewRootImpl object, a mAttachInfo = View.AttachInfo() is created. AttachInfo is a series of binding information. mWindowSession and mWindow are passed in as parameters. When creating AttachInfo, note that mWindowToken = window.asBinder(); - mWindowSession is described in subsequent 2.5/2.6/2.7. It is a Session object, a proxy class of IWindowSession, through which the binder interface can communicate with WMS.
- mWindow is the W object, which is IWindow.Stub, created by new. You can see that it will be passed to WMS later. It is the binder interface for WMS to call back the application (communicate with the application).
- mWindowToken, which is the IBinder object of W, is also the interface for WMS to communicate with applications.
After creating the ViewRootImpl object, WindowManagerGlobal saves View, ViewRootImpl, and LayoutParams into the corresponding ArrayList. As mentioned earlier, WindowManagerGlobal is a singleton, and there is only one in the application process. Finally, View (here is DecorView) is passed to ViewRootImpl through root.setView(). 2.4.1: adjustLayoutParamsForSubWindow() As we saw earlier, mAppToken is passed in from Activity. Here mAppToken is set to WindowManager.LayoutParams, and later we can see that it is finally passed to WMS for processing. - //Window.java
- void adjustLayoutParamsForSubWindow(WindowManager.LayoutParams wp) {
- CharSequence curTitle = wp.getTitle();
- //Subwindow, this article is the application window, so I won’t go through this, but I’ll also learn about it.
- 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();
- }
- }
- ...
- //System window, don't go this way either
- } else if (wp.type >= WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW &&
- wp.type <= WindowManager.LayoutParams.LAST_SYSTEM_WINDOW) {
- ...
- //Application window, this article goes here
- } else {
- if (wp.token == null ) {
- //Set to WindowManager.LayoutParams
- wp.token = mContainer == null ? mAppToken : mContainer.mAppToken;
- }
- ...
- }
- ...
- }
2.4.2: Learn more about AttachInfo ViewRootImpl and each View. Through the following process, the AttachInfo binding information is set to each View, that is, each View can obtain various related information. 2.6 After executing ViewRootImpl.setView(), refer to the process: setView()->requestLayout()->scheduleTraversals()->mTraversalRunnable->doTraversal()->performTraversals()->host.dispatchAttachedToWindow(mAttachInfo, 0)->View.dispatchAttachedToWindow()->ViewGroup.dispatchAttachedToWindow(). The AttachInfo belonging to the same ViewGroup is the same. - //ViewGroup.java
- @Override
- @UnsupportedAppUsage
- void dispatchAttachedToWindow(AttachInfo info, int visibility) {
- mGroupFlags |= FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW;
- super.dispatchAttachedToWindow(info, visibility);
- mGroupFlags &= ~FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW;
-
- final int count = mChildrenCount;
- final View [] children = mChildren;
- for ( int i = 0; i < count ; i++) {
- final View child = children[i];
- child.dispatchAttachedToWindow(info,
- combineVisibility(visibility, child.getVisibility()));
- }
- final int transientCount = mTransientIndices == null ? 0 : mTransientIndices. size ();
- for ( int i = 0; i < transientCount; ++i) {
- View view = mTransientViews.get(i);
- view .dispatchAttachedToWindow(info,
- combineVisibility(visibility, view .getVisibility()));
- }
- }
- }
The above process performTraversals() has a general understanding: traverse the view tree from top to bottom, each View draws itself, and ViewGroup notifies the child View to draw. Measure performMeasure() Perform layout performLayout() Draw performDraw(). The important part of Android drawing is here. If you need to understand it, you can study this method (performTraversals()) carefully. I will not pay attention to it here. 2.5: WindowManagerGlobal.getWindowSession() - // WindowManagerGlobal.java
- @UnsupportedAppUsage
- public static IWindowSession getWindowSession() {
- synchronized (WindowManagerGlobal.class) {
- if (sWindowSession == null ) {
- try {
- InputMethodManager.ensureDefaultInstanceForDefaultDisplayIfNecessary();
- IWindowManager windowManager = getWindowManagerService();
- //Create a Session object
- sWindowSession = windowManager.openSession(
- new IWindowSessionCallback.Stub() {
- @Override
- public void onAnimatorScaleChanged( float scale) {
- ValueAnimator.setDurationScale(scale);
- }
- });
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
- return sWindowSession;
- }
- }
-
- //WindowManagerService.java
- @Override
- public IWindowSession openSession(IWindowSessionCallback callback) {
- return new Session(this, callback);
- }
Get the Sessiond object, if not, create it through windowManager.openSession(). Session is the proxy class of IWindowSession, and then return it to mWindowSession in ViewRootImpl. 2.6: ViewRootImpl.setView() - //ViewRootImpl.java
- public void setView( View view , WindowManager.LayoutParams attrs, View panelParentView) {
- synchronized (this) {
- if (mView == null ) {
- mView = view ;
- mWindowAttributes.copyFrom(attrs);
- ...
- // Schedule the first layout -before- adding to the window
- // manager, to make sure we do the relayout before receiving
- // any other events from the system.
- requestLayout();//TODO
- try {
- mOrigWindowType = mWindowAttributes.type;
- mAttachInfo.mRecomputeGlobalAttributes = true ;
- collectViewAttributes();
- //Refer to 2.7, enter the system_server process
- res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
- getHostVisibility(), mDisplay.getDisplayId(), mTmpFrame,
- mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
- mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel,
- mTempInsets);
- setFrame(mTmpFrame);
- }
- ...
- }
- }
- }
res = mWindowSession.addToDisplay(): mWindowSession is the created Session returned above, mWindowSession.addToDisplay() enters the system_server process through binder and executes Session.addToDisplay(). mView is DecorView. The mWindow here is mentioned in 2.4, which is W inherited from IWindow.Stub. This is an IBinder object, which is created when the application process creates ViewRootImpl. Here mWindowSession.addToDisplay() can be seen being passed to WMS. 2.7: Session.addToDisplay() - //Session.java
- class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
- final WindowManagerService mService;
- public Session(WindowManagerService service, IWindowSessionCallback callback) {
- mService = service;
- ...
- }
-
- @Override
- public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
- int viewVisibility, int displayId, Rect outFrame, Rect outContentInsets,
- Rect outStableInsets, Rect outOutsets,
- DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
- InsetsState outInsetsState) {
- //Refer to 2.8
- return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outFrame,
- outContentInsets, outStableInsets, outOutsets, outDisplayCutout, outInputChannel,
- outInsetsState);
- }
- }
Enter WMS and add Window. 2.8: mService.addWindow() Finally, we come to WMS.addWindow(), where we complete the window addition. You can carefully look at the source code and comments below. Even though this method has been shortened a lot, it is still quite long and requires patience. - //WindowManagerService.java
- public int addWindow(Session session, IWindow client, int seq,
- LayoutParams attrs, int viewVisibility, int displayId, Rect outFrame,
- Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
- DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
- InsetsState outInsetsState) {
- int [] appOp = new int [1];
- // Check permissions, no permission can not add window
- int res = mPolicy.checkAddPermission(attrs, appOp);
- if (res != WindowManagerGlobal.ADD_OKAY) {
- return res;
- }
-
- boolean reportNewConfig = false ;
- WindowState parentWindow = null ;
- ...
- final int type = attrs.type;
- synchronized (mGlobalLock) {
- ...
- //Get the DisplayContent to which the window is to be added. That is, on which screen it is displayed
- final DisplayContent displayContent = getDisplayContentOrCreate(displayId, attrs.token);
-
- if (displayContent == null ) {
- Slog.w(TAG_WM, "Attempted to add window to a display that does not exist: "
- + displayId + ". Aborting." );
- return WindowManagerGlobal.ADD_INVALID_DISPLAY;
- }
- if (!displayContent.hasAccess(session.mUid)) {
- Slog.w(TAG_WM, "Attempted to add window to a display for which the application "
- + "does not have access: " + displayId + ". Aborting." );
- return WindowManagerGlobal.ADD_INVALID_DISPLAY;
- }
-
- if (mWindowMap.containsKey(client.asBinder())) {
- Slog.w(TAG_WM, "Window " + client + " is already added" );
- return WindowManagerGlobal.ADD_DUPLICATE_ADD;
- }
- //To add a child window, the parent window must exist.
- if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) {
- parentWindow = windowForClientLocked( null , attrs.token, false );
- if (parentWindow == null ) {
- Slog.w(TAG_WM, "Attempted to add window with token that is not a window: "
- + attrs.token + ". Aborting." );
- return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
- }
- //Here we can see that WMS requires the window level to be at most two layers
- if (parentWindow.mAttrs.type >= FIRST_SUB_WINDOW
- && parentWindow.mAttrs.type <= LAST_SUB_WINDOW) {
- Slog.w(TAG_WM, "Attempted to add window with token that is a sub-window: "
- + attrs.token + ". Aborting." );
- return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
- }
- }
- if (type == TYPE_PRIVATE_PRESENTATION && !displayContent.isPrivate()) {
- Slog.w(TAG_WM, "Attempted to add private presentation window to a non-private display. Aborting." );
- return WindowManagerGlobal.ADD_PERMISSION_DENIED;
- }
-
- AppWindowToken atoken = null ;
- final boolean hasParent = parentWindow != null ;
- //Get WindowToken and use the parent window's token for the child window.
- //Take it out from mTokenMap through attrs.token: private final HashMap<IBinder, WindowToken> mTokenMap = new HashMap();
- //About Activity window: WindowToken. As mentioned earlier, AppWindowToken is created when ActivityRecord is created. During this process, appToken and AppWindowToken are saved in mTokenMap.
- WindowToken token = displayContent.getWindowToken(
- hasParent ? parentWindow.mAttrs.token : attrs.token);
-
- // If this is a child window, we want to apply the same type checking rules as the
- // parent window type.
- final int rootType = hasParent ? parentWindow.mAttrs.type : type;
- boolean addToastWindowRequiresToken = false ;
- //The following is the relationship between WindowToken and window
- if (token == null ) {
- //For the following window types, WindowToken cannot be empty
- if (rootType >= FIRST_APPLICATION_WINDOW && rootType <= LAST_APPLICATION_WINDOW) {
- Slog.w(TAG_WM, "Attempted to add application window with unknown token "
- + attrs.token + ". Aborting." );
- return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
- }
- if (rootType == TYPE_INPUT_METHOD) {
- Slog.w(TAG_WM, "Attempted to add input method window with unknown token "
- + attrs.token + ". Aborting." );
- return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
- }
- if (rootType == TYPE_VOICE_INTERACTION) {
- Slog.w(TAG_WM, "Attempted to add voice interaction window with unknown token "
- + attrs.token + ". Aborting." );
- return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
- }
- if (rootType == TYPE_WALLPAPER) {
- Slog.w(TAG_WM, "Attempted to add wallpaper window with unknown token "
- + attrs.token + ". Aborting." );
- return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
- }
- if (rootType == TYPE_DREAM) {
- Slog.w(TAG_WM, "Attempted to add Dream window with unknown token "
- + attrs.token + ". Aborting." );
- return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
- }
- if (rootType == TYPE_QS_DIALOG) {
- Slog.w(TAG_WM, "Attempted to add QS dialog window with unknown token "
- + attrs.token + ". Aborting." );
- return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
- }
- if (rootType == TYPE_ACCESSIBILITY_OVERLAY) {
- Slog.w(TAG_WM, "Attempted to add Accessibility overlay window with unknown token "
- + attrs.token + ". Aborting." );
- return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
- }
- if (type == TYPE_TOAST) {
- // Apps targeting SDK above N MR1 cannot arbitrary add toast windows.
- if (doesAddToastWindowRequireToken(attrs.packageName, callingUid,
- parentWindow)) {
- Slog.w(TAG_WM, "Attempted to add a toast window with unknown token "
- + attrs.token + ". Aborting." );
- return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
- }
- }
- final IBinder binder = attrs.token != null ? attrs.token : client.asBinder();
- final boolean isRoundedCornerOverlay =
- (attrs.privateFlags & PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) != 0;
- //Token is empty, except for the above window type, the others are allowed. Create a new WindowToken at this time
- token = new WindowToken(this, binder, type, false , displayContent,
- session.mCanAddInternalSystemWindow, isRoundedCornerOverlay);
- //Token is not empty, and it is the application window type. AppWindowToken type is when token is needed.
- } else if (rootType >= FIRST_APPLICATION_WINDOW && rootType <= LAST_APPLICATION_WINDOW) {
- //Judge whether token is AppWindowToken type
- //I know earlier that Activity creates AppWindowToken, that is, the obtained atoken is not empty
- atoken = token.asAppWindowToken();
- if (atoken == null ) {
- Slog.w(TAG_WM, "Attempted to add window with non-application token "
- + token + ". Aborting." );
- return WindowManagerGlobal.ADD_NOT_APP_TOKEN;
- } else if (atoken.removed) {
- Slog.w(TAG_WM, "Attempted to add window with exiting application token "
- + token + ". Aborting." );
- return WindowManagerGlobal.ADD_APP_EXITING;
- } else if (type == TYPE_APPLICATION_STARTING && atoken.startingWindow != null ) {
- Slog.w(TAG_WM, "Attempted to add starting window to token with already existing"
- + " starting window" );
- return WindowManagerGlobal.ADD_DUPLICATE_ADD;
- }
- //All other window types handle
- } else if (rootType == TYPE_INPUT_METHOD) {
- ...
- }...
- //WindowState maintains the information of all windows, it is the "window" actually managed by WMS
- //It is closely related to Z- Order (multiple Window Cascading Layout). The larger the mLayer, the higher the window.
- final WindowState win = new WindowState(this, session, client, token, parentWindow,
- appOp[0], seq, attrs, viewVisibility, session.mUid,
- session.mCanAddInternalSystemWindow);
- if (win.mDeathRecipient == null ) {
- // Client has apparently died, so there is no reason to
- // continue .
- Slog.w(TAG_WM, "Adding window client " + client.asBinder()
- + " that is dead, aborting." );
- return WindowManagerGlobal.ADD_APP_EXITING;
- }
- ...
- origId = Binder.clearCallingIdentity();
- //Create a SurfaceSession to communicate with SurfaceFlinger. Refer to 2.9 for a brief description
- win.attach();
- //Add WindowState object to mWindowMap
- mWindowMap.put(client.asBinder(), win);
- win.initAppOpsState();
- ...
- final AppWindowToken aToken = token.asAppWindowToken();
- win.mToken.addWindow(win);
- win.applyAdjustForImeIfNeeded();
- ...
- }
- ...
- return res;
- }
WMS manages windows, saves status, etc. through WindowState. When adding windows, you need to specify its WindowToken; at the same time, the windows need to specify its DisplayContent to determine which screen device to be displayed to. For details, please see the comments above, which are more detailed. After reading it, you can roughly understand the window type and the role of WindowToken in window addition. Understand the role of token. For example: If a child window is added, it must have a parent window, and the window level is at most two layers. WindowToken is null, so it can be clearly seen that those situations are not allowed to be added. When applying windows when adding windows, if the WindowToken is AppWindowToken. etc. addWindow() will talk about this for the time being. The result after this addition was finally returned to 2.6 through res: ViewRootImpl.setView() and continues to be processed according to the result. 2.9: win.attach() Why does win.attach() create communication with SurfaceFlinger? Let's take a brief look. Tracking continues to create a SurfaceSession object, which enters native, and ultimately creates a SurfaceComposerClient that communicates with the SurfaceFlinger. Therefore, it is possible to communicate with the SurfaceFlinger. The mSurface created when ViewRootImpl is created. mSurface is a member variable of ViewRootImpl. The Surface created at this time has nothing. Finally, enter WMS through relayoutWindow() and send a request to SurfaceFlinger step by step. Several related codes are available at this time. - //WindowState.java
- void attach() {
- if (localLOGV) Slog.v(TAG, "Attaching " + this + " token=" + mToken);
- mSession.windowAddedLocked(mAttrs.packageName);
- }
-
- //Session.java
- void windowAddedLocked(String packageName) {
- mPackageName = packageName;
- mRelayoutTag = "relayoutWindow: " + mPackageName;
- if (mSurfaceSession == null ) {
- mSurfaceSession = new SurfaceSession();
- ...
- }
- mNumWindow++;
- }
-
- //ViewRootImpl.java
- // These can be accessed by any thread, must be protected with a lock.
- // Surface can never be reassigned or cleared (use Surface.clear()).
- @UnsupportedAppUsage
- public final Surface mSurface = new Surface();
- private final SurfaceControl mSurfaceControl = new SurfaceControl();
Here we will only talk about adding windows to WMS, and we will summarize them later on the processing after window addition, the calculation and display update of interfaces, etc. |