1. Window

val layoutParams = WindowManager.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE shl WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL shl  WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
windowManager.addView(imageView,layoutParams)
Copy the code
  • The Window type
  1. System-level Windows: System-level Windows can only be created with declared permissions, such as Toast
  2. Application-level Window: indicates the system’s active Window, such as Activity
  3. Child Window: attached to a parent Window, such as Dialog
  • FLAG
  1. FLAG_NOT_FOCUSABLE: indicates that focus is not captured and the event is eventually passed to the underlying Window
  2. FLAG_NOT_TOUCH_MODEL: Handles only click events in its zone, passing those outside the zone to the underlying Window
  3. FLAG_SHOW_WHEN_LOCKED: Enable this mode to display on the lock screen

2

Window is an abstract concept, which exists in the form of View. Each Window corresponds to View and ViewRootImpl. The ViewRootImpl is used to establish the connection between Window and View

2.1. Adding process of Window

The entire process of adding Windows can be performed in two parts:

  • WindowManager
  • WindowManagerService

The View is added by calling WindowManager.addView (). In fact, windowManager is just an interface that inherits from ViewManager. The real task in the activity is its implementation class WindowMangerImpl. So the method is executed to WindowMangerImp.addView (), but WindowMangerImpl is a clever class, and in addView(), in addition to verifying the validity of LayoutParams, It Bridges all work to Windows ManagerGlobal:

@Override public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) { applyDefaultToken(params); Mglobal.addview (view, params, McOntext.getdisplay (), mParentWindow); // Direct to WindowManagerGlobal}Copy the code
  • WindowManagerGlobal

Before implementing the methods, let’s take a look at the various collections in Windows Global (see comments below), which hold the state of the Window and View throughout the Window’s working life

private final ArrayList<View> mViews = new ArrayList<View>(); View private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>(); / / save all Window corresponding ViewRootImpl private final ArrayList < WindowManager. LayoutParams > mParams = / / Save all the Window corresponds to the WindowManager. LayoutParams new ArrayList < WindowManager. LayoutParams > (); private final ArraySet<View> mDyingViews = new ArraySet<View>(); // Save the View being deletedCopy the code
  • WindowManagerGlobal. AddView ()
root = new ViewRootImpl(view.getContext(), display); view.setLayoutParams(wparams); mViews.add(view); mRoots.add(root); mParams.add(wparams); root.setView(view, wparams, panelParentView); // View drawingCopy the code

Here’s part of the code in addView (), which does the following:

  1. Create an instance of ViewRootImpl
  2. Set the layout parameters of the View
  3. Save the View, root, and params in the collection, respectively

After saving the data, the View is actually executed by setView ()

  • ViewRootImpl. SetView ()

ViewRootImpl is the highest level in the View, the root of all views (ViewRootImpl is not a View, but implements the ViewParent interface), and implements the communication protocol between the View and WindowManager

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) { synchronized (this) { ...... requestLayout(); / / the View for the first time measuring and drawing res = mWindowSession addToDisplay (mWindow mSeq, mWindowAttributes, getHostVisibility (), mDisplay.getDisplayId(), mWinFrame, mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel); // Call WindowSession addTodiaplay () to add window}}Copy the code

RequestLayout () calls scheduleTraversals(), which gets the main thread’s Handler and sends a message to execute the TraversalRunnable instance, TraversalRunnable is an implementation of Runnable. OTraversal () is executed in run () and then performTraversals ().

  • PerformTraversals ()
PerformMeasure (childWidthMeasureSpec, childHeightMeasureSpec); int width = host.getMeasuredWidth(); int height = host.getMeasuredHeight(); boolean measureAgain =false;
                    if(lp.horizontalWeight > 0.0f) {width += (int) ((mwidth-width) * lp.horizontalWeight); childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY); measureAgain =true;
                    }
                    if(lp.verticalWeight > 0.0f) {height += (int) ((mheight-height) * lp.verticalWeight); childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY); measureAgain =true;
                    }

                    if(measureAgain) { performMeasure(childWidthMeasureSpec, childHeightMeasureSpec); }... final boolean didLayout = layoutRequested && (! mStopped || mReportNextDraw); boolean triggerGlobalLayoutListener = didLayout || mAttachInfo.mRecomputeGlobalAttributes;if(didLayout) { performLayout(lp, mWidth, mHeight); // Complete the View Layout}...... performDraw(); // Draw the ViewCopy the code

PerformMeasure, performLayout and performDraw are called from performMeasure, performLayout and performDraw. In these three methods, measure, layout and draw methods of View or ViewGroupde are respectively called to complete the measurement, layout and drawing of View;

  • WindowSession use IWindowSession Binder system call interface, internal call WindowManagerService. AddWindow () to add, At this point, all operations are performed in WindowManagerService. For details about how WindowManagerService works, see Android Window Management Analysis
@Override
 public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
         int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
         Rect outOutsets, InputChannel outInputChannel) {
     return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
             outContentInsets, outStableInsets, outOutsets, outInputChannel);
 }
