The questions we will answer in this article are:
- How does setContentView work?
- What is the reason why activities are displayed after onResume?
- What is ViewRoot? Is it the rootView of ViewTree?
First, the overall flow chart
First map:
To outline the process (and again at the end of this article) :
- create
PhoneWindow
Object toPhoneWindow
Object to add aDecorView
- for
DecorView
Bind aViewRootImpl
By thisViewRootImpl
To be responsible for theView
Draw and refresh ViewRootImpl
throughIWindowSession
toWMS
Initiate a Binder call, andWMS
Also throughIWindow
Initiate a call to the applicationViewRootImpl
Will be inWMS
Inside register a window, and then byWMS
Manages the size, location, and hierarchy of all Windows uniformly- So the first time I drew it,
ViewRootImpl
I will also apply for one from WMSSurface
(WMS
toSurface
Application), got itSurface
After that, the application is ready to draw - Once I’ve drawn it on the application side,
SurfaceFlinger
Will be in accordance with theWMS
The hierarchy and other information provided inside the composite, the final display
In order to let you in the tracking source will not get lost, first with you popular science road on the road of the several “guard brother” :
PhoneWindow
:Window
The only implementation class on mobile,WMS
Management is one by oneWindow
Rather thanView
DecorView
: View on the top, we usuallysetContentView
Set up theView
That corresponds to the blue oneContentView
.ViewRootImpl
: This guy isView
andWMS
Communication bridge, every timeView
Want to talk toWMS
Communication goes through it; Every timeWMS
Want to makeView
Updates also go through it; aDecorView
Corresponds to aViewRootImpl
SurfaceFlinger
: be responsible forSurface
The composition of, one pieceSurface
It’s just a canvas, and the application side is actually inSurface
Graph onWindowManagerService
: What we always sayWMS
, mainly responsible for the management window,I’m not responsible for drawing the view. The following is aWMS
The main functions of:
By the way, THE source code I use is Android 28.
Let’s start with Action, go
Second, source code analysis
2.1 the setContentView
// Class: --> view.java
public void setContentView(int layoutResId){ getWindow().setContentView(layoutResId) ; . }/ / class: -- -- -- -- > PhoneWindow. Java
public void setContentView(int layoutResID) {
if (mContentParent == null) { installDecor(); }... mLayoutInflater.inflate(layoutResID, mContentParent); . }Copy the code
You can see that the loading layout is done through the mLayOutInflater.inflate (layoutResID, mContentParent), which has two parameters: layoutId and mContentParent, What is this mContentParent?
MContentParent is initialized via installDecor().
2.2 installDecor
-- - > categories: PhoneWindow.java
private void installDecor(a) {
if (mDecor == null) {
//1、new 一个 DecorView
mDecor = generateDecor(-1); . }if (mContentParent == null) {
//2mContentParent = generateLayout(mDecor); . }}//1、new 一个 DecorView
protected DecorView generateDecor(int featureId) {...return new DecorView(context, featureId, this, getAttributes());
}
//2
protected ViewGroup generateLayout(DecorView decor) {...// Inflate the window decor.
//3, inflate layout, and add it to the decorView
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
//4, find contentView, returnViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT); .return contentParent;
}
///----->DecorView.java
void onResourcesLoaded(LayoutInflater inflater, int layoutResource){
/ / 3.
final View root = inflater.inflate(layoutResource, null); .// Put it below the color views.
addView(root, 0.new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
}
Copy the code
Let’s take a look at the process. The above code mainly does:
- Create a DecorView that is essentially a
FrameLayout
- Inflate a layout root
- Add root to the decorView
- through
findViewById
Find the contentParent, which is actually the one with ID R.D.C. tentView
GetWindow ().requestFeature(window.feature_no_title); Why do setContentView() precede setContentView()? Check out the installDecor() method
At this point, is the entire interface loaded? Too naive, this step only initializes the View Tree, the whole interface is not displayed yet, the interface is really displayed depends on what is done in the onResume() lifecycle. Keep following
2.3 handleResumeActivity (IBinder token)
-- -- -- -- > categories: ActivityThread. Java@Override
public void handleResumeActivity(IBinder token,..){
// TODO Push resumeArgs into the activity for consideration
/ / 1.
final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
final Activity a = r.activity;
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;
if (a.mVisibleFromClient) {
if(! a.mWindowAdded) { a.mWindowAdded =true;
/ / 2,
wm.addView(decor, l);
} else{... }}if(! r.activity.mFinished && willBeVisible && r.activity.mDecor ! =null && !r.hideForNow) {
...
if (r.activity.mVisibleFromClient) {
/ / 3.r.activity.makeVisible(); }}}Copy the code
The whole process is divided into three steps:
- The callback
Activity
theonResume()
methods - call
WindowManager
theaddView()
methods - Set up the
Activity
forvisible
The next thing we’re going to focus on is the addView() method. Why is that? Since the first step is the callback and the third step is to make it visible, the process that triggers the interface drawing is in the second step. So, we’re going to track step two.
2.4 wm. AddView (decor, l)
---->WindowManagerImpl.java
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
----> WindowManagerGlobal.java
public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) {... ViewRootImpl root;synchronized (mLock) {
root = newViewRootImpl(view.getContext(), display); .// do this last because it fires off messages to start doing things
Call this method last because it sends a message to start work
try{ root.setView(view, wparams, panelParentView); }... }}Copy the code
This method does two things:
- To create the
ViewRootImpl
Object (so,ViewRootImpl
andWindow
The relationship is one to one. - call
ViewRootImpl
The method setView
2.5 viewRootImpl.setView
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
if (mView == null) {
mView = view;
// 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.
requestLayout();
try{... res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes, getHostVisibility(), mDisplay.getDisplayId(), mWinFrame, mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel); . }... }}Copy the code
The setContentView method focuses on a few things:
- to
mView
The assignment - call
requestlayout
- call
windowSession
theaddToDisplay
The first fork in the road comes when we separate the requestLayout () method from the addToDisplay() method.
2.5.1 RequestLayout ()
----> ViewRootImpl.java
@Override
public void requestLayout() {
if(! MHandlingLayoutInLayoutRequest) {/ / check whether it is the main thread. The next time the interviewer asks you where the UI cannot update // exceptions in child threads are thrown, don't be afraid to tell the interviewer, in ViewRootImpl // requestLayout() checkThread(); mLayoutRequested =true;
//1
scheduleTraversals();
}
}
//1
void scheduleTraversals() {
if(! mTraversalScheduled) { ... mChoreographer.postCallback( Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); . }}Copy the code
In this step, a callback will be posted, which will be called the next time a Vsync signal arrives (see this article on how to callback to the Java layer the next time a Vsync signal arrives).
Anyway, when the Vsync signal arrives, the mTraversalRunnable run method will eventually be executed.
----> ViewRootImpl.java
final class TraversalRunnable implements Runnable {
@Override
public void run(a) { doTraversal(); }}void doTraversal(a) {... performTraversals(); . }private void performTraversals(a) {... relayoutResult = relayoutWindow(params, viewVisibility, insetsPending); .// Draw trilogy: Measure, layout, drawperformMeasure(childWidthMeasureSpec, childHeightMeasureSpec); . performLayout(lp, mWidth, mHeight); . performDraw(); . }Copy the code
Measure, layout, draw these methods we are familiar with, so we focus on relayoutWindow this method to do what? Why is it placed before Measure, layout and DRAW?
private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
boolean insetsPending) throws RemoteException {
// When RemoteException is seen, it is assumed that there is ipc communication.intrelayoutResult = mWindowSession.relayout(... , mSurface); . }Copy the code
In fact, this step is a ———— to WMS to apply for a Surface.
Isn’t there a Surface already? Isn’t that the mSurface?? (Black question mark face ❓)
In fact, this mSurface is an empty Surface, it is just created, but has not allocated memory space, it is still blank.
public final Surface mSurface = new Surface();
Copy the code
The application side needs to call relayoutWindow of WMS to assign value to Surface. I will not analyze the transfer and assignment process of Surface.
After Surface assignment is successfully initialized, the client is ready to draw. When the drawing is complete, unlockAndPostCanvas() is called to tell SurfaceFlinger to compose.
Now that the requestLayout() method is done, go to another fork in the road.
Points forks in the road 2.5.2 windowSession. AddToDisplay ()
WindowSession is an AIDL, which is used to communicate between applications and WMS. Its real implementation class is Session, specifically looking for the process as follows:
---> Session.java
@Override
public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,...) {
// Note that IWindow is a WMS call to Binder
//Session passes it to WMS so that WMS can initiate calls to the application
return mService.addWindow(this, window, seq, attrs, ...) ; }Copy the code
As you can see, the addView method of WMS is finally called. The function of this method is to register Window objects with WMS, and then WMS coordinates the hierarchy, size, and position of these Windows.
For WMS, it does not care about the Window object or View object of the application itself. For WMS, one of its important functions is to assign Surface to the Window of the application and control the display order, position and size of these surfaces.
When the application is finished drawing the Surface, SurfaceFlinger will synthesize the image data according to the size, level and position provided by WMS, and finally write it into the screen buffer and draw it.
2.6 summary
handleResumeActivity()
Third, summary
To summarize, the whole process is complete:
- To create the
PhoneWindow
Object toPhoneWindow
Object to add aDecorView
- for
DecorView
Bind aViewRootImpl
By thisViewRootImpl
To be responsible for theView
Draw and refresh ViewRootImpl
throughIWindowSession
toWMS
Initiate a Binder call, andWMS
Also throughIWindow
Initiate a call to the applicationViewRootImpl
Will be inWMS
Inside register a window, and then byWMS
Manages the size, location, and hierarchy of all Windows uniformly- So the first time I drew it,
ViewRootImpl
I will also apply for one from WMSSurface
, with theSurface
After that, the application is ready to draw - And when I’ve drawn it,
SurfaceFlinger
Will be in accordance with theWMS
The hierarchy and other information provided inside the composite, the final display
Iv. Problem solving
setContentView
What is the principle of?
The principle of setContentView() is that (1) you create a DecorView and ViewRootImpl and bind them together, and (2) you create a ViewTree through the inflate method, which is not displayed yet
Activity
inonResume
What’s the reason why it’s not shown until later?
The onResume method calls ViewRootImpl’s performTraversal() method for rendering the interface and makeVisible for displaying the interface;
ViewRoot
What is it? Is it the RootView of ViewTree?
View wroot, or its implementation class View Wroot PL, has nothing to do with views. The view wroot is only the manager of the ViewTree, not the root node of the ViewTree. The real root node is the DecorView.
5. Reference documents
- In-depth understanding of control systems
- Lesson for video
- Android Window mechanism
Dregs programmer a, uneducated, mistakes and omissions please forgive me ~