View needs to be attached to the carrier for display, and here the Activity is used as the carrier for analysis.
The Activity to create
//ActivityThread.java private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {/ / create ContextImpl ContextImpl appContext = createBaseContextForActivity (r); Activity activity = null; try { java.lang.ClassLoader cl = appContext.getClassLoader(); / / create the Activity Activity = mInstrumentation. NewActivity (cl, component getClassName (), r.i ntent); } try { if (activity ! Attach (appContext, this, getInstrumentation(), r.token, R.I.dent, app, r.intent, r.activityInfo, title, r.parent, r.embeddedID, r.lastNonConfigurationInstances, config, r.referrer, r.voiceInteractor, window, r.configCallback, r.assistToken); / / call the activity's onCreate method if (r.i sPersistable ()) {mInstrumentation. CallActivityOnCreate (activity, r.s Tate. r.persistentState); } else { mInstrumentation.callActivityOnCreate(activity, r.state); } } } return activity; }Copy the code
In this process, three main things were done:
- The Activity is instantiated
- The attach method is called for initialization
- Call the onCreate method to start loading the layout from the layout file and prepare the View for display.
PhoneWindow create
PhoneWindow is responsible for interaction with views. In the attach method create:
// activity.java final void attach() {// Create PhoneWindow mWindow = new PhoneWindow(this, window, activityConfigCallback); mWindow.setCallback(this); mWindow.setWindowManager( (WindowManager)context.getSystemService(Context.WINDOW_SERVICE), mToken, mComponent.flattenToString(), (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) ! = 0); }Copy the code
DecorView create
DecorView acts as a container for the View View. Create in the onCreate method:
//PhoneWindow.java @Override public void setContentView(int layoutResID) { if (mContentParent == null) { installDecor(); } if (! hasFeature(FEATURE_CONTENT_TRANSITIONS)) { mLayoutInflater.inflate(layoutResID, mContentParent); } } private void installDecor() { if (mDecor == null) { mDecor = generateDecor(-1); } else { mDecor.setWindow(this); } if (mContentParent == null) { mContentParent = generateLayout(mDecor); } } protected DecorView generateDecor(int featureId) { return new DecorView(context, featureId, this, getAttributes()); }Copy the code
Loading layout files
// Load the XML layout file mLayOutinflater.inflate (layoutResID, mContentParent); public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) { final Resources res = getContext().getResources(); final XmlResourceParser parser = res.getLayout(resource); try { return inflate(parser, root, attachToRoot); } finally { parser.close(); }}Copy the code
Display View (View wrootimpl)
After onCreate is called, the onResume method is called, which goes back to the handleResumeActivity method.
@Override public void handleResumeActivity() { //onResume final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason); //addView if (r.window == null && ! a.mFinished && willBeVisible) { r.window = r.activity.getWindow(); View decor = r.window.getDecorView(); ViewManager wm = a.getWindowManager(); WindowManager.LayoutParams l = r.window.getAttributes() wm.addView(decor, l); }Copy the code
The method does two main things:
- Call the onResume method
- Call WM’s addView method.
//WindowManagerGlobal.java
public void addView() {
synchronized (mLock) {
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
try {
root.setView(view, wparams, panelParentView);
}
}
}
public ViewRootImpl(Context context, Display display) {
mContext = context;
mWindowSession = WindowManagerGlobal.getWindowSession();
mThread = Thread.currentThread();
}
Copy the code
There are two variables worth paying attention to in this view wrootimPL:
- MWindowSession. The type is IWindowSession, a Binder object used for interprocess communication. Its implementation on the server side is Session, which can be used to complete WMS related work.
- MThread. The thread variable is set to the current thread, which is the thread when ViewRootImpl is instantiated. When the UI is updated by different threads, the current thread and mThread are determined to be equal, and if they are different, an exception will be thrown.
Viewrootimpl.java public void setView() {synchronized (this) {requestLayout(); / / call the WMS res = mWindowSession addWindow method. The addToDisplay (mWindow mSeq, mWindowAttributes, getHostVisibility (), mDisplay.getDisplayId(), mWinFrame, mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel); // Set this(ViewRootImpl) to parent view.assignParent(this); }}Copy the code
There are three main functions:
- Trigger drawing (including measurement, layout, drawing)
//ViewRootImpl.java @Override public void requestLayout() { if (! mHandlingLayoutInLayoutRequest) { checkThread(); mLayoutRequested = true; scheduleTraversals(); }} ->scheduleTraversals() ->performMeasure() performLayout() ->measure, layout, draw methodCopy the code
- WMS’s addWindow method is called with Binder
The addToDisplay method eventually assigns a Surface to the WMS process’s addWindow method, which is responsible for displaying the final interface and eventually drawing it onto the screen.
- Set ViewRootImpl to the parent of the decorView
After this setting, requestLayout will be able to find the ViewRootImpl all the way through the parent, and the ViewRootImpl will take care of all the views. The entire call process is:
View.requestLayout -> DecorView.requestLayout -> ViewRootImpl.requestLayout
//View.java public void requestLayout() { if (mParent ! = null && ! mParent.isLayoutRequested()) { mParent.requestLayout(); }}Copy the code
The relationship between Activity, PhoneWindow, DecorView, ViewRootImpl?
PhoneWindow: is the middle layer of interaction between an Activity and a View, helping the Activity manage the View.
DecorView: is the top View of all views, is the parent of all views.
ViewRootImpl: Used to handle view-related events, such as drawing, event distribution, and is the parent of a DecorView.
When was the four established?
The Activity is created in the performLaunchActivity method and is triggered at startActivity.
PhoneWindow, also created in the performLaunchActivity method, is more specifically the Attach method of the Activity.
DecorView, created in setContentView->PhoneWindow. InstallDecor.
The ViewRootImpl, created in the handleResumeActivity method, is finally created with addView.
When did the first drawing of the View take place?
The first drawing takes place in the handleResumeActivity method, creating ViewRootImpl with the addView method and calling its setView method.
Finally, a call to the requestLayout method begins the layout, measurement, and drawing process.
Thread update UI causes crash?
In requestLayout, which triggers drawing, there is a checkThread method:
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
Copy the code
The mThread is compared to the current thread. MThread is assigned when ViewRootImpl is instantiated.
So the reason for the crash is that the thread on which the view was drawn to the interface (i.e. the thread on which the ViewRootImpl was created) is not the same thread on which the UI was updated.