About the author

Guo Xiaoxing, programmer and guitarist, is mainly engaged in the infrastructure of Android platform. Welcome to exchange technical questions. You can go to my Github to raise an issue or send an email to [email protected] to communicate with me.

For the first time in this series, see the introduction and the table of contents for more articles.

The article directories

  • Window add process
  • Window deletion process
  • Window update process

The interface that apps use to talk to the window manager.

WindowManager is the interface that the application interacts with the Window management service WindowManagerService. WindowManagerService is a Window management service located in the Framework layer. Its responsibility is to manage all Windows in the system, that is, Windows. For an introduction to Windows, we looked at the Android display framework in article 03: Window, the manager of Android application view, has been analyzed in detail. Generally speaking, Window is a display area on mobile phone, namely the process of drawing canvas Surface in Android, and adding a Window is also the process of applying for and allocating a Surface. The manager of the entire process is WindowManagerService.

Windows are orderly displayed on the screen under the management of WindowManagerService, forming a colorful user interface. The entire Android Window system can be represented as follows:

  • WindowManager: The interface that applies to interact with WindowManagerService, the window management service
  • WindowManagerService: Window management service, which runs in a separate process, so the interaction between WindowManager and WindowManagerService is also an IPC process.
  • SurfaceFlinger: The SurfaceFlinger service runs in the System process of the Android System. It is responsible for managing the Frame Buffer of the Android System. The display screen of the Android device is abstracted as a Frame Buffer. The SurfaceFlinger service on Android draws an application’s user interface by writing to this frame buffer.
  • Surface: Each display window is a Surface.

WindowManager is an interface that inherits from ViewManager. The implementation class is WindowManagerImpl. In fact, most of the functions we use are defined in ViewManager.

public interface ViewManager{
    / / add the View
    public void addView(View view, ViewGroup.LayoutParams params);
    / / update the View
    public void updateViewLayout(View view, ViewGroup.LayoutParams params);
    / / delete the View
    public void removeView(View view);
}Copy the code

The WindowManager can be accessed from the Context, and the WindowManager will register with the ContextImpl map container at startup just like any other service, and then be accessed by their key.

windowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);Copy the code

WindowManager implementation class is WindowManagerImpl, in WindowManagerImpl internal actual function is WindowManagerGlobal to complete, we directly to analyze it inside the implementation of these three methods.

Window add process

public final class WindowManagerGlobal {

     public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) {
            // Verify the validity of the parameter.//ViewRootImpl encapsulates the interaction between View and WindowManager
            ViewRootImpl root;
            View panelParentView = null;

            synchronized (mLock) {
                // Start watching for system property changes.
                if (mSystemPropertyUpdater == null) {
                    mSystemPropertyUpdater = new Runnable() {
                        @Override public void run(a) {
                            synchronized (mLock) {
                                for (int i = mRoots.size() - 1; i >= 0; --i) { mRoots.get(i).loadSystemProperties(); }}}}; SystemProperties.addChangeCallback(mSystemPropertyUpdater); }int index = findViewLocked(view, false);
                if (index >= 0) {
                    if (mDyingViews.contains(view)) {
                        // Don't wait for MSG_DIE to make it's way through root's queue.
                        mRoots.get(index).doDie();
                    } else {
                        throw new IllegalStateException("View " + view
                                + " has already been added to the window manager.");
                    }
                    // The previous removeView() had not completed executing. Now it has.
                }

                // If this is a panel window, then find the window it is being
                // attached to for future reference.
                if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
                        wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
                    final int count = mViews.size();
                    for (int i = 0; i < count; i++) {
                        if(mRoots.get(i).mWindow.asBinder() == wparams.token) { panelParentView = mViews.get(i); }}}// Build ViewRootImpl from context
                root = new ViewRootImpl(view.getContext(), display);

                view.setLayoutParams(wparams);

                //mViews stores all View objects corresponding to Windows
                mViews.add(view);
                //mRoots stores all ViewRootImpl objects corresponding to Windows
                mRoots.add(root);
                / / mParams store all Window corresponding to the WindowManager LayoutParams object
                mParams.add(wparams);
            }

            // do this last because it fires off messages to start doing things
            try {
                // Call the viewrootimpl.setView () method to add the Window and update the interface
                root.setView(view, wparams, panelParentView);
            } catch (RuntimeException e) {
                // BadTokenException or InvalidDisplayException, clean up.
                synchronized (mLock) {
                    final int index = findViewLocked(view, false);
                    if (index >= 0) {
                        removeViewLocked(index, true); }}throwe; }}}Copy the code

There are three important member variables in this method:

  • MViews stores all View objects corresponding to Windows
  • MRoots stores all ViewRootImpl objects corresponding to Windows
  • MParams store all Window corresponding to the WindowManager. LayoutParams object

