The article has been authorized “Hongyang” public account published

preface

Hello! I am an immortal ape, welcome to read my article.

Windows, which readers may recognize more as Windows system. On Windows, we can run multiple Windows at the same time, each window representing an application. Android doesn’t seem to have such a thing, but the reader can immediately think, isn’t there a small window mode, like mi UI’s latest system, isn’t it possible to create a small window at will, and then two applications operate at the same time? Yeah, that’s a representation of Windows in Android. However, the mobile phone screen cannot be compared with the computer after all, because the screen is too small to operate only one application, multiple Windows are very unaccustomed to, so Android readers may not have much knowledge about Windows. So window just means the small window in xiaomi system?

Of course not. Windows in the Android framework is a little different from Windows as we know it. Our daily most intuitive, every application interface, there is an application level window. For example, popupWindow, Toast, Dialog and Menu are all implemented by creating Windows. So we actually see Window all the time, we just didn’t know it was Window. By understanding the mechanics of Windows, you can better understand Windows and, in turn, how Android manages views on the screen. This way, when we need to use dialog or popupWindow, we can understand what is behind it, so that we can better use dialog, popupWindow, etc.

Of course, if you have a lot of questions, or even doubt my theory, then I hope you can finish reading this article. I will explain Windows in Android from what it is, what it does, what the internal mechanism is, how various components create Windows and so on. The article is very informative, and readers can choose their own chapters to read.

What is the Window mechanism

Imagine what would happen if there were no Windows:

The UI we see is a View, like our application layout, or even simpler, a button. Suppose you now have a Button on the screen, as shown in Figure 1, and now add a TextView to the middle of the screen, the final result will be figure 2 or figure 3:

In Figure 2, what if I wanted to click on a textView to perform its listener event logic and click on a region that is not a textView to make the textView disappear? Readers might say, we can add this logic to our activities, but what if we need one hover window to display on all screens, such as the mi hover window I mentioned above, two views without application, how to determine their display order? For example, we need to popup a dialog to remind the user, how can we keep the dialog at the top level forever? Including the application popupWindow must be displayed at the bottom of the dialog, but toast must be displayed at the top of the dialog.

Obviously, our screen can allow multiple applications to display a large number of views at the same time, and their display order or display height is not the same. If there is no unified manager, then each application will want to display the view at the top, and the view on the screen will be very messy.

Also, when we click on the screen, which view should the touch event be passed to? Obviously we all know we should pass it to the top view, but it’s the screen that receives the event, it’s another system service, how does it know which view is at the top of the touch position? Even if he knew, how could he transmit the event accurately to him?

In order to solve these problems, there is a need to have a manager to manage the view displayed on the screen, so that the application can run smoothly. This is the Window mechanism in Android.

The Window mechanism is designed to manage the display of views on the screen and the delivery of touch events.

What is the window?

So what is a window? In Android’s Window mechanism, every view tree can be considered a window. Why not every view? Because the display order of each view in the view tree is fixed, for example, in our Activity layout, the display of each control is already arranged. For the Window mechanism, it belongs to the “indivisible view”.

What is a View tree? For example, if you set a layout XML for your Activity in the layout, then the top-level layout, such as LinearLayout, is the root of the view tree. All the views it contains are nodes of the view tree, so the view tree corresponds to a window.

A few specific examples:

  • When we add dialog, we need to set a view for it, so this view is not part of the Antivity layout, it is added to the screen through Windows Manager, it is not part of the Activity view tree, so this dialog is an independent view tree. So he is a window.
  • PopupWindow also corresponds to a Window because it is also added via windowManager and is not part of the Activity’s View tree.
  • When we use Windows Manager, any view added to the screen does not belong to the Activity’s layout view tree, even if only a button is added.

View tree is the operation unit of window mechanism. Every view corresponds to a window. View is the existence form of Window and window is the carrier of view. The application interfaces, dialogs, popupWindows, and the hover Windows described above are all window representations. Notice that instead of seeing a Window, we’re seeing a View. ** Window is the view manager and the view carrier. It’s an abstract concept that doesn’t exist in and of itself. View is a representation of window. ** Here does not exist, which means that we do not see Windows on the screen, unlike Windows, as shown in the following picture:

There is a clear sign: look, I am Window. But in Android we can’t sense it, we can only see the View and we can’t see the Window, and the window is the manager that controls how the view needs to be displayed. Behind every successful man, there is a woman. Behind every view, there is a window.

Window doesn’t exist, it’s just a concept. For example: such as class collective, is a concept, his form of existence is the students of the whole class, when the students do not exist so the class collective does not exist. But his benefit is to get a new concept, we can arrange activities by class as a unit. Because he does not exist, so it is difficult to find his traces in the source code, the operation unit of the Window mechanism is view, if it is to say that he exists in the form of source code, the author’s current cognition is in WindowManagerService each view corresponds to a windowStatus. What Windows ManagerService is you can ignore it if you haven’t already and we’ll talk about it later. The reader can take a moment to think about this abstract concept, and will slowly go into the source code to help understand.

  • View is the window form, window is the carrier of view
  • Window is the manager of the view and the carrier of the view. It’s an abstract concept that doesn’t exist in and of itself. View is a representation of window

Thinking: Android has an abstract class called Window and a PhoneWindow implementation class, they are not the existence of Window, why say window is abstract does not exist? You can think about that for yourself, and we’ll talk about that later.

Properties associated with the Window

Before we look at the window operation process, let’s add the properties of window.

The Window’s Type property

One of the problems solved by the Window mechanism is the order in which views are displayed. This property determines the order in which Windows are displayed. Window is classified, and different categories display different heights. For example, 1-1000m height is called low altitude, 1001-2000m height is called medium altitude, and above 2000 height is called high altitude. Windows are also classified by height range. It also has a variable, Z-order, which determines the height of the window. Windows can be divided into three categories:

  • Application Window: The application window is usually located at the bottom, with z-order in 1-99
  • Child Windows: Child Windows are generally displayed above the application window, z-order in 1000-1999
  • System-level Windows: System-level Windows are usually located at the top layer and will not be covered by other Windows, such as Toast and Z-Order in 2000-2999. If you want to play custom system-level Windows, you need to apply for permissions dynamically.

