Window should be clear, it is an abstract class, the concrete implementation class is PhoneWindow, it manages the View. WindowManager is an interface class that inherits from ViewManager and is named for managing Windows. Its implementation class is WindowManagerImpl. If you want to add, update, or delete Windows, you can use Windows Manager. Windows Manager hands over the work to WMS. Windows Manager and WMS communicate across processes with Binder.
Window and WindowManager
To implement a procedure for adding Windows through WIndowManager:
Button mFloatingButton = new Button(this);
mFloatingButton.setText("button");
WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT, 0.0, PixelFormat.TRANSPARENT);
layoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION;
layoutParams.flags= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
layoutParams.gravity= Gravity.LEFT|Gravity.TOP;
layoutParams.x=100;
layoutParams.y=300;
WindowManager windowManager = getWindowManager();
windowManager.addView(mFloatingButton,layoutParams);
Copy the code
Add a Button to the screen coordinate of (100,300). Focus on the two attributes Type and Flag, which are important parameters.
The Type attribute
Window refers to the type of Window, which is divided into three types: application Window, child Window and system Window.
- The application class Window corresponds to an Activity.
- A child Window cannot stand alone, it needs to be attached to a particular parent WIindow (Dialog is a child Window)
- A system Window is a Window that you need to declare permission to create (Toast and status bar are system Windows).
The Window type | Scope of the hierarchy |
---|---|
Window | 1~99 |
The child Window | 1000 ~ 1999 |
The WIndow system | 2000 ~ 2999 |
Flag properties
The Window Flag is used to control the display of the Window. It is also defined in the WindowManager inner class LaoyoutParams.
Flag | describe |
---|---|
FLAG_ALLOW_LOCK_WHITE_SCREEN_ON | Lock the screen on the open screen as long as the window is visible |
FLAG_NOT_FOCUSABLE | Windows don’t get input focus, so FLAG_NOT_TOUCH_MODAL is also set when you set the change flag |
FLAG_NOT_TOUCHABLE | The window does not receive any touch events |
FLAG_NOT_TOUCH_MODAL | It passes touch events outside the Window area to other Windows, and only handles touch events inside the Window area itself |
FLAG_KEEP_SCREEN_ON | The screen stays on as long as the window is visible |
FLAG_LAYOUT_NO_LIMITS | Allows Windows to go beyond the screen |
FLAG_FULLSCREEN | Hide all screen decoration Windows, such as full screen playback in games and players |
FLAG_SHOW_WHEN_LOCKED | Windows can be displayed above the lock screen window |
FLAG_IGNORE_CHEEK_PRESSES | This event does not respond when the user’s face is close to the screen, such as when making a phone call |
FLAG_TURN_SCREEN_ON | Light up the screen when the window is displayed |
The internal operation of Window
Each Window corresponds to a View and a ViewRootImpl. The connection between Window and View is established through ViewRootImpl. Therefore, Window does not exist in time, it exists in the form of View. WindowManager manages Windows, and when it comes to managing Windows, it adds, updates, and deletes them.
// All operations are performed on the View, indicating that the View is the entity existing in the Window
public interface ViewManager
{
public void addView(View view, ViewGroup.LayoutParams params);
public void updateViewLayout(View view, ViewGroup.LayoutParams params);
public void removeView(View view);
}
Copy the code
Windows MangerImpl does not directly implement the three operations of Windows, but all are given to Windows ManagerGlobal.
### WindowManagerImpl
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
@Override
public void removeView(View view) {
mGlobal.removeView(view, false);
}
@Override
public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.updateViewLayout(view, params);
}
Copy the code
Window adding process
In Windows ManagerGlobal addView, you create a ViewrotimPL and add a View to the list, update the interface and complete the Window addition process with viewrotimPL.
# # #WindowManagerGlobal
public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) {
// Check if the parameters are valid. If it is a child Window, some layout parameters need to be adjusted
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"); }... ViewRootImpl root; View panelParentView =null;
synchronized (mLock) {
......
// Create ViewRootImpl and add the View to the list
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
//mViews stores all views corresponding to Windows
mViews.add(view);
//mRoots stores the ViewRootImpl for all Windows
mRoots.add(root);
mParams.add(wparams);
try {
// Update the interface via ViewRootImpl and complete the Window addition process
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
// BadTokenException or InvalidDisplayException, clean up.
if (index >= 0) {
removeViewLocked(index, true);
}
throwe; }}}Copy the code
Call requestLayouot and finally add Window via WindowSession. MWIndowSession is of type IWindowSession, a Binder object whose real implementation class is Sessiion. The addition of the Window is an IPC call.
# # #ViewRootImpl
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {...// Arrange the first layout before adding it to the window manager to ensure that we rearrange it before receiving any other events from the system.requestLayout(); .try {
mOrigWindowType = mWindowAttributes.type;
mAttachInfo.mRecomputeGlobalAttributes = true;
collectViewAttributes();
// The process of adding Windows is finally completed through WindowSession
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(), mWinFrame,
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, 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();
}
}
}
### ViewRootImpl
public void requestLayout(a) {
if(! mHandlingLayoutInLayoutRequest) { checkThread(); mLayoutRequested =true;
// This is actually the View drawing entryscheduleTraversals(); }}Copy the code
Windows ManagerService is used to add Windw within the Session.
### WindowManagerService
@Override
public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
Rect outOutsets, InputChannel outInputChannel) {
// Leave it to it
return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
outContentInsets, outStableInsets, outOutsets, outInputChannel);
}
Copy the code
The addToDisplay method internally calls the addWindow method of WMS and passes in its own Session. Each application will have a Session, and WMS will store it in an ArrayList, so WMS will do all the work.
/ / WMS added
The Window deletion process
Windows ManagerGlobal removeView implementation
# # #WindowManagerGlobal
public void removeView(View view, boolean immediate) {
if (view == null) {
throw new IllegalArgumentException("view must not be null");
}
synchronized (mLock) {
// Find the index of the View to be deleted
int index = findViewLocked(view, true);
View curView = mRoots.get(index).getView();
// Delete further
removeViewLocked(index, immediate);
if (curView == view) {
return;
}
throw new IllegalStateException("Calling with view " + view
+ " but the ViewAncestor is attached to "+ curView); }}Copy the code
RemoveView and removeViewImmediate are two delete interfaces provided in Windows Manager, indicating asynchronous and synchronous deletion respectively. RemoveViewLocked is used to perform further deletes, which are performed by the ViewRoootImpl’s Die method.
# # #WindowManagerGlobal
private void removeViewLocked(int index, boolean immediate) { ViewRootImpl root = mRoots.get(index); View view = root.getView(); .// Delete using ViewRoootImpl
boolean deferred = root.die(immediate);
if(view ! =null) {
view.assignParent(null);
if(deferred) { mDyingViews.add(view); }}}Copy the code
In the die method, doDie() is called directly for synchronous deletes, and an MSG_DIE message is sent for asynchronous deletes. In doDie approach, eventually call dispatchDetachedFromWindow () to actually delete the View.
# # #ViewRootImpl
boolean die(boolean immediate) {
// Sync delete calls doDie directly
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());
}
// Async remover sends an MSG_DIE message, still calls doDie()
mHandler.sendEmptyMessage(MSG_DIE);
return true;
}
### ViewRootImpl
// Both end up calling the doDie method
void doDie(a) {
checkThread();
if (LOCAL_LOGV) Log.v(mTag, "DIE in " + this + " of " + mSurface);
synchronized (this) {
if (mRemoved) {
return;
}
mRemoved = true;
if (mAdded) {
// The real logic for deleting a View is implemented inside itdispatchDetachedFromWindow(); }... mAdded =false;
}
WindowManagerGlobal.getInstance().doRemoveView(this);
}
Copy the code
Windows update process
So what does updateViewLayout do
# # #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 View's LayoutParams and replace the old LayooutParams
view.setLayoutParams(wparams);
synchronized (mLock) {
int index = findViewLocked(view, true);
ViewRootImpl root = mRoots.get(index);
mParams.remove(index);
mParams.add(index, wparams);
// Update LayoutParams in ViewRootImpl
root.setLayoutParams(wparams, false); }}Copy the code
The ViewRootImpl setLayoutParams(wParams, False) method finally calls the ViewRootImpl schedultTraversals method to rearrange the View, including measurement, layout, and redraw. The Window view is also updated via WindowSession, which is finally implemented by relayoutWindow of WindowManagerService.
# # #ViewRootImpl
void scheduleTraversals(a) {
if(! mTraversalScheduled) { mTraversalScheduled =true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
// It is used to receive a new VSYN number from the display system and perform some operations during the next frame rendering.
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
Copy the code
The Window of the Activity creates the add procedure
No matter what kind of window, its addition process is basically similar in the WMS processing part, here takes the most typical application window Activity as an example.
When an Activity is started, a new process is created if the process in which the Activity is started does not already exist. The new process will run the ActivityThread instance that represents the main thread. PreformLaunchActivity () in ActivityThread completes the entire startup process.
Inside this method, an instance object of the Activity is created through the class loader and the attach method is called for a series of associated operations.
# # #ActivityThread
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {...try {
Application app = r.packageInfo.makeApplication(false, mInstrumentation); .if(activity ! =null) {... appContext.setOuterContext(activity);// Call attch to bind window
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);
if(customIntent ! =null) { activity.mIntent = customIntent; }... }}Copy the code
In the Attach method of the Activity, the system creates the Window to which the Activity belongs and sets the callback interface for it. Because the Activity implements the Window’s Callback interface, the Activity method is called back when the Window receives a state change.
###Activity.attach
// Create PhoneWindow with unique implementation class
mWindow = new PhoneWindow(this, window, activityConfigCallback);
mWindow.setWindowControllerCallback(this);
// Pass the window callback to bind
mWindow.setCallback(this);
mWindow.setOnWindowDismissedCallback(this);
mWindow.getLayoutInflater().setPrivateFactory(this);
if(info.softInputMode ! = WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) { mWindow.setSoftInputMode(info.softInputMode); }if(info.uiOptions ! =0) {
mWindow.setUiOptions(info.uiOptions);
}
Copy the code
And since the Actiivity view calls the setContentView method to show it, let’s now look at the setContentView method.
### Activity
// Add all top-level views to the activity
public void setContentView(@LayoutRes int layoutResID) {
// The implementation is left to Window
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
Copy the code
The implementation of phoneWndow is left to The window, and the implementation of phoneWndow is PhoneWindow’s setConotentView method.
# # #PhoneWindow
public void setContentView(int layoutResID) {
if (mContentParent == null) {
/ / DecorView creation
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 {
// Add the Activity's layout file
mLayoutInflater.inflate(layoutResID, mContentParent);
}
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
if(cb ! =null && !isDestroyed()) {
// The onContentChanged method of the callback Activity notifies the Activity view that it has changed
cb.onContentChanged();
}
mContentParentExplicitlySet = true;
}
Copy the code
The DecorView creation is done by the installDecor method. Inside the method, the DecorView is created directly through the generateDecor method. GenerateLayout is also needed to load the specific layout file into the DecorView.
### PhoneWindow.installDecor
if (mDecor == null) {
// Create a blank FrameLayout
mDecor = generateDecor(-1);
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
mDecor.setIsRootNamespace(true);
if(! mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures ! =0) { mDecor.postOnAnimation(mInvalidatePanelMenuRunnable); }}else {
mDecor.setWindow(this);
}
if (mContentParent == null) {
// Use this method to load the concrete layout file into the DecorViewmContentParent = generateLayout(mDecor); . } ### PhoneWiindow.generateDecor
protected DecorView generateDecor(int featureId) {
// This is a blank FrameLayout
return new DecorView(context, featureId, this, getAttributes());
}
### PhoneWiindow.generateLayout
protected ViewGroup generateLayout(DecorView decor) {...// The ViewGroup corresponding to ID_ANDROID_CONTENT is mContentParent
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
if (contentParent == null) {
throw new RuntimeException("Window couldn't find content container view"); }...return contentParent;
}
Copy the code
Once you’ve created the DecorView and PhoneWindow, the next step is to add the Activity view directly to the DecorView’s mContentParent. Let’s go back to the setContentView method and look at a line of code:
### PhoneWindow.setContentView
// Add an XML view
mLayoutInflater.inflate(layoutResID, mContentParent);
Copy the code
At this point, the Activity layout file is added to the DecorView, but the DecorView is still not added to the Window by The WindowManager, The final screen display is in the ActiivtyThread’s handleResumeActivity method, which is called after onResume, and the Activity view is displayed.
###ActivityThread.handleResumeActivity
public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
String reason) {...if (r.window == null && !a.mFinished && willBeVisible) {
r.window = r.activity.getWindow();
View decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
l.softInputMode |= forwardBit;
if (r.mPreserveWindow) {
a.mWindowAdded = true;
r.mPreserveWindow = false;
ViewRootImpl impl = decor.getViewRootImpl();
if(impl ! =null) { impl.notifyChildRebuilt(); }}if (a.mVisibleFromClient) {
if(! a.mWindowAdded) { a.mWindowAdded =true;
// The Decorview really does both the add and display
// The wm.addView method is familiar
wm.addView(decor, l);
} else{ a.onWindowAttributesChanged(l); }}}... }Copy the code
View refresh process
The add and update of the Window will eventually go to the scheduleTraversals() method. What does that do?
# # #ViewRoootImpl
void scheduleTraversals(a) {
if(! mTraversalScheduled) { mTraversalScheduled =true;
// Add a synchronization barrier
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
// This added callback will be executed on the next frame render
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
Copy the code
When a drawing task is submitted, a synchronization barrier is added so that the asynchronous task executes first and then waits for the Vsyn callback to start drawing the next frame. The add a callback refers to the type of TraversalRunnabl mTraversalRunnable, in mChoreographer. PostCallback sends an asynchronous message delay is carried out.
### ViewRootImpl
final class TraversalRunnable implements Runnable {
@Override
public void run(a) { doTraversal(); }}void doTraversal(a) {
if (mTraversalScheduled) {
mTraversalScheduled = false;
// Remove the synchronization barrier
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
if (mProfile) {
Debug.startMethodTracing("ViewAncestor");
}
// Start drawing
performTraversals();
if (mProfile) {
Debug.stopMethodTracing();
mProfile = false; }}}Copy the code
This method starts by removing the synchronization barrier and then calls performTraversals(), enabling the View to draw, and then performMeasure, performLayout, and performDraw.
Summarize the refresh principle:
Both the View requestLayout and ViewRootImpl##setView will eventually call the ViewRootImpl requestLayout method. The scheduleTraversals method is then used to submit the drawing task, and the DisplayEventReceiver is used to request the vsync signal from the bottom layer. When the vsync signal comes in, it is called back by JNI. An asynchronous task is then posted to the message queue via Handler, the ViewRootImpl is used to perform the rendering task, and the performTraversals method is called to complete the rendering.
Finally the performTraversals() method triggers the rendering of the View. Inside the method, call performMeasure(), performLayout(), performDraw(), the View measure, layout, draw process, distributed from the top View down.
There’s a lot more to View refresh, and I’ll cover that later.
reference
Exploring the Art of Android Development