Android Window series - WindowManager Source code analysis of addview (update of View)

summary

The previous article explained the knowledge points related to window and decorview. Interested readers can see the following:
Android Window series (I) - window and decorview

This article will continue to explore the relationship between window and view, mainly focusing on "how to add view to window".

How to add View in window

There are many such scenarios, including the following examples:

  • The activity adds a view to the window at startup
  • dialog adds a view to the window at startup
  • toast adds a view to the window at startup
  • Suspension window

The activity adds a view to the window at startup

The task stack calls of relevant logic are as follows:

  • ActivityThread.handleResumeActivity()
  • WindowManager.addView()

This can also explain why it is inaccurate to obtain the width and height of the view when onCreate.
The reason is that the View is not added to the Window until onResume is executed.

Suspension window

The author has written relevant articles before. Interested readers can see the following:
Android suspended window log tool

The key codes displayed by the suspended window in the author's demo are as follows:

  @UiThread
  public void showDebugViewOnUiThread(Context context) {
    if (studyFloatUtilView != null || delegate == null || !isOpenFloatUtil) {
      return;
    }
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !Settings.canDrawOverlays(context)) {
      Toast.makeText(context, "SoGameDebug The function can only be used with the permission to open the floating window", Toast.LENGTH_LONG).show();
      Intent intent = new Intent();
      intent.setAction(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
      intent.setData(Uri.parse("package:" + context.getPackageName()));
      context.startActivity(intent);
    }
    //Width height is set to the screen width. (there are horizontal and vertical screens in the game)
    WindowManager.LayoutParams layoutParams =
        new WindowManager.LayoutParams();
    layoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
    layoutParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
      layoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
    } else {
      layoutParams.type = WindowManager.LayoutParams.TYPE_PHONE;
    }
    layoutParams.format = PixelFormat.RGBA_8888;
    layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
        | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
    layoutParams.gravity = Gravity.END | Gravity.CENTER_VERTICAL;

    studyFloatUtilView = new StudyFloatUtilView(context);
    WindowManager windowManager = (WindowManager) context.getSystemService(WINDOW_SERVICE);
    if (windowManager == null) {
      return;
    }
    windowManager.addView(studyFloatUtilView, layoutParams);
  }

Official description of WindowManagerImpl

Since WindowManager itself is of interface type, we can directly see its implementation class WindowManagerImpl.

/**
 * Provides low-level communication with the system window manager for
 * operations that are bound to a particular context, display or parent window.
 * Instances of this object are sensitive to the compatibility info associated
 * with the running application.
 *
 * This object implements the {@link ViewManager} interface,
 * allowing you to add any View subclass as a top-level window on the screen.
 * Additional window manager specific layout parameters are defined for
 * control over how windows are displayed.  It also implements the {@link WindowManager}
 * interface, allowing you to control the displays attached to the device.
 *
 * <p>Applications will not normally use WindowManager directly, instead relying
 * on the higher-level facilities in {@link android.app.Activity} and
 * {@link android.app.Dialog}.
 *
 * <p>Even for low-level window manager access, it is almost never correct to use
 * this class.  For example, {@link android.app.Activity#getWindowManager}
 * provides a window manager for adding windows that are associated with that
 * activity -- the window manager will not normally allow you to add arbitrary
 * windows that are not associated with an activity.
 *
 * @see WindowManager
 * @see WindowManagerGlobal
 * @hide
 */

The author's summary is as follows:

  1. Provide the communication mode with the underlying system window manager.
  2. WindowManagerImpl implements the ViewManager interface, which is used to add views in the top window.
  3. Other parameters of WindowManager are used to control the display of window. WindowManagerImpl implements the WindowManager interface, which is used to control the display of the device interface.
  4. Generally, applications do not directly use WindowManager, but generally use Activity and Dialog.
  5. If you directly use windows manager, it is difficult to ensure that you do not make mistakes. For example, through activity The WindowManager obtained by getwindowmanager () is not allowed to add windows unrelated to this activity.

windowmanager.addview()

The key to the relationship between window and view is windows manager Addview () this method.
List the calling process first, so that readers can have a general understanding:

  • windowmanager.addview()
  • WindowManagerImpl.addview()
  • WindowManagerGlobal.addview()
  • ViewRootImpl.setView()

WindowManagerImpl.addview()

WindowManagerImpl.addView() always calls the addView() method of WindowManagerGlobal.

    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();

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

Official description of WindowManagerGlobal