The larger the z-order is, the closer the window is to the user, and the higher the display. A window with a higher height overwrites a window with a lower height.

The type attribute of the window is the z-order value. We can assign a value to the type attribute of the window to determine the height of the window. The system presets static constants for all three types of Windows as follows:

  • Application level window

    // Start value of application Window
    public static final int FIRST_APPLICATION_WINDOW = 1;
    
    // The base value of the application Window
    public static final int TYPE_BASE_APPLICATION = 1;
    
    // Common applications
    public static final int TYPE_APPLICATION = 2;
    
    // Special application Window that is used to display something before the application can display Window
    public static final int TYPE_APPLICATION_STARTING = 3;
    
    // A variant of TYPE_APPLICATION in which WindowManager waits for the Window to be drawn before the application is displayed
    public static final int TYPE_DRAWN_APPLICATION = 4;
    
    // The end value of the application Window
    public static final int LAST_APPLICATION_WINDOW = 99;
    Copy the code
  • The child window

    // The start value of the child Window type
    public static final int FIRST_SUB_WINDOW = 1000;
    
    // The panel on top of the application Window. These Windows appear at the top of their attached Windows.
    public static final int TYPE_APPLICATION_PANEL = FIRST_SUB_WINDOW;
    
    // Window used to display media such as video. These Windows appear after their attached Windows.
    public static final int TYPE_APPLICATION_MEDIA = FIRST_SUB_WINDOW + 1;
    
    // Subpanel at the top of the application Window. These Windows appear at the top of their attached Windows and any Windows
    public static final int TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW + 2;
    
    // While the current Window layout is the same as the top-level Window layout, it cannot be used as a child container
    public static final int TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW + 3;
    
    // Overwrite the top Window with the display media Window, which is the system's hidden API
    public static final int TYPE_APPLICATION_MEDIA_OVERLAY  = FIRST_SUB_WINDOW + 4;
    
    // The subpanel is at the top of the application Window, which is displayed at the top of its attached Window, which is the system's hidden API
    public static final int TYPE_APPLICATION_ABOVE_SUB_PANEL = FIRST_SUB_WINDOW + 5;
    
    // The end value of the child Window type
    public static final int LAST_SUB_WINDOW = 1999;
    Copy the code
  • System level window

    // Start value of system Window type
    public static final int FIRST_SYSTEM_WINDOW = 2000;
    
    // System status bar, there can be only one status bar, it is placed at the top of the screen, all other Windows move down
    public static final int TYPE_STATUS_BAR = FIRST_SYSTEM_WINDOW;
    
    The system search window can have only one search bar, which is placed at the top of the screen
    public static final int TYPE_SEARCH_BAR = FIRST_SYSTEM_WINDOW+1;
    
    // Has been removed from the system and can be replaced with TYPE_KEYGUARD_DIALOG
    public static final int TYPE_KEYGUARD = FIRST_SYSTEM_WINDOW+4;
    
    // System dialog box window
    public static final int TYPE_SYSTEM_DIALOG = FIRST_SYSTEM_WINDOW+8;
    
    // A dialog box is displayed when the screen is locked
    public static final int TYPE_KEYGUARD_DIALOG = FIRST_SYSTEM_WINDOW+9;
    
    // The input window, which sits on top of the normal UI, can be rearranged to avoid being overlaid by this window
    public static final int TYPE_INPUT_METHOD = FIRST_SYSTEM_WINDOW+11;
    
    // Input method dialog box, displayed above the current input method window
    public static final int TYPE_INPUT_METHOD_DIALOG= FIRST_SYSTEM_WINDOW+12;
    
    / / wallpaper
    public static final int TYPE_WALLPAPER = FIRST_SYSTEM_WINDOW+13;
    
    // Sliding panel for status bar
    public static final int TYPE_STATUS_BAR_PANEL = FIRST_SYSTEM_WINDOW+14;
    
    // The application overlay window is displayed on top of all Windows
    public static final int TYPE_APPLICATION_OVERLAY = FIRST_SYSTEM_WINDOW + 38;
    
    // End value of system Window type
    public static final int LAST_SYSTEM_WINDOW = 2999;
    Copy the code

The flags parameter for Window

There are a lot of things that use the flag to control Windows. Many materials describe it as “control the display of Windows”, but I think it is not accurate enough. The scope of flag control includes: display logic in various situations (lock screen, game, etc.) and handling logic of touch events. Controlling the display is a big part of what it does, but not all of it. Let’s take a look at some common flags to see what flag does. (The following common parameters are introduced from the first article in the references.)

// Lock the screen while Window is visible
public static final int FLAG_ALLOW_LOCK_WHILE_SCREEN_ON = 0x00000001;

// Everything behind Window goes dark
public static final int FLAG_DIM_BEHIND = 0x00000002;

// The Window does not receive input focus, that is, it does not accept any keystrokes or button events. For example, the Window has an EditView. Clicking EditView does not bring up the soft keyboard
// Events outside the scope of the Window are still handled by the original Window; For example, if you click on the view outside the window, you will still get a response. In addition, whenever this Flag is set, FLAG_NOT_TOUCH_MODAL will be enabled
public static final int FLAG_NOT_FOCUSABLE = 0x00000008;

// Set this Flag to send keystroke events outside the Window to the next Window, and only touch events inside the Window itself
// Views outside the Window can also respond to touch events.
public static final int FLAG_NOT_TOUCH_MODAL  = 0x00000020;

// If this Flag is set, the Window will not receive any touch events. For example, clicking on the Window will not receive a response, but only the Window with focus below.
public static final int FLAG_NOT_TOUCHABLE      = 0x00000010;