ViewRootImpl is a wrapper class that encapsulates how a View interacts with WindowManager. It acts as a bridge between View and WindowManagerService. Finally, call the Viewrootimpl.setView () method to add the Window and update the interface.

Let’s look at the implementation of this method.

public final class ViewRootImpl implements ViewParent.View.AttachInfo.Callbacks.ThreadedRenderer.HardwareDrawCallbacks {

     public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
            synchronized (this) {
                if (mView == null) {
                    mView = view;

                    // Check and preprocess parameters.// 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.

                    //1. Call requestLayout() to complete the asynchronous interface drawing request
                    requestLayout();
                    if ((mWindowAttributes.inputFeatures
                            & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
                        mInputChannel = newInputChannel(); } mForceDecorViewVisibility = (mWindowAttributes.privateFlags & PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY) ! =0;
                    try {
                        mOrigWindowType = mWindowAttributes.type;
                        mAttachInfo.mRecomputeGlobalAttributes = true;
                        collectViewAttributes();
                        //2. Create WindowSession and request WindowManagerService through WindowSession to complete the Window adding process
                        // This is an IPC process.
                        res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                                getHostVisibility(), mDisplay.getDisplayId(),
                                mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                                mAttachInfo.mOutsets, mInputChannel);
                    } catch (RemoteException e) {
                        mAdded = false;
                        mView = null;
                        mAttachInfo.mRootView = null;
                        mInputChannel = null;
                        mFallbackEventHandler.setView(null);
                        unscheduleTraversals();
                        setAccessibilityFocus(null.null);
                        throw new RuntimeException("Adding window failed", e);
                    } finally {
                        if(restore) { attrs.restore(); }}... }}}Copy the code

This approach does two main things:

  1. RequestLayout () will call scheduleTraversals() to draw the View asynchronously. The scheduleTraversals() method submits a TraversalRunnable to the work queue for rendering the View. TraversalRunnable ultimately calls the performTraversals() method to do the actual drawing. The performTraversals() method is already familiar, and its implementation has been analyzed in detail in article 02Android display framework: Android application View carrier View.
  2. WindowSession is created and WindowManagerService is requested by WindowSession to complete the process of adding Windows. This is an IPC process, WindowManagerService acts as the actual Window manager, Windows are created, deleted and updated by it to complete, it is also responsible for the window stacking sorting and size calculation and other work.

Note: in article 02Android display framework: Android application View, we have analyzed the implementation of performTraversals() method in detail. Here we briefly mention:

  1. Gets the Surface object for graphing.
  2. The performMeasure() method is called to measure the size of each View in the View tree.
  3. Call the performLayout() method to calculate the position of each View in the View tree.
  4. Call the performMeasure() method to draw each View in the View tree.

Now that we have mentioned the cross-process communication between WindowManager and WindowManagerService, let’s talk about their communication flow. Android’s various services are based on C/S structure to design, the system layer provides services, the application layer uses services. The same is true for WindowManager, which communicates with WindowManagerService through WindowSession.

  1. First call ServiceManager. GetService (” window “) get WindowManagerService, this method returns the IBinder object, Then call IWindowManager. Stub. AsInterface () method converts WindowManagerService a IWindowManager object.
  2. The openSession() method is then called to establish a communication session with WindowManagerService for subsequent cross-process communication. This communication session is the WindowSession we will use later.

Almost all Android services are implemented in this way, and it is a process based on the IPC implemented by AIDL. Readers can consult the materials for AIDL.

public final class WindowManagerGlobal {

    public static IWindowSession getWindowSession(a) {
        synchronized (WindowManagerGlobal.class) {
            if (sWindowSession == null) {
                try {
                    InputMethodManager imm = InputMethodManager.getInstance();
                    // Get the WindowManagerService object and convert it to the IWindowManager type
                    IWindowManager windowManager = getWindowManagerService();
                    // Call the openSession() method to establish a communication session with WindowManagerService for subsequent purposes
                    // Cross-process communication.
                    sWindowSession = windowManager.openSession(
                            new IWindowSessionCallback.Stub() {
                                @Override
                                public void onAnimatorScaleChanged(float scale) {
                                    ValueAnimator.setDurationScale(scale);
                                }
                            },
                            imm.getClient(), imm.getInputContext());
                } catch (RemoteException e) {
                    throwe.rethrowFromSystemServer(); }}returnsWindowSession; }}public static IWindowManager getWindowManagerService(a) {
        synchronized (WindowManagerGlobal.class) {
            if (sWindowManagerService == null) {
                / / call ServiceManager. GetService (" window ") get WindowManagerService, this method returns the IBinder object
                / /, and then call IWindowManager. Stub. AsInterface () method converts WindowManagerService a IWindowManager object
                sWindowManagerService = IWindowManager.Stub.asInterface(
                        ServiceManager.getService("window"));
                try {
                    sWindowManagerService = getWindowManagerService();
                    ValueAnimator.setDurationScale(sWindowManagerService.getCurrentAnimatorScale());
                } catch (RemoteException e) {
                    throwe.rethrowFromSystemServer(); }}returnsWindowManagerService; }}}Copy the code