/**
 * Provides low-level communication with the system window manager for
 * operations that are not associated with any particular context.
 *
 * This class is only used internally to implement global functions where
 * the caller already knows the display and relevant compatibility information
 * for the operation.  For most purposes, you should use {@link WindowManager} instead
 * since it is bound to a context.
 *
 * @see WindowManagerImpl
 * @hide
 */

The author's summary is as follows:

  1. It provides context with relevant interfaces with the underlying system window manager.
  2. This class is generally used to call objects that internally hold display and related compatibility information.
  3. For most cases, developers should not use WindowManagerGlobal directly, but use windowManager with context bound

WindowManagerGlobal.addView()

There are many codes in this method. Let's look at the codes related to the logic of exploration:

  1. Create ViewRootImpl and cache it in arrayList.
  2. Match the view with the corresponding WindowManager Layoutparams are cached in arrayList.
  3. Call viewrootimpl Setview method to trigger ui update.
    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>();

    public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
--------ellipsis

        ViewRootImpl root;
        
--------ellipsis
            root = new ViewRootImpl(view.getContext(), display);

            view.setLayoutParams(wparams);

            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);

            // do this last because it fires off messages to start doing things
            try {
                root.setView(view, wparams, panelParentView);
            } catch (RuntimeException e) {
                // BadTokenException or InvalidDisplayException, clean up.
                if (index >= 0) {
                    removeViewLocked(index, true);
                }
                throw e;
            }
        }
    }

ViewRootImpl.setView()

ViewRootImpl.setView has many source codes, so we only leave this part related to View.
There are two main logics here:

  1. ViewRootImpl.requestLayout()
    Update UI logic
  2. IWindowSession.addToDisplay()
    Add window to the system
    final IWindowSession mWindowSession;

    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
--------ellipsis
                requestLayout();
--------ellipsis
                    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(),
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mInputChannel);
--------ellipsis
    }

ViewRootImpl.requestLayout()

The subsequent call process of requestLayout() is long. The call stack is directly listed here. Interested readers can read the source code by themselves.

  • ViewRootImpl.requestLayout()
  • ViewRootImpl.scheduleTraversals()
  • ViewRootImpl.doTraversal()
  • ViewRootImpl.performTraversals()

The source code of performTraversals() is as follows.
Three of these methods will eventually call the view method, and the corresponding relationship is as follows:

  • performMeasure() will eventually call view measure()
  • performLayout() will eventually call view layout()
  • Finally, draw. Perform() will be called draw()
private void performTraversals() {  
--------ellipsis
        performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
--------ellipsis
        performLayout(lp, desiredWindowWidth, desiredWindowHeight);
--------ellipsis
        performDraw();
--------ellipsis
        }
        ......  
    }  
       

So far, we know that after windows manager calls addview, we will finally come to the logic of View update.

Continue to talk about activity and window

As mentioned earlier, the task stack called to windowManager in activity is:

  • ActivityThread.handleResumeActivity()
  • WindowManager.addView()

From this logic, we can also determine that the first time the View in the activity updates the UI is in handleResumeActivity().

Therefore, when we obtain the width and height attribute of View in onCreate, it will often be equal to 0, because the logic of View updating UI has not been executed in onCreate.

If you want to add "after UI update" logic to OnCreate, you can use ViewTreeObserver.
Readers interested in this can pay attention to another article by the author:
ViewTreeObserver listens to the status of the View

IWindowSession.addToDisplay()

The calling process of addToDisplay() is long. The calling process is directly listed here:

  • IWindowSession.addToDisplay() (cross process call)
  • Session.addToDisplay()
  • WindowManagerService.addWindow()
  • PhoneWindowManager.prepareAddWindowLw()

Session inherits iwindowsession Stub, known from iwindowsession Addtodisplay() is through IPC, and finally uses windowmanagerservice in the system process Addwindow() to implement the logic of updating window.

Talk about the writer's personal understanding. If you have any questions, please comment and correct them.
All UI update operations of View must be processed by the operating system in the system process before they can be displayed to the user through hardware.
Therefore, ViewRootImpl is a very important part. View can inform the operating system of the operation of updating UI through ViewRootImpl. Iwindowsession Addtodisplay () is one of the key calls.

We won't go further here. Readers interested in the relevant source code can take a look at it by themselves.

Continue exploring ViewRootImpl

Careful readers will find that there are still many unclear points about ViewRootImpl:

  • What is the relationship between ViewRootImpl and View?
  • What are the functions of ViewRootImpl?

Readers who are interested in this are welcome to pay attention to readers' follow-up articles, or you can read the source code by yourself.

Keywords: Java Android window view

Added by Renich on Thu, 17 Feb 2022 22:47:20 +0200