// The screen stays on as long as the Window is visible
public static final int FLAG_KEEP_SCREEN_ON     = 0x00000080;

// Allow Window to fill the entire screen
public static final int FLAG_LAYOUT_IN_SCREEN   = 0x00000100;

// Allow Windows to go beyond the screen
public static final int FLAG_LAYOUT_NO_LIMITS   = 0x00000200;

// Full screen display to hide all Window decorations, such as full screen display in games and players
public static final int FLAG_FULLSCREEN      = 0x00000400;

// FLAG_FULLSCREEN is one level lower than FLAG_FULLSCREEN and displays the status bar
public static final int FLAG_FORCE_NOT_FULLSCREEN   = 0x00000800;

// When the user's face is close to the screen (such as making a phone call), this event is not responded to
public static final int FLAG_IGNORE_CHEEK_PRESSES    = 0x00008000;

// A motionEvent.action_outside event is received when the keystroke action occurs outside the Window.
public static final int FLAG_WATCH_OUTSIDE_TOUCH = 0x00040000;

@Deprecated
// The Window can be displayed above the Window on the lock screen, using the active #setShowWhenLocked(Boolean) method instead
public static final int FLAG_SHOW_WHEN_LOCKED = 0x00080000;

// Is responsible for drawing the system bar background. If set, the system bar will be drawn on a transparent background,
// The corresponding area in this Window will be filled with the color specified in Windows # getStatusBarColor () and Windows # getNavigationBarColor ().
public static final int FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS = 0x80000000;

The Window surface must be translucent in order to see the wallpaper behind it
public static final int FLAG_SHOW_WALLPAPER = 0x00100000;
Copy the code

The solfInputMode property of the window

This part is the processing logic of window when the software disk pops up, which is often encountered in daily life. For example, when we chat in wechat, click the input box, and the input box will be pushed up when the soft keyboard pops up. If you don’t want to be topped up, you can also set it to be covered by the soft keyboard. Here are some common attributes (from the first article in the bibliography) :

// If no state is specified, the system selects an appropriate state or subject-dependent configuration
public static final int SOFT_INPUT_STATE_UNCHANGED = 1;

// Hide the soft keyboard when the user enters the window
public static final int SOFT_INPUT_STATE_HIDDEN = 2;

// Hide the soft keyboard when the window gets focus
public static final int SOFT_INPUT_STATE_ALWAYS_HIDDEN = 3;

// When the user enters the window, the soft keyboard is displayed
public static final int SOFT_INPUT_STATE_VISIBLE = 4;

// Displays a soft keyboard when the window gets focus
public static final int SOFT_INPUT_STATE_ALWAYS_VISIBLE = 5;

// The window is resized to fit the soft keyboard window
public static final int SOFT_INPUT_MASK_ADJUST = 0xf0;

// If no state is specified, the system selects an appropriate state or subject-dependent setting
public static final int SOFT_INPUT_ADJUST_UNSPECIFIED = 0x00;

// When the soft keyboard pops up, the window will be resized, such as clicking an EditView, and the entire layout will be visible and placed above the software tray
This mode can't be used in conjunction with SOFT_INPUT_ADJUST_PAN.
// If the window's layout parameter flag contains FLAG_FULLSCREEN, this value is ignored and the window does not resize, but remains full-screen.
public static final int SOFT_INPUT_ADJUST_RESIZE = 0x10;

// When the soft keyboard pops up, the window does not need to be resized, make sure the input focus is visible,
For example, if you have two EditView input boxes, one is Ev1 and the other is Ev2, when you click Ev1 to enter data, the current Ev1 input box will move to the top of the soft keyboard
// This mode cannot be used in conjunction with SOFT_INPUT_ADJUST_RESIZE
public static final int SOFT_INPUT_ADJUST_PAN = 0x20;

// The window will not be resized and will be overwritten directly
public static final int SOFT_INPUT_ADJUST_NOTHING = 0x30;
Copy the code

Other properties of window

The above three attributes are window’s more important and complex three, in addition to several commonly used attributes:

  • X and Y properties: Specify the location of the window
  • Alpha: Window transparency
  • Gravity: The position of the window on the screen, using the gravity class constant
  • Format: window PixelFormat. Values are defined in PixelFormat

How do I assign a value to the window property

Window attributes most of the constant values stored in the WindowManager. LayoutParams class, we can obtain these constants by this class. There are also Gravity classes and PixelFormat classes.

Normally we add a window to and from the screen as follows:

// Called in the Activity
WindowManager.LayoutParams windowParams = new WindowManager.LayoutParams();
windParams.flags = WindowManager.LayoutParams.FLAG_FULLSCREEN;
TextView view = new TextView(this);
getWindowManager.addview(view,windowParams);
Copy the code

We can directly to WindowManager. LayoutParams object set properties.

The second method of assignment is to assign the window directly, as in

getWindow().flags = WindowManager.LayoutParams.FLAG_FULLSCREEN;
Copy the code

In addition, the window solfInputMode attribute is special, which can be specified directly in the AndroidManifest as follows:

 <activity android:windowSoftInputMode="adjustNothing" />
Copy the code

To sum up:

  • Important properties of Windows include type, flags, solfInputMode, gravity, and so on
  • We can assign the window property in different ways
  • There’s no need to write them all down, just find the constants when you need them

Window adding process

After understanding the source code, you can understand the previous theory more thoroughly. The window addition process refers to the process by which we add the window through the addView method of WindowManagerImpl.

Want to add a window, we know that the first to have a view and WindowManager LayoutParams object, to create a window, this is our common code:

Button button = new Button(this);
WindowManager.LayoutParams windowParams = new WindowManager.LayoutParams();
// windowParam is initialized here
windowParam.addFlags...
// Get the WindowManager object that applies PhoneWindow to add window
getWindowManager.addView(button,windowParams);
Copy the code

And then let’s go inside the addView method. We know that the windowManager implementation class is WindowManagerImpl. As mentioned above, go into its addView method and take a look:

@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
    applyDefaultToken(params);
    mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
Copy the code

You can see that he has handed the logic directly to mGlobal. This mGlobal is Windows ManagerGlobal, a global singleton that is a concrete logical implementation of the Windows Manager interface. The bridge mode is used here. Let’s look at Windows ManagerGlobal methods:

public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) {
    // Check whether the argument is valid
    if (view == null) {
        throw new IllegalArgumentException("view must not be null");
    }
    if (display == null) {
        throw new IllegalArgumentException("display must not be null");
    }
    if(! (paramsinstanceof WindowManager.LayoutParams)) {
        throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
    }
    
    final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
    // If it is not a child window, its parameters will be adjusted
    if(parentWindow ! =null) {
        parentWindow.adjustLayoutParamsForSubWindow(wparams);
    } else {
        final Context context = view.getContext();
        if(context ! =null&& (context.getApplicationInfo().flags & ApplicationInfo.FLAG_HARDWARE_ACCELERATED) ! =0) { wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; }}synchronized (mLock) {
        ...
        // Create a new viewRootImpl and set the parameters
        root = new ViewRootImpl(view.getContext(), display);
        view.setLayoutParams(wparams);

        // Add to Windows ManagerGlobal's three important lists, covered later
        mViews.add(view);
        mRoots.add(root);
        mParams.add(wparams);

        // Finally add the window via viewRootImpl
        try{ root.setView(view, wparams, panelParentView); }... }}Copy the code

The code is a bit long, step by step:

  • Firstly, check the validity of parameters
  • Then determine whether the window is a child window. If so, adjust the window. This is easy to understand, the child window should follow the characteristics of the parent window.
  • Then create a viewRootImpl object and add view, viewRootImpl, and Params to the three lists for saving
  • Finally, add it via viewRootImpl

A quick note about the three lists in Windows ManagerGlobal:

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>();
Copy the code

The three objects for each window are stored here, and then operations on the window can fetch objects directly from there. When the window is deleted, these objects are also removed from the list.