Copy the code

2.2 Window deletion process

The removeView() method is the same as the removeView() method for Windows ManagerGlobal.

int index = findViewLocked(view, true); 
View curView = mRoots.get(index).getView(); 
removeViewLocked(index, immediate);
Copy the code

RemoveView () performs three main steps:

  1. Gets the index of the current operation View
  2. Get the ViewRootImpl instance saved in mRoots
  3. Call removeViewLocked to perform the delete
  • RemoveViewLocked () : Get the View to be deleted and delete it
ViewRootImpl root = mRoots.get(index); View view = root.getView(); // Get ViewRootImpl View Boolean deferred = root.die(immediate); // Call die () to delete View mdyingviews.add (View); // Add the View to be deleted to mDyingViewsCopy the code
  • Die () : Sends a delete message
boolean die(boolean immediate) {
if(immediate && ! mIsInTraversal) {doDie(); 
    return false;
}
    mHandler.sendEmptyMessage(MSG_DIE); 
    return true;
}
Copy the code

Perform synchronous or asynchronous deletion based on the immediate passed in the die () method:

  1. Synchronous delete: Perform the delete directly by calling the doDie() method
  2. Asynchronous delete: send Handler message to call doDie ()
  • DoDie () : Actually deletes the View
mView.dispatchDetachedFromWindow();  
mWindowSession.remove(mWindow); 
mView.onDetachedFromWindow();
WindowManagerGlobal.getInstance().doRemoveView(this);  
Copy the code

DoDie is where the deletion is actually initiated, as follows:

  1. Call with mWindowSession eventually call WindowMangerService. RemoveWindow ()
  2. Call the View’s onDetachedFromWindow () to remove the View
  3. Remove View information saved in mRoots, mParams, and mDyingView

2.3 Window update process

  • WindowManagerGlobal. UpdateViewLayout ()