Window deletion process

The Window deletion process is also done in Windows ManagerGlobal.

public final class WindowManagerGlobal {

   public void removeView(View view, boolean immediate) {
        if (view == null) {
            throw new IllegalArgumentException("view must not be null");
        }

        synchronized (mLock) {
            //1. Find the index of the View to be deleted
            int index = findViewLocked(view, true);
            View curView = mRoots.get(index).getView();
            //2. Call removeViewLocked() to remove the View
            // Continue to call the viewrootimpl.die () method to complete the View deletion.
            removeViewLocked(index, immediate);
            if (curView == view) {
                return;
            }

            throw new IllegalStateException("Calling with view " + view
                    + " but the ViewAncestor is attached to "+ curView); }}private void removeViewLocked(int index, boolean immediate) {
        ViewRootImpl root = mRoots.get(index);
        View view = root.getView();

        if(view ! =null) {
            InputMethodManager imm = InputMethodManager.getInstance();
            if(imm ! =null) { imm.windowDismissed(mViews.get(index).getWindowToken()); }}boolean deferred = root.die(immediate);
        if(view ! =null) {
            view.assignParent(null);
            if(deferred) { mDyingViews.add(view); }}}}Copy the code

Let’s look again at the implementation of the Viewrootimpl.die () method.

public final class ViewRootImpl implements ViewParent.View.AttachInfo.Callbacks.ThreadedRenderer.HardwareDrawCallbacks {
      boolean die(boolean immediate) {
            // Make sure we do execute immediately if we are in the middle of a traversal or the damage
            // done by dispatchDetachedFromWindow will cause havoc on return.

            // Determine whether to perform asynchronous or synchronous deletion based on the immediate parameter
            if(immediate && ! mIsInTraversal) { doDie();return false;
            }

            if(! mIsDrawing) { destroyHardwareRenderer(); }else {
                Log.e(mTag, "Attempting to destroy the window while drawing! \n" +
                        " window=" + this + ", title=" + mWindowAttributes.getTitle());
            }
            // In the case of asynchronous deletion, sending a message to delete the View will return MSG_DIE directly
            mHandler.sendEmptyMessage(MSG_DIE);
            return true;
        }

        void doDie(a) {
            checkThread();
            if (LOCAL_LOGV) Log.v(mTag, "DIE in " + this + " of " + mSurface);
            synchronized (this) {
                if (mRemoved) {
                    return;
                }
                mRemoved = true;
                if (mAdded) {
                    / / call dispatchDetachedFromWindow delete () to complete the View
                    dispatchDetachedFromWindow();
                }

                if(mAdded && ! mFirst) { destroyHardwareRenderer();if(mView ! =null) {
                        int viewVisibility = mView.getVisibility();
                        booleanviewVisibilityChanged = mViewVisibility ! = viewVisibility;if (mWindowAttributesChanged || viewVisibilityChanged) {
                            // If layout params have been changed, first give them
                            // to the window manager to make sure it has the correct
                            // animation info.
                            try {
                                if ((relayoutWindow(mWindowAttributes, viewVisibility, false) & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) ! =0) { mWindowSession.finishDrawing(mWindow); }}catch (RemoteException e) {
                            }
                        }

                        mSurface.release();
                    }
                }

                mAdded = false;
            }
            // Refresh the data to remove the information related to the currently removed View from the three lists we mentioned above: mRoots, mParms and mViews.
            WindowManagerGlobal.getInstance().doRemoveView(this);
        }

        void dispatchDetachedFromWindow(a) {
                / / 1. Callback View dispatchDetachedFromWindow method, notify the View has been removed from the Window
                if(mView ! =null&& mView.mAttachInfo ! =null) {
                    mAttachInfo.mTreeObserver.dispatchOnWindowAttachedChange(false);
                    mView.dispatchDetachedFromWindow();
                }

                mAccessibilityInteractionConnectionManager.ensureNoConnection();
                mAccessibilityManager.removeAccessibilityStateChangeListener(
                        mAccessibilityInteractionConnectionManager);
                mAccessibilityManager.removeHighTextContrastStateChangeListener(
                        mHighContrastTextManager);
                removeSendWindowContentChangedCallback();

                destroyHardwareRenderer();

                setAccessibilityFocus(null.null);

                mView.assignParent(null);
                mView = null;
                mAttachInfo.mRootView = null;

                mSurface.release();

                if(mInputQueueCallback ! =null&& mInputQueue ! =null) {
                    mInputQueueCallback.onInputQueueDestroyed(mInputQueue);
                    mInputQueue.dispose();
                    mInputQueueCallback = null;
                    mInputQueue = null;
                }
                if(mInputEventReceiver ! =null) {
                    mInputEventReceiver.dispose();
                    mInputEventReceiver = null;
                }

                // Call the windowSession.remove () method, which is also an IPC procedure
                / / WindowManagerService removeWindow () method to remove the Window.
                try {
                    mWindowSession.remove(mWindow);
                } catch (RemoteException e) {
                }

                // Dispose the input channel after removing the window so the Window Manager
                // doesn't interpret the input channel being closed as an abnormal termination.
                if(mInputChannel ! =null) {
                    mInputChannel.dispose();
                    mInputChannel = null; } mDisplayManager.unregisterDisplayListener(mDisplayListener); unscheduleTraversals(); }}Copy the code

Let’s summarize the Window deletion process again:

  1. Find the index of the View to be deleted
  2. Call removeViewLocked() to remove the View, and removeViewLocked() calls viewrootimpl.die () to remove the View.
  3. The viewrootimpl.die () method determines whether to perform an asynchronous or synchronous deletion based on the immediate parameter. If the deletion is asynchronous, MSG_DIE sends a message to delete the View and returns it. In the case of synchronous deletion, the doDie() method is called.
  4. DoDie () method call dispatchDetachedFromWindow delete () to complete the View, in View of this method in the first callback dispatchDetachedFromWindow method, to inform the View has been removed from the Window, Then call WindowSession. Remove () method, which is also a process of IPC, the final call is WindowManagerService removeWindow () method to remove the Window.

Window update process

public final class WindowManagerGlobal {

    public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
        if (view == null) {
            throw new IllegalArgumentException("view must not be null");
        }
        if(! (paramsinstanceof WindowManager.LayoutParams)) {
            throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
        }

        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;

        // Update the View's LayoutParams parameter
        view.setLayoutParams(wparams);

        synchronized (mLock) {
            // Find the index of Viewd and update the parameters in mParams
            int index = findViewLocked(view, true);
            ViewRootImpl root = mRoots.get(index);
            mParams.remove(index);
            mParams.add(index, wparams);
            / / call ViewRootImpl. SetLayoutParams () to the layout of the work done.
            root.setLayoutParams(wparams, false); }}}Copy the code

We take a look at ViewRootImpl. SetLayoutParams () method.

public final class ViewRootImpl implements ViewParent.void setLayoutParams(WindowManager.LayoutParams attrs.boolean newView) {
           synchronized (this) {
               // Parameter preprocessing.// If the View is new, call requestLayout() to redraw it
               if (newView) {
                   mSoftInputMode = attrs.softInputMode;
                   requestLayout();
               }

               // Don't lose the mode we last auto-computed.
               if ((attrs.softInputMode & WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST)
                       == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_UNSPECIFIED) {
                   mWindowAttributes.softInputMode = (mWindowAttributes.softInputMode
                           & ~WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST)
                           | (oldSoftInputMode & WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST);
               }

               mWindowAttributesChanged = true;
               // If it is not a new View, call requestLayout() to redraw itscheduleTraversals(); }}}Copy the code

The Window update process is also similar to other processes:

  1. Update the View’s LayoutParams parameter, find the index of Viewd, and update the mParams parameter.
  2. Call ViewRootImpl. SetLayoutParams () method to the layout of the work, in setLayoutParams () method will call in scheduleTraversals decode () the heavy drawing, The subsequent scheduleTraversals() process is the View’s measure, layout, and draw process, which we have discussed above.

From the above analysis, we have learned about the process of adding, deleting, and updating Windows. Add, delete, and update Windows. Why are methods addView, updateViewLayout, removeView, and what is the nature of a Window? 🤔

In fact, Window is an abstract concept, that is to say, it does not actually exist, it exists in the form of View, each Window corresponds to a View and a ViewRootImpl, Window and View are connected through ViewRootImpl. By extension, WindowManagerService does not actually manage Windows, but views, and manages which View should be displayed at the top in its current state. SurfaceFlinger draws are also views.

In this article, we focus on the implementation of the Android window service Client side, in fact, more content is on the Server side, that is, WindowManagerService. Windows ManagerService is an internal service of the system, so we will not expand it further. In general, the purpose of this series of articles is to better serve the developers of the application layer. After the analysis of the implementation principle of the Android application layer Framework is completed, we will go further to analyze the implementation of the system layer Framework.