You can see that the added logic for the window is left to view Windows PL. The viewRootImpl is a bridge between the window and the View. The viewRootImpl can handle objects on both sides and then join them together. Let’s see how the viewRootImpl works:

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
    synchronized (this) {...try {
            mOrigWindowType = mWindowAttributes.type;
            mAttachInfo.mRecomputeGlobalAttributes = true;
            collectViewAttributes();
            // The windowSession method is called, the WMS method is called, and the logic for adding Windows is handed over to WMSres = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes, getHostVisibility(), mDisplay.getDisplayId(), mTmpFrame, mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel, mTempInsets); setFrame(mTmpFrame); }... }}Copy the code

View Windows PL has a lot of logic, but the important thing is that the method that calls mWindowSession calls the method that calls WMS. So this mWindows Session is really important to talk about.

MWindowSession is an IWindowSession object, and you can see this naming very quickly as you use AIDL cross-process communication here. IWindowSession is an IBinder interface whose implementation is in WindowManagerService. The native mWindowSession is just a Binder object. With this mWindowSession you can directly call WMS methods for cross-process communication.

So where does this mWindowSession come from? Let’s look at the viewRootImpl constructor method:

public ViewRootImpl(Context context, Display display) {... mWindowSession = WindowManagerGlobal.getWindowSession(); . }Copy the code

You can see that the Session object is from Windows ManagerGlobal. Take a closer look:

public static IWindowSession getWindowSession(a) {
 synchronized (WindowManagerGlobal.class) {
     if (sWindowSession == null) {
         try{... sWindowSession = windowManager.openSession(newIWindowSessionCallback.Stub() { ... }); }... }returnsWindowSession; }}Copy the code

So if you look at this familiar code format, you can see that this session is a singleton, that all view Windows PL Windows Sessions for the entire application are the same, that an application has only one Windows Session. For WMS, which serves multiple applications, it would be too much of a task to say that each view should have an entire session. The object unit of WMS is the application. It internally assigns some data structures to each application session, such as list, which is used to store each application window and the corresponding viewRootImpl. When you need to manipulate a view, you can do so by going directly to the View wrotimPL via session.

WMS creates the window, calculates the height of the window with parameters, etc., and draws it using viewRootImpl. I don’t want to talk about the code logic behind this, it is too complicated to go into WMS (I haven’t read WMS yet). The purpose of reading source code is to understand the nature of the whole system and workflow, the perception of the system as a whole, and not too deep into the details of the code, Android system so much code, if you go in depth will not come out, so it is good to point to.

We know that the windowManager interface inherits from the viewManager interface, which has two other interfaces: removeView and updateView. Here will not speak, interested readers can read the source code. The main purpose of adding processes is to understand the operation of the Window system and to have a sense of the internal processes so as to better understand Windows.

A final conclusion:

Window is added through the Corresponding WindowManagerImpl of PhoneWindow, which is internally implemented by calling WindowManagerGlobal. Windows ManagerGlobal uses view Windows PL to communicate across processes to get WMS to perform the business of creating Windows.

Each application has a windowSession that is used to communicate with WMS, such as ApplicationThread and AMS.

The key class for the Window mechanism

The previous source code process involves a lot of classes, here the relevant class unified analysis. Let’s start with a picture:

These are basically all the key classes we’ve covered in this article. Just listen to me. (The window in green is not a class, but a real window.)

Windows related

Window has only one implementation class: PhoneWindow, which inherits from the Window abstract class. I’ll focus on him later.

WindowManager related

As the name implies, windowManager is the Window management class. The key classes in this section are windowManager, viewManager, windowManagerImpl, and windowManagerGlobal. WindowManager is an interface that inherits from viewManager. In the viewManager contains three interfaces: we are very familiar with addView, removeView, updateView. Windows manager impl and PhoneWindow come in a pair; the former manages the latter. WindowManager ImpL is a windowManager implementation class, but it does not actually implement the logic itself, but is handed over to Windows ManagerGlobal. Windows ManagerGlobal is a global singleton. Windows Manager ImpL uses bridge mode internally. It is the true implementation of Windows Manager interface logic

View related

Here’s a key class: ViewRootImpl. Each view tree will have one. When I use the addView method of Windows Manager, I create a View wrootimpl. The role of ViewRootImpl is critical:

  • Responsible for the bridge transaction between view and window
  • Be responsible for contacting Windows ManagerService
  • Responsible for managing and drawing view trees
  • A staging post for events

Each window will have a ViewRootImpl, which is responsible for drawing the view tree and the bridge between the window and view. Each window will have a ViewRootImpl.

WindowManagerService

This is the real manager of Window, similar to AMS (Active ManagerService) managing the four components. All Window creations eventually go through windowManagerService. WMS is absolutely central to the Android Windows mechanism, determining how all Windows on the screen should be displayed, how click events should be distributed, etc.

Relationship between Window and PhoneWindow

To clarify the title, window refers to the concept of window in the window mechanism, and PhoneWindow refers to the class PhoneWindow. When I talk about it later, if I’m talking about a class, I’m going to put a class at the end. For example, window refers to the concept of window, and window class refers to the abstract class window. Readers should not be confused.

Remember when I was talking about the concept of Windows?

Thinking: Android has an abstract class called Window and a PhoneWindow implementation class, they are not the existence of Window, why say window is abstract does not exist

Here are a few more questions:

  • Some sources say that a PhoneWindow is a Window, which is a view container that manages the view inside the container. WindowManagerImpl can add views to it, like the addView method we talked about above. However, it also says that every window corresponds to a viewRootImpl, but it doesn’t explain why addView creates a new viewRootImpl every time.
  • There are some sources that call a PhoneWindow a window, but he says that the addView method doesn’t add a view, it adds a window, and he takes the name of the method as an argument that a view is a window, However, he does not explain why the PhoneWindow object was not created when the addView method was used to create the window.

Let’s take it step by step. Let’s first look at the source code for the window abstract class comment:

 Abstract base class for a top-level window look and behavior policy.  An
 instance of this class should be used as the top-level view added to the
 window manager. It provides standard UI policies such as a background.title
 area.default key processing.etc. Abstract base class for top-level window appearance and behavior policy. Instances of this class should be added to the top view of the window manager. It offers standardUIPolicies, such as backgrounds, header areas, default key handling, and so on.Copy the code

This class is the abstract base class of the top-level window, which must inherit from it. It is responsible for the appearance of the window such as background, title, default key handling, etc. An instance of this class is added to windowManager and allowed to be managed by windowManager. A PhoneWindow is a top-level window that is added to the top-level view of the top-level window manager. All other Windows need to be added to this top-level view, so it is more accurate to say that a PhoneWindow is not a view container, but a Window container.

So what’s the point of PhoneWindow?

First, provide a DecorView template. The diagram below:

Our Activity uses the setContentView to set the layout into a DecorView, so the DecorView’s layout becomes the background of the Activity interface. Also, the DecorView is split into a title bar and a content bar, so you can set the title bar interface as well. Also, since our interface is added to the DecorView, it is part of the DecorView. Setting the window property on the DecorView will also apply to our layout interface. Remember the last word in Google’s official comment for the Window class: it provides standard UI policies such as background, title area, default key handling, and so on. All of this can be done through a DecorView, which is the first use of PhoneWindow.

Second, remove the window logic from the Activity. An Activity has a lot of responsibilities, and if you do everything yourself, your code will become very bloated. Those of you who have read about Activity startup may know that AMS also uses the ActivityStarter class to extract the logic that starts an Activity. This leaves All window-related matters to PhoneWindow. (In fact, the Activity calls WindowManagerImpl, but since PhoneWindow and WindowManagerImpl are paired, they both handle window-related transactions, so we’ll simply write it as PhoneWindow.) When an Activity needs to add a interface, all it needs to do is say setContentView and call the PhoneWindow setContentView method to set the layout onto the screen. The Activity doesn’t have to worry about how to do that.

Third, restrict the component’s permission to add Windows. PhoneWindow has a token property inside that verifies that a PhoneWindow is allowed to add Windows. When the Activity creates a PhoneWindow, it assigns a value to the token passed from AMS, which gives it permission to add the token. Other PhoneWindows do not have this permission and therefore cannot add Windows. This is covered in detail in another article, so those who are interested should head over to the portal.

Of course, PhoneWindow does much more than that, and here are the three most important things I’ve learned so far. There must be a lot of official considerations for the design of a class, which can not be explained by the author’s simple analysis, but only gives a new thinking direction and leads people to know the real Window.

To sum up:

  • PhoneWindow itself is not really a window, but rather a tool class that helps activities operate Windows.
  • WindowManagerImpl is not a class that manages Windows, but a class that manages PhoneWindow. It is THE WMS that really manages Windows.
  • A PhoneWindow can be used in conjunction with a DecorView to provide standard UI policies for Windows that follow certain logic
  • PhoneWindow limits the permissions of different components to add Windows.

Window creation process for common components

WindowManagerImpl creates Windows with windowManagerImpl. As we learned earlier, windowManagerImpl manages the PhoneWindow, and they both appear at the same time. So there are two ways to create a window:

  • PhoneWindow already exists, create window directly through WindowManagerImpl
  • PhoneWindow does not yet exist. Create PhoneWindow first and then windowManagerImpl to create window

When we use the getWindowManager method in the Activity, we get the WindowManagerImpl corresponding to the application’s PhoneWindow. Let’s look at how different components create Windows,

Activity

Those of you who have read the Activity startup process will know that the Activity startup ends up in the handleLaunchActivity method of the ActivityThread.

I wrote a post about the Activity startup process that interested readers can go to by clicking the link below:

Activity Startup Process (based on API28)

As for why this method is not discussed here, interested readers can go to see the article above. Let’s look directly at the code for this method:

public void handleLaunchActivity(IBinder token, boolean finalStateRequest, boolean isForward,
        String reason) {
    ...;
    // Windows ManagerGlobal is initialized here
    WindowManagerGlobal.initialize();

   	// Start the Activity and call back the Activity's onCreate method
    finalActivity a = performLaunchActivity(r, customIntent); . }private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    try {
        // Create Application here
        Application app = r.packageInfo.makeApplication(false, mInstrumentation); .if(activity ! =null) {... Window window =null;
            if(r.mPendingRemoveWindow ! =null && r.mPreserveWindow) {
                window = r.mPendingRemoveWindow;
                r.mPendingRemoveWindow = null;
                r.mPendingRemoveWindowManager = null;
            }
            appContext.setOuterContext(activity);
            // Here we pass window as an argument to the attach method of the activity
            // Window ==null
            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); .// Finally there is a callback to the Activity's onCreate method
            if (r.isPersistable()) {
                mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
            } else{ mInstrumentation.callActivityOnCreate(activity, r.state); }}... }Copy the code

The handleLaunchActivity code first initializes Windows ManagerGlobal and then calls the performLaunchActivity method. There’s a lot of code, and I’ve only captured important parts of it. The Application object is created, the attach method of the Activity is called, the window is passed in, and the onCreate method of the Activity is called back. So the most likely method to create a window here is the Attach method of the Activity. Let’s go inside and have a look:

final void attach(... ,Context context,Window window, ...) {
    ...;
 	// Create a new PhoneWindow object and initialize the window
	mWindow = new PhoneWindow(this, window, activityConfigCallback);
    // The Activity implements the Window's callBack interface and sets itself to the window
    mWindow.setWindowControllerCallback(this);
    mWindow.setCallback(this);
    mWindow.setOnWindowDismissedCallback(this);
    mWindow.getLayoutInflater().setPrivateFactory(this); .// This initializes the WindowManager object for WindowsmWindow.setWindowManager( (WindowManager)context.getSystemService(Context.WINDOW_SERVICE), mToken, mComponent.flattenToString(), (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) ! =0);        
}
Copy the code

Again, only important code was captured. The attach method had a lot of parameters, so I only left parameters related to window. In this method, the PhoneWindow is first created using the window passed in. The Activity implements the Window’s callBack interface and can set itself to the Window as an observer. You can notify the activity when the window changes. Then create a WindowManager and bind it to PhoneWindow so that we can operate PhoneWindow through WindowManager. (Isn’t this setWindowManager? When was Windows Manager created?) Let’s go into the setWindowManager method and have a look:

public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
        boolean hardwareAccelerated) {
    mAppToken = appToken;
    mAppName = appName;
    mHardwareAccelerated = hardwareAccelerated;
    if (wm == null) {
        wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
    }
    // Here windowManager is created
    mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
}
Copy the code

This method first gets the WindowManager of the application service (the implementation class is also WindowManagerImpl), and then creates a new WindowManager from the WindowManager of the application service.

As you can see here, the windowManagerImpl is created using the windowManager of the system service, so all windowManagerImpl of this application are the same kernel windowManager, and the created is just a shell.

This binds PhoneWindow to WindowManagerImpl. The Activity can operate PhoneWindow through WindowManagerImpl.


Now that the Activity’s PhoneWindow and WindowManagerImpl objects are created, it’s time to set the Activity’s layout file to the PhoneWindow. After calling the attach method of the Activity, we call back the Activity’s onCreate method. In the onCreate method, we call setContentView to set the layout as follows:

public void setContentView(View view, ViewGroup.LayoutParams params) {
    getWindow().setContentView(view, params);
    initWindowDecorActionBar();
}
Copy the code

GetWindow here is the PhoneWindow object we created above. Here we go:

// Note that it has multiple overloaded methods. Select the corresponding method for the parameter
public void setContentView(int layoutResID) {
    / / create a DecorView
    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 {
        // Here the layout is loaded based on the layout ID
        mLayoutInflater.inflate(layoutResID, mContentParent);
    }
    mContentParent.requestApplyInsets();
    final Callback cb = getCallback();
    if(cb ! =null && !isDestroyed()) {
        // Call back the activity method
        cb.onContentChanged();
    }
    mContentParentExplicitlySet = true;
}
Copy the code

Again, let’s just look at the key code:

  • First check if the decorView is created. If not, create the decorView
  • Load the layout into the DecorView
  • CallBack the Activity’s callBack method

Here’s a sidebar on what a DecorView is. DecorView is a preset layout in PhoneWindow that looks like this:

It is a vertical layout with ActionBar at the top and ContentView at the bottom, which is a FrameLayout. Our Activity layout is loaded into the ContentView for display. So a Decorview is the top-level viewGroup of the Activity layout.

Then let’s look at how to initialize DercorView:

private void installDecor(a) {
    mForceDecorInstall = false;
    if (mDecor == null) {
        // The DecorView is created here
        mDecor = generateDecor(-1); . }else {
        mDecor.setWindow(this);
    }
    if (mContentParent == null) {
        // Initialize the DecorView to get the ContentViewmContentParent = generateLayout(mDecor); . }}Copy the code

The installDecor method basically creates a DecorView object, initializes the DecorView by loading a preset layout (that’s the layout described above) and gets the ContentView for that preset layout. After initializing the DecorView, load the Activity layout into the DecorView’s ContentView as follows:

// Note that it has multiple overloaded methods. Select the corresponding method for the parameter
public void setContentView(int layoutResID) {...if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
        final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
                getContext());
        transitionTo(newScene);
    } else {
        // Here the layout is loaded based on the layout IDmLayoutInflater.inflate(layoutResID, mContentParent); }... mContentParent.requestApplyInsets();final Callback cb = getCallback();
    if(cb ! =null && !isDestroyed()) {
        // Call back the activity methodcb.onContentChanged(); }}Copy the code

So you can see that the Activitiy layout is indeed added to the ContentView of the DecorView, which is why onCreate uses setContentView instead of setView. The method that finally calls back to the Activity tells the Activity that the DecorView has been created and initialized.


At this point, the DecorView creation is complete, but the most important step is missing: adding the DecorView to the screen as a window. We know from the previous introduction that adding a window uses the addView method of WindowManagerImpl. This step is performed in the ActivityThread’s handleResumeActivity method:

public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
        String reason) {
    // Call the Activity's onResume method
    finalActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason); .// Display the decorView on the screen
	if (r.activity.mVisibleFromClient) {
        r.activity.makeVisible();
  	}
Copy the code

This step has two main points: callback the onResume method and add the decorView to the screen. Let’s look at what the makeVisible method does:

void makeVisible(a) {
    if(! mWindowAdded) { ViewManager wm = getWindowManager(); wm.addView(mDecor, getWindow().getAttributes()); mWindowAdded =true;
    }
    mDecor.setVisibility(View.VISIBLE);
}
Copy the code

Is that familiar? Call the WindowManagerImpl addView method directly to add the decorView to the screen, and our Activity interface will now appear on the screen.


Okay, this is a long part, so to sum it up:

  • From the Activity launch process, you can see how the Activity creates the Window
  • Create PhoneWindow -> Create WindowManager -> Create decorView -> use WindowManager to display the decorView on screen
  • When the onResume method is called back, the DecorView has not yet been added to the screen, so when onResume is called back, the screen is about to be displayed, not already displayed

PopupWindow

PopupWindow is also widely used in daily life. The most common requirement is to popup a menu. PopupWindow also uses windowManager to add Windows to and from the screen, but popupWindow is attached to the activity and cannot pop popupWindow when the activity is not running. When the onResume method is called, there is still a lot of work to do and the Activity is not fully started, so popupWindow cannot be popped in the onCreate, onStart, and onResume methods.

The popupWindow process is divided into two parts: create the view; Add Windows through windowManager. First look at the PopupWindow constructor:

public PopupWindow(View contentView, int width, int height, boolean focusable) {
    if(contentView ! =null) {
        mContext = contentView.getContext();
        mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
    }

    setContentView(contentView);
    setWidth(width);
    setHeight(height);
    setFocusable(focusable);
}
Copy the code

He has multiple overloaded methods, but he always ends up calling this method with four arguments. It’s basically getting the context and getting the WindowManager from the context.


And then we see his display method. The display method is showAtLocation and showAsDropDown. The main thing is that the position of the processing display is different, but everything else is similar. We look at the first method:

public void showAtLocation(View parent, int gravity, int x, int y) {
    mParentRootView = new WeakReference<>(parent.getRootView());
    showAtLocation(parent.getWindowToken(), gravity, x, y);
}
Copy the code

The logic is simple: the root layout of the parent view is stored and another overloaded method is called:

public void showAtLocation(IBinder token, int gravity, int x, int y) {
    // Return if contentView is empty
    if (isShowing() || mContentView == null) {
        return;
    }

    TransitionManager.endTransitions(mDecorView);
    detachFromAnchor();
    mIsShowing = true;
    mIsDropdown = false;
    mGravity = gravity;
	/ / get the WindowManager. LayoutParams object
    final WindowManager.LayoutParams p = createPopupLayoutParams(token);
    // Do some preparatory work
    preparePopup(p);

    p.x = x;
    p.y = y;
	// Perform popupWindow display
    invokePopup(p);
}
Copy the code

The main logic of this method is:

  • Determines whether the contentView is empty or whether it is displayed
  • Do some prep work
  • Perform popupWindow display

Here’s a look at what he did to prepare:

private void preparePopup(WindowManager.LayoutParams p) {...if(mBackground ! =null) {
        mBackgroundView = createBackgroundView(mContentView);
        mBackgroundView.setBackground(mBackground);
    } else {
        mBackgroundView = mContentView;
    }
	// Create a DecorView
    // Note that the DecorView here is not the DecorView we were talking about earlier, but its inner class: PopupDecorView
    mDecorView = createDecorView(mBackgroundView);
    mDecorView.setIsRootNamespace(true); . }Copy the code

Here’s his display:

private void invokePopup(WindowManager.LayoutParams p) {...// Call windowManager to add the windowmWindowManager.addView(decorView, p); . }Copy the code

At this point popupWindow will be added to the screen.


To sum up:

  • Build popupDecorView based on the parameters
  • Add the popupDecorView to the screen

Dialog

The dialog creation process is like creating a PhoneWindow, initializing a DecorView, and adding a DecorView. I’m just going to explain it briefly. First look at his construction method:

Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) {
    ...
    / / get windowManager
    mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
	/ / PhoneWindow construction
    final Window w = new PhoneWindow(mContext);
    mWindow = w;
    // Initialize PhoneWindow
    w.setCallback(this);
    w.setOnWindowDismissedCallback(this);
    w.setOnWindowSwipeDismissedCallback(() -> {
        if(mCancelable) { cancel(); }}); w.setWindowManager(mWindowManager,null.null);
    w.setGravity(Gravity.CENTER);
    mListenersHandler = new ListenersHandler(this);
}
Copy the code

This is very similar to the previous Activity creation process, but it is important to note that mWindowManager is actually a WindowManager for an Activity, and the context is usually an Activity. A window token is a token that is not an activity and is not a token that is not a window token.) we see the getSystemService method for an activity:

public Object getSystemService(@ServiceName @NonNull String name) {
    if (getBaseContext() == null) {
        throw new IllegalStateException(
                "System services not available to Activities before onCreate()");
    }
	// Get the windowManager for the activity
    if (WINDOW_SERVICE.equals(name)) {
        return mWindowManager;
    } else if (SEARCH_SERVICE.equals(name)) {
        ensureSearchManager();
        return mSearchManager;
    }
    return super.getSystemService(name);
}
Copy the code

You can see that the windowManager here is really the windowManager for the Activity. Here’s his show method:

public void show(a) {...// Call the onStart method to retrieve the previously initialized decorViewonStart(); mDecor = mWindow.getDecorView(); . WindowManager.LayoutParams l = mWindow.getAttributes(); .// Use windowManager to add WindowsmWindowManager.addView(mDecor, l); . mShowing =true;
    sendShowMessage();
}
Copy the code

Note that the mWindowManager here is the WindowManager for the Activity, so this is actually added to the Activity’s PhoneWindow. The next step is the same as the previous add process, which I won’t explain here.


To sum up:

  • Dialog differs from popupWindow in that dialog creates a new PhoneWindow using the PhoneWindow DecorView template. And popupWindow is not
  • Dialogs have a higher level of display and are displayed directly above the Activity, and popupWindows added after the dialog are also displayed under the dialog
  • The dialog creation process is very much like an activity

Look at Windows from an Android architecture perspective

Earlier we looked at the relationship between PhoneWindow and Window and saw that PhoneWindow is not really a Window, but a Window container. Have you ever wondered why Google would create a class named Window that is not window? Are you trying to confuse us? To understand this, let’s review the entire Android Window mechanism structure.

We’ll start with WindowManagerService. We know that WMS is the ultimate administrator of Windows and holds a session for each application in WMS. As we discussed earlier about sessions, each application is a global singleton, a binder object that communicates with WMS. WMS creates a windowStatus object for each window. Windows of the same application use the same session to communicate across processes. The structure is roughly as follows:

The person responsible for communicating with the WMS is view ArotimPL. As we mentioned earlier, each view tree is a window, and the View tree is responsible for communicating with the WMS and drawing the view. If you look at the picture above more carefully, it looks like:

Each windowStatus in the figure corresponds to a viewRootImpl, through which WMS controls the view. This is the administrative structure of the Window mechanism. When we need to add Windows, the final logical implementation is Windows Global, which internally uses its session to create a ViewrotimPL and then applies to WMS to add Windows. The structure looks something like this:

Windows ManagerGlobal creates viewrotimPL using its own IWindows Session, which is a global singleton. The viewRootImpl and WMS request that the window be created, and then the WMS allows the viewRootImpl to draw the view. Meanwhile, WMS stores the viewRootImpl information through windowStatus, so that if THE WMS needs to modify the view, You can modify the View directly via view wrootimpl.


You can see from the above description that I didn’t mention PhoneWindow and Windows manager impl at all. This is because they are not classes within the Window mechanism, but frameworks that encapsulate the Window mechanism. Suppose we don’t have PhoneWindow and WindowManager how do we add a window? Call WindowGlobal to get session, create viewRootImpl, access WMS, and then use viewRootImpl to draw the view. Isn’t that complicated, but this is just the whole process. And Windows Manager ImpL does just that. He has the Windows ManagerGlobal singleton internally and then helps us through this series of steps. Also, there is only one Instance of Windows ManagerImpl, the other Windows ManagerImpl is built on Windows ManagerImpl singleton. This point was introduced earlier through the source code.

Additionally, PhoneWindow is not a window but rather a tool class that AIDS Activity management, so why not call it windowUtils? First, the PhoneWindow class is Google’s way of encapsulating the Windows mechanism. PhoneWindow has a DecorView inside, and our layout view is added to the DecorView because we can set our layout view indirectly by giving the DecorView a background, width, title bar, keystroke feedback, etc. In this way, the existence of PhoneWindow shields developers from the real Window and exposes developers to an “existing” Window. We can think of a PhoneWindow as a window, window as a view container. When we need to add a view to the screen, we just need to get the windowManagerImpl corresponding to the window and call addView method to add the view. It also explains why the Windows Manager interface method is addView instead of addWindow, because the window is a view, and because it hides the real window from the developer that we’re adding a view to the window, Window is a real thing. Let me draw a picture of their relationship as follows:

The yellow part is missing from the Window framework that Google provides to developers, while the green part is the real Window mechanic. PhoneWindow makes it very easy to do window operations without having to understand how the underlying layer works. PhoneWindow makes Windows visible and becomes a view container.

Ok, to sum up:

  • The Windows mechanism inside Android is not the same as the API that Google exposed to us, and the purpose of Google encapsulation is to make Windows work better for us.
  • Frameworks such as Dialog and popupWindow further encapsulate specific scenarios.
  • When we look at the Window mechanism, we need to skip the application layer and see the essence of Window to better understand it.
  • In the rest of Android, too, encapsulation hides the underlying logic from developers, allowing us to make better use of it. But if we need to understand his mechanics, we need to bypass this layer of encapsulation and get to the bottom of it.

conclusion

This is the end of the text. Here’s a summary of what I said in this article:

  • What is window
  • Explain the parameters of the Window
  • Explains the key classes within the Window mechanism
  • From the source code to explain the window add process and the main components of the Window add process
  • The relation between PhoneWindow and Window is explained in detail, and the encapsulation thought of Google is discussed

The most important point in this paper is to understand the nature of Window, distinguish the relationship between Window and View and the relationship between Window and PhoneWindow.

When THE author wrote this article, the arrangement of each section is more hesitant: if the concept of the first, there is no source process is difficult to understand; Let’s start with the source code flow. It’s hard to read the source code without a conceptual understanding. Finally, I decided to talk about the real concept of Window first, so that readers can have a sense of the whole.

The article is very long. What the author wants to talk about Window is in this article.

Hope this article is helpful.

That’s all. Thank you for reading

Original is not easy, feel helpful can like collection comments forward concerns. I am uneducated, any mistakes welcome comment area or private communication. If need to reprint please private letter or comment area exchange.

And welcome to my blog: Portal

reference

  • Face the ground: Explore Windows in Android
  • Face the ground: WindowManager view binding and architecture
  • Fantastic Window tour
  • Android advanced essential, Window mechanism exploration

Exploring the Art of Android Development

Android Advanced Decryption