public void updateViewLayout(View view, ViewGroup.LayoutParams params) { final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params; view.setLayoutParams(wparams); // set new LyaoutParams synchronized (mLock) {int index = findViewLocked(view,true); ViewRootImpl root = mRoots.get(index); Mparams.remove (index); // Update mparams.remove (index); mParams.add(index, wparams); // Replace wparams root.setLayoutParams(wparams,false); // Update View}}Copy the code

Root. setLayoutParams will trigger scheduleTraversals of ViewRootImpl to measure, arrange, and draw views.

3, the instance,

3.1. Add Window in the Activity

  1. PhoneWindow: the Activity window of the Activity
  2. DecorView: The root View of all views, containing the title bar and content
  3. ContentView: The layout file to which the layout container Settings are loaded
  • The creation of a window
  1. In the Activity. The attach () method is used in PolicyManager. MakeNewWindow create PhoneWIndow ()
 mWindow = new PhoneWindow(this, window, activityConfigCallback);
        mWindow.setWindowControllerCallback(this);
        mWindow.setCallback(this);
        mWindow.setOnWindowDismissedCallback(this);
        mWindow.getLayoutInflater().setPrivateFactory(this);
Copy the code
  • Window Settings view
/ / the Activity of public voidsetContentView(@LayoutRes int layoutResID) { getWindow().setContentView(layoutResID); // Call PhoneWindowsetContentView (which happens) initWindowDecorActionBar (); }Copy the code

Here getWindow () yields the PhoneWindow created earlier, so setContentView () is ultimately executed in PhoneWindow

public void setContentView(int layoutResID) {
      if (mContentParent == null) {
            installDecor();
        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            mContentParent.removeAllViews();
        }
}
Copy the code

The setContentView() method determines whether the contentParent is empty. If it is, installDecor() is executed. There are two obvious things in installDecor() that initialize the DecorView and mContentParent. Let’s look at each of these methods

  • GenerateDecor () : Creates a DecorView instance
protected DecorView generateDecor(int featureId) {
    returnnew DecorView(context, featureId, this, getAttributes()); // Initialize the DecorView, just a FrameLayout}Copy the code
  • GenerateLayout () : loads the layout file and initializes mContentParent
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource); ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT); / / load DecorView layout of the content container public static final ints ID_ANDROID_CONTENT = com. Android. Internal. R.i, dc ontent; / / the content of the id / / mDecor onResourcesLoaded () final View root = inflater. Inflate (layoutResource, null); / / load the original layout file: contains the title bar, and the content addView (root, new ViewGroup. MarginLayoutParams (MATCH_PARENT, MATCH_PARENT)); // Add the loaded layout to the DecorViewCopy the code

The layout file is loaded in generateLayout with the following details:

  1. Load the property values in getWindowStyle
  2. According to the style of setting initial Layout WindowManager. LayoutParams layoutResource and select the Layout of the system resources
  3. Set the background, title, color, and other states of the DecorView
  4. Then call mDecor. OnResourcesLoaded () to load the layoutResource into the DecorView
  5. Gets the contentParent container in the layout based on the resource ID
  • Add the View to the contentParent container of the DecorView
@Override
public void setContentView(int layoutResID) { mLayoutInflater.inflate(layoutResID, mContentParent); // Load the layout into mContentParent}Copy the code
  • After loading the layout, call onContentChange () to notify the Activity that it has loaded
final Callback cb = getCallback();
if(cb ! = null && ! isDestroyed()) { cb.onContentChanged(); }Copy the code

The DecorView and contentParent initialization are complete, a layout with TitleView and ContentView is loaded in the DecorView, and the loaded layoutResID has been loaded into the ContentView. So the work inside the DecorView is done, but the DecorView is not added to the Window, so the interface is still invisible at this point

  • DecorView added to Window ()

ActivityThread’s handleResumeActivity () calls the Activity’s makeVisible () method, A call to WindowManager.addView() in makeVisible adds a DecorView to the PhoneWindow, and the layout resource is displayed on the screen

//handleResumeActivity
if (r.activity.mVisibleFromClient) {
    r.activity.makeVisible();
}

void makeVisible() {
    if(! mWindowAdded) { ViewManager wm = getWindowManager(); wm.addView(mDecor, getWindow().getAttributes()); // Add the DecorView to the PhoneWindow mWindowAdded =true; } mDecor.setVisibility(View.VISIBLE); // Set DecorView visible}Copy the code

Add Window to Dialog

  • Use the Dialog
val dialog = Dialog(this,R.style.Base_ThemeOverlay_AppCompat_Dialog)
dialog.setContentView(R.layout.dialog)
dialog.show()
dialog.cancel()
Copy the code
  • Create a Window

As you can see from the previous example, both dialog and Activity use setContentView when setting their layout, so their initialization is the same as that of an Activity, except when a DecorView is added to the Window

  • Add the DecorView to the Window
public void show() {// Add mWindowManager.addView(mDecor, l) to Window when Dialog is displayed; // Add DecorView}Copy the code
  • Remove the DectorView through WindowManager when dialog closes
mWindowManager.removeViewImmediate(mDecor);
Copy the code

3.3. Create Windows in Toast

  • use
Toast.makeText(this,"Toast",Toast.LENGTH_SHORT).show()
Copy the code
  • MakeText () : The makeText performs the file loading and setting of Toast
Toast result = new Toast(context, looper); // Create Toast instance, Loop LayoutInflater inflate = (LayoutInflater) context.getSystemService(context.layout_inflater_service); View v = inflate.inflate(com.android.internal.R.layout.transient_notification, null); / / load the Toast and set the View layout TextView TV = (TextView) v. indViewById (com) android. Internal. R.i d.m essage); tv.setText(text); Result. mNextView = v; // Copy to mNextView result.mDuration = duration; // Set the pop-up duration of Toastreturn result;
Copy the code
  • Toast the display of the
INotificationManager service = getService();
String pkg = mContext.getOpPackageName();
TN tn = mTN;
tn.mNextView = mNextView;

service.enqueueToast(pkg, tn, mDuration);
Copy the code

A few comments on the above method:

  1. Service is the proxy class for INotificationManager, in this case IPC communication;
  2. TN is the proxy class for ITransientNotification
  3. MNextView is the View loaded by Toast
  4. Service.enqueuetoast () adds Toast to the message queue

Toast finally calls the show method in TN. Show () sends a Message to Handle and then calls handleShow ().

public void handleShow(IBinder windowToken) {
handleHide();
mParams.x = mX;
mParams.y = mY;
mParams.verticalMargin = mVerticalMargin;
mParams.horizontalMargin = mHorizontalMargin;
mParams.packageName = packageName;
mParams.hideTimeoutMilliseconds = mDuration ==
    Toast.LENGTH_LONG ? LONG_DURATION_TIMEOUT : SHORT_DURATION_TIMEOUT;
mParams.token = windowToken;

mWM = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
mWM.addView(mView, mParams);
}
Copy the code

Do the following in handleShow () :

  1. Call handleHide() to hide the previous Toast
  2. Set mParams parameters for Toast, such as coordinates and mDuration
  3. Call WindowManager addView () to add a View

How does WindowManagerService perform additions and operations on Windows?