On paper come zhongjue shallow, must know this to practice

Zero, preface,

There are many articles on the Internet to introduce View loading, but fate is predetermined, happiness is their own

You don’t always know what the author is thinking about in an article. Articles can only guide thoughts. A lot of things without their own thoughts processing, it is difficult to enter the brain. This is especially true of complex layers of principle. If you don’t draw a few pictures, after a month, your article won’t look like it was written by yourself…

This paper focus on
1. When, where and how are Windown objects implemented? 2. How and when were some of the core views implemented in PhoneWindow? 3. When to add View on Window, where to implement ViewRootImpl? 4. How does ViewRootImpl handle views and how does it relate to the measurement, layout, and drawing of views? 5. As a View drawing fan, I need to know where the Canvas object of the View's OnDraw comes from. 6. How do LayoutInflaters load layouts?Copy the code

A, introducing

1. Analogies to divergence

In the case of my phone, on a physical level, the View is a screen made up of 2,430 x 1,080 = 2,624,400 pixels. In the case of ARGB_8888 images, each pixel can hold 256*256*256*256=4294967296 colors. If I had a full screen of ARGB_8888 images on my phone, I could display: 2624400 times 4294967296 is 1, 1271, 7121, 7162, 2400 kinds of pictures. So what is that? That is, the circumference of the earth's equator is 40075.02 km =40075020 m = 4007502,000 cm in 16 orders of magnitude. If each picture is printed as a 1cm high photo, it can circle the earth's equator 281265 times, isn't it a lot? Small | - popular science knowledge: one astronomical unit: 1 A.U. = 149597870700 m As to the distance, 00 1 1271 7121 7162 2400/149597870700 of about 75 people, is even 37 back and forth from the earth to the solar energyCopy the code

That said, the amount of information the display can store is enormous

The hardware layer is how the physical pixels of the screen correspond to the colors to be displayed. I won’t go down here to the hardware layer (if I can write it in the future… Long shot), and stretch it further


2.Window objects and views

At the software level, the screen is abstracted into an abstract Window object

There are almost 2000 lines of code that define a lot of the abstract behavior of Window

We have encountered it in the source analysis of the Activity at the appearance level, I do not know if you have any impression

An Activity starts with a Handler that calls the ActivityThread’s performLaunchActivity method, where the Activity is attached to the Window object via the Attach method, and the Window is instantiated in the attach method

-- -- -- - > [ActivityThread# member variable] -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- private Window mWindow; ---->[ActivityThread#performLaunchActivity]------------------ Window Window = null; if (r.mPendingRemoveWindow ! = null && r.mPreserveWindow) { window = r.mPendingRemoveWindow; r.mPendingRemoveWindow = null; r.mPendingRemoveWindowManager = null; } 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); -- -- -- - > [Activity# attach] -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - | - can learn important point: PhoneWindow ------------ final void Attach (Context Context, ActivityThread aThread, Instrumentation instr, IBinder token, int ident, Application application, Intent intent, ActivityInfo info, CharSequence title, Activity parent, String id, NonConfigurationInstances lastNonConfigurationInstances, Configuration config, String referrer, IVoiceInteractor voiceInteractor, Window window) { ... mWindow = new PhoneWindow(this, window); / / < -- note here to initialize mWindow mWindow. SetWindowControllerCallback (this); 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); } mUiThread = Thread.currentThread(); . Mwindow.setwindowmanager (// setWindowManager(WindowManager) context.getsystemservice (context.window_service), mToken, mComponent.flattenToString(), (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) ! = 0); if (mParent ! = null) { mWindow.setContainer(mParent.getWindow()); } mWindowManager = mWindow.getWindowManager(); mCurrentConfig = config; }Copy the code

PhoneWindow and DecorView

Where the package com. Android. Internal policy

PhoneWindow has a member variable in it. If the Window object passed in is not empty, then mDecor refers to it directly if you don’t know what the DecorView is. See below:


1. The constructor of PhoneWindow
-- -- -- - > [PhoneWindow member variable] -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- private DecorView mDecor; private LayoutInflater mLayoutInflater; ViewGroup mContentParent; ---->[PhoneWindow constructor # 2 parameters]------------------ public PhoneWindow(Context Context, Window preservedWindow, ActivityConfigCallback activityConfigCallback) { this(context); // Call the constructor mUseDecorContext = true; if (preservedWindow ! = null) {/ / - if the incoming Window is not null -- mDecor = (DecorView) preservedWindow. GetDecorView (); / / take ancestors to mElevation = preservedWindow getElevation (); mLoadElevation = false; mForceDecorInstall = true; getAttributes().token = preservedWindow.getAttributes().token; } // Even though the device doesn't support picture-in-picture mode, // An user can force using it through developer options. Boolean forceResizable = Settings.Global.getInt(context.getContentResolver(), DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES, 0) ! = 0; mSupportsPictureInPicture = forceResizable || context.getPackageManager().hasSystemFeature( PackageManager.FEATURE_PICTURE_IN_PICTURE); mActivityConfigCallback = activityConfigCallback; } -- -- -- - > [PhoneWindow constructor # a] -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- public PhoneWindow (Context Context) {super (Context); MLayoutInflater = LayoutInflater. From (context); }Copy the code

2. Initialization of DecorView and sublayout

-- -- -- - > [DecorView] -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - | -- - it is a FrameLayout, that can add the View public class DecorView extends FrameLayout implements  RootViewSurfaceTaker, WindowCallbacks{... } -- -- -- - > [PhoneWindow# the setContentView] -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - | - ever meet an old friend of feeling: setContentView @Override public void setContentView(int layoutResID) { if (mContentParent == null) { 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 { mLayoutInflater.inflate(layoutResID, mContentParent); } mContentParent.requestApplyInsets(); final Callback cb = getCallback(); if (cb ! = null && ! isDestroyed()) { cb.onContentChanged(); } mContentParentExplicitlySet = true; } ---->[PhoneWindow#installDecor]------------------ private void installDecor() { mForceDecorInstall = false; if (mDecor == null) { mDecor = generateDecor(-1); . / / < - the instantiation DecorView mDecor setDescendantFocusability (ViewGroup. FOCUS_AFTER_DESCENDANTS); mDecor.setIsRootNamespace(true); if (! mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures ! = 0) { mDecor.postOnAnimation(mInvalidatePanelMenuRunnable); } } else { mDecor.setWindow(this); } if (mContentParent == null) { mContentParent = generateLayout(mDecor); MContentParent ---->[PhoneWindows #generateDecor]------------------ protected generateDecor(int featureId) {... return new DecorView(context, featureId, this, getAttributes()); } -- -- -- - > [PhoneWindow# generateLayout] -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - | - this is a rather long way, in front of a bunch of too attribute or style Settings, core just a few lines of code, Protected ViewGroup generateLayout(DecorView decor) {... [Property style Settings]... mDecor.startChanging(); mDecor.onResourcesLoaded(mLayoutInflater, layoutResource); Getviewgroup contentParent = via ID_ANDROID_CONTENT in a framework layer (ViewGroup)findViewById(ID_ANDROID_CONTENT); . [Property style Settings]... return contentParent; } -- -- -- - > [Window# ID_ANDROID_CONTENT] -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - | - the id in the Window class defined in the public static final ints ID_ANDROID_CONTENT = com.android.internal.R.id.content; So the two view owners [mDecor and mContentParent] instantiate the mlayoutInflater.inflate (layoutResID, mContentParent) in the setContentView method; For child elements added to the mContentParent this ViewGroup, I have been very want to know my curiosity com. Android. Internal. R.i, dc ontent correspond to the layout of is what?Copy the code

3. Find the XML corresponding to mContentParent

So this is kind of interesting because findViewById was originally a method for a View, looking for the layout of the View with the corresponding ID name

How does PhoneWindow call it directly? The answer in it dad Windows core is to find the com. Android. Internal. R.i, dc ontent

---->[Windows #findViewById]------------------ @nullable public View findViewById(@idres int) id) { return getDecorView().findViewById(id); } ---->[Window#findViewById]------------------ public abstract View getDecorView(); -- -- -- - > [PhoneWindow# the findViewById] -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - | - is visible through the top to the findViewById mDecor @ Override public final View getDecorView() { if (mDecor == null) { installDecor(); } return mDecor; } -- -- -- - > [PhoneWindow# generateLayout] -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - | - you can see here USES a call screen_simple layout (you will find place...). } else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) ! = 0) { layoutResource = R.layout.screen_simple_overlay_action_mode; } else { // Embedded, so no decoration is needed. layoutResource = R.layout.screen_simple; // System.out.println("Simple!" ); }Copy the code

So why is the view tree the way it is


3. One of the Android butlers: WindowManager

1. Instantiation of mWindowManager in ActivityThread

To tell you the truth, now I see XXXManager my heart is a little square…

---->[WindowManager]------------------ public interface WindowManager extends ViewManager {} ---->[ViewManager]------------------ public interface ViewManager{ public void addView(View view, ViewGroup.LayoutParams params); public void updateViewLayout(View view, ViewGroup.LayoutParams params); public void removeView(View view); } | - WindowManager is an interface, inherited from ViewManager, defines the WindowManager due actions | - ViewManager only three interface methods: Add, update, and remove the -- -- -- - > [Activity# attach] -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - | - back to instantiate mWindow, Set Window to WindowManager mwindow.setwinDowManager ((WindowManager) Context.getSystemService (context.window_Service), mToken, mComponent.flattenToString(), (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) ! = 0); if (mParent ! = null) { mWindow.setContainer(mParent.getWindow()); } mWindowManager = mWindow.getWindowManager(); ---->[Window#setWindowManager]------------------ public void setWindowManager(WindowManager wm, IBinder appToken, String appName) { setWindowManager(wm, appToken, appName, false); } void setWindowManager(WindowManager wm, IBinder appToken, String appName, boolean hardwareAccelerated) { mAppToken = appToken; mAppName = appName; mHardwareAccelerated = hardwareAccelerated || SystemProperties.getBoolean(PROPERTY_HARDWARE_UI, false); If (wm == null) { Context. WINDOW_SERVICE get wm WindowManager object = (WindowManager) mContext. GetSystemService (Context. WINDOW_SERVICE); } / / - to highlight, the realization of the WindowManager class is WindowManagerImpl mWindowManager = ((WindowManagerImpl) wm). CreateLocalWindowManager (this); }Copy the code

2. Add a view to handleResumeActivity

This section is covered in the Activity section, but it’s covered here in detail

-- -- -- - > [ActivityThread# handleResumeActivity] -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- / / callback after onResume Windows interface shows the if (truly indow = = null &&! a.mFinished && willBeVisible) { r.window = r.activity.getWindow(); // Get Window View decor = R.window.getDecorView (); // Get DecorView decor. SetVisibility (view.invisible); //DecorView as ViewManager wm = a.getwindowManager (); / / get WindowManager, here with the parent interface to undertake type WindowManager. = LayoutParams l truly indow. GetAttributes (); a.mDecor = decor; l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION; l.softInputMode |= forwardBit; If (r.preservewindow) {// if there is a Window a.windowadded = true; r.mPreserveWindow = false; ViewRootImpl impl = decor.getViewRootImpl(); If (impl! = null) { impl.notifyChildRebuilt(); } } if (a.mVisibleFromClient && ! a.mWindowAdded) { a.mWindowAdded = true; Wm. addView(decor, l); wm.addView(decor, l); } -- -- -- - > [WindowManagerImpl# addView] -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - | - here call mGlobal addView @ Override public void addView (@ NonNull View view, @NonNull ViewGroup.LayoutParams params) { applyDefaultToken(params); mGlobal.addView(view, params, mDisplay, mParentWindow); } ---->[WindowManagerImpl# member variable]---------------- private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance(); ---->[WindowManagerGlobal#addView]---------------- public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) { ... final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params; . ViewRootImpl root; . root = new ViewRootImpl(view.getContext(), display); // Here we create ViewRootImpl View.setLayoutParams (wParams); mViews.add(view); mRoots.add(root); mParams.add(wparams); } try { root.setView(view, wparams, panelParentView); } catch (RuntimeException e) { ... }}Copy the code

3.ViewThe big Boss behind the curtainViewRootImpl

As mentioned in Handler, the famous exception is that non-main threads cannot update the UI.

Why are all the main thread to refresh the View will go ViewRootImpl. RequestLayout method?

| - note ViewRootImpl not a View, just realized ViewParent this operation interface public final class ViewRootImpl implements ViewParent, View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks { ---->[ViewRootImpl#setView]--------------------------- public void setView(View  view, WindowManager.LayoutParams attrs, View panelParentView) { synchronized (this) { if (mView == null) { mView = view; // Without further comment, private treasure first... //requestLayout--------- to check threads and issue traversal tasks requestLayout(); if ((mWindowAttributes.inputFeatures & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) { mInputChannel = new InputChannel(); } try { mOrigWindowType = mWindowAttributes.type; mAttachInfo.mRecomputeGlobalAttributes = true; collectViewAttributes(); // Add the Window to the screen. //mWindowSession implements the IWindowSession interface, which is a client Binder object for Session. //addToDisplay is an AIDL cross-process communication. Inform WindowManagerService add IWindow res = mWindowSession. AddToDisplay (mWindow mSeq, mWindowAttributes, getHostVisibility(), mDisplay.getDisplayId(), mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, mAttachInfo.mOutsets, mInputChannel); } catch (RemoteException e) { ... view.assignParent(this); . }} | - mWindowSession Bert also? [IWindowSession]Copy the code

IWindowSession is implemented by AIDL, so follow the routine, find his implementation class

| - implementation class IWindowSession Stub, was his final class Session extends IWindowSession. The Stub {... final WindowManagerService mService; } ---->[Session#addToDisplay]----------------- @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); } -- -- -- - > [WindowManagerService] -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - | - implementation class IWindowManager AIDL interface, so far... public class WindowManagerService extends IWindowManager.Stub implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {Copy the code

4.The ViewRootImpl requestLayoutmethods

As the name implies, is to initiate the layout request, this part of the reference to this article, well written, but a little obscure, here cato explain

---->[ViewRootImpl#requestLayout]-------------------- @Override public void requestLayout() { if (! mHandlingLayoutInLayoutRequest) { checkThread(); mLayoutRequested = true; scheduleTraversals(); }} - > [ViewRootImpl# checkThread] -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - | - this is the reason why the child thread can't update the View void checkThread () {if (mThread! = Thread.currentThread()) { throw new CalledFromWrongThreadException( "Only the original thread that created a view hierarchy can touch its views."); }} - > [ViewRootImpl# scheduleTraversals] -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - | - traversal task arrangement void scheduleTraversals () {if (! mTraversalScheduled) { mTraversalScheduled = true; mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier(); MChoreographer. PostCallback (/ / mTraversalRunnable the traversal mission Choreographer. CALLBACK_TRAVERSAL, mTraversalRunnable, null); if (! mUnbufferedInputDispatch) { scheduleConsumeBatchedInput(); } notifyRendererOfFramePending(); pokeDrawLockIfNeeded(); }} ---->[ViewRootImpl# traversal task]-------------------- final TraversalRunnable mTraversalRunnable = new TraversalRunnable(); final class TraversalRunnable implements Runnable { @Override public void run() { doTraversal(); }} void doTraversal() {if (mTraversalScheduled) {mTraversalScheduled = false; mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier); if (mProfile) { Debug.startMethodTracing("ViewAncestor"); } // traversals (); // Traversals(); if (mProfile) { Debug.stopMethodTracing(); mProfile = false; }}} - > [ViewRootImpl# performTraversals] -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - | - this method a total of nearly 1000 lines, turning the strange tiring... But this method is very important | - calling mView measure, layout, the draw private void performTraversals () {final View host = mView; . / / layout to prepare the if (mFirst | | windowShouldResize | | insetsChanged | | viewVisibilityChanged | | params! = null) { ... if (! mStopped || mReportNextDraw) { boolean focusChangedDueToTouchMode = ensureTouchModeLocally( (relayoutResult&WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE) ! = 0); if (focusChangedDueToTouchMode || mWidth ! = host.getMeasuredWidth() || mHeight ! = host.getMeasuredHeight() || contentInsetsChanged) { int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width); int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height); ---> performMeasure(childWidthMeasureSpec, childHeightMeasureSpec); . int width = host.getMeasuredWidth(); int height = host.getMeasuredHeight(); boolean measureAgain = false; If (lp.horizontalweight > 0.0f) {// if LayoutParams horizontalWeight > 0 width += (int) ((mwidth-width) *) lp.horizontalWeight); childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY); measureAgain = true; } if (lp.verticalWeight > 0.0f) {// if LayoutParams' verticalWeight is greater than 0 height += (int) ((mheight-height) * lp.verticalWeight); childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY); measureAgain = true; } if (measureAgain) {... ---> performMeasure(childWidthMeasureSpec, childHeightMeasureSpec); } layoutRequested = true; }}}... final boolean didLayout = layoutRequested /*&& ! mStopped*/ ; boolean triggerGlobalLayoutListener = didLayout || mAttachInfo.mRecomputeGlobalAttributes; if (didLayout) { ---> performLayout(lp, desiredWindowWidth, desiredWindowHeight); . }... if (! cancelDraw && ! newSurface) { if (! skipDraw || mReportNextDraw) { ... ---> performDraw(); } } else { ... mIsInTraversal = false; }}Copy the code

5.ViewRootImpl is the three methods to manipulate views

Through the above focus on performMeasure, performLayout, performDraw body

-- -- -- - > [ViewRootImpl# performMeasure] -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - | - look really cool... Private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) { Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure"); try { mView.measure(childWidthMeasureSpec, childHeightMeasureSpec); } finally { Trace.traceEnd(Trace.TRACE_TAG_VIEW); } } ---->[ViewRootImpl#performLayout]-------------------- private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth, int desiredWindowHeight) { ... final View host = mView; //host to record the current View... Layout (0, 0, host.getMeasuredWidth(), host.getMeasuredHeight())); mInLayout = false; . Slightly many} -- -- -- - > [ViewRootImpl# performDraw] -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - | - this method is not human nature, The core in the draw private void performDraw () {if (mAttachInfo. MDisplayState = = Display. STATE_OFF &&! mReportNextDraw) { return; } final boolean fullRedrawNeeded = mFullRedrawNeeded; mFullRedrawNeeded = false; . try { draw(fullRedrawNeeded); . -- -- -- - > [ViewRootImpl# the draw] -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - | - I turned on this method, only interested in these three lines mAttachInfo. MTreeObserver. DispatchOnDraw (); mAttachInfo.mHardwareRenderer.draw(mView, mAttachInfo, this); drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, Dirty) | -- - this is going to have a look at this member variable mAttachInfo -- -- -- - > [ViewRootImpl# member variable mAttachInfo] -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- the final View. AttachInfo mAttachInfo; ---->[ViewRootImpl#ViewRootImpl]-------------------- mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display, this, mHandler, this); ---->[ViewRootImpl#setView]-------------------- mAttachInfo.mRootView = view; ---->[View#mTreeObserver]-------------------- final ViewTreeObserver mTreeObserver = new ViewTreeObserver(); ---->[ViewTreeObserver#dispatchOnDraw]-------------------- public final void dispatchOnDraw() { if (mOnDrawListeners ! = null) { final ArrayList<OnDrawListener> listeners = mOnDrawListeners; int numListeners = listeners.size(); for (int i = 0; i < numListeners; ++i) { ---> listeners.get(i).onDraw(); } } } ---->[ViewRootImpl#drawSoftware]-------------------- private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff, boolean scalingRequired, Rect dirty) { // Draw with software renderer. final Canvas canvas; try { final int left = dirty.left; final int top = dirty.top; final int right = dirty.right; final int bottom = dirty.bottom; canvas = mSurface.lockCanvas(dirty); // The dirty rectangle can be modified by Surface.lockCanvas() //noinspection ConstantConditions if (left ! = dirty.left || top ! = dirty.top || right ! = dirty.right || bottom ! = dirty.bottom) { attachInfo.mIgnoreDirtyState = true; } // TODO: Do this in native canvas.setDensity(mDensity); }... try { ... if (! canvas.isOpaque() || yoff ! = 0 || xoff ! = 0) { canvas.drawColor(0, PorterDuff.Mode.CLEAR); } dirty.setEmpty(); mIsAnimating = false; mView.mPrivateFlags |= View.PFLAG_DRAWN; . try { canvas.translate(-xoff, -yoff); if (mTranslator ! = null) { mTranslator.translateCanvas(canvas); } canvas.setScreenDensity(scalingRequired ? mNoncompatDensity : 0); attachInfo.mSetIgnoreDirtyState = false; ---> mView.draw(canvas); / / the view to perform mapping function drawAccessibilityFocusedDrawableIfNeeded (canvas); }... return true; } | - I don't know you had no doubt, in the View of ontouch canvas is where come of? | - onMeasure two parameters? And see below -- -- -- -- > [View# the draw] -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - | - full text search, confusion, the callback is the canvas! public void draw(Canvas canvas) { ... // Step 3, draw the content if (! dirtyOpaque) onDraw(canvas); } ---->[View#measure]-------------------- public final void measure(int widthMeasureSpec, int heightMeasureSpec) { ... onMeasure(widthMeasureSpec, heightMeasureSpec); }Copy the code

6. I think it’s worth mentioning heresetContentView
---->[Activity# setContentView(int)]---------------- public void setContentView(@LayoutRes int layoutResID) { getWindow().setContentView(layoutResID); initWindowDecorActionBar(); } ---->[Activity# setContentView(View)]---------------- public void setContentView(View view) { getWindow().setContentView(view); initWindowDecorActionBar(); } -- -- -- - > [Activity# getWindow] -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - | - where mWindow initialization remember? Public Window getWindow() {return mWindow; } | - that is, call the PhoneWindow# the setContentView (id), ---->[PhoneWindows # setContentView(View)]---------------------- @override public void setContentView(View) SetContentView (View, new ViewGroup.layoutParams (MATCH_PARENT, MATCH_PARENT)); } @Override public void setContentView(View view, ViewGroup.LayoutParams params) { if (mContentParent == null) { ---> installDecor(); } else if (! hasFeature(FEATURE_CONTENT_TRANSITIONS)) { mContentParent.removeAllViews(); } if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) { view.setLayoutParams(params); final Scene newScene = new Scene(mContentParent, view); transitionTo(newScene); } else { ---> mContentParent.addView(view, params); } mContentParent.requestApplyInsets(); final Callback cb = getCallback(); if (cb ! = null && ! isDestroyed()) { cb.onContentChanged(); }}Copy the code

That’s a long way to go. Let’s get this straight.

1. When, where and how are Windown objects implemented? | - Windown object when in open the Activity, by the Activity of the attach method to create PhoneWindow object 2. PhoneWindow when and where some of the core View is how to achieve? | - the two core View PhoneWindow is initialized in installDecor method (3) when to add a View on the Window, where ViewRootImpl implemented? | - handleResumeActivity trigger a WindowManager addView method, | - ViewRootImpl is instantiated in WindowManagerGlobal addView method 4. ViewRootImpl is how to deal with the View, its measurement, layout, drawing on the View have what relation? | - core method is ViewRootImpl# setView in WindowManagerGlobal# addView triggered in | - through ViewRootImpl# requestLayout for processing, Using TraversalRunnable action | - View processing is the core method of performTraversals, trigger measurement, layout, drawing 5. As a View drawing fan, I need to know where the Canvas object of the View's OnDraw comes from. | - by performDraw - > the draw - > drawSoftware trigger the draw method, the canvas intoCopy the code

4. Layoutinflaters load layouts

I was going to do it in the next one, but it’s a little short, so I’ll do it.

Layoutinflaters inflate(layoutResID, mContentParent). Layoutinflaters inflate(layoutResID, mContentParent). This is its second use: to add an XML layout to the mContentParent

 mLayoutInflater = LayoutInflater.from(context);//实例化
 mLayoutInflater.inflate(layoutResID, mContentParent);
 
---->[LayoutInflater#from]-----------------------
|--该静态方法通过一个系统服务获取布局加载器LayoutInflater
public static LayoutInflater from(Context context) {
   LayoutInflater LayoutInflater =
           (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
   if (LayoutInflater == null) {
       throw new AssertionError("LayoutInflater not found.");
   }
   return LayoutInflater;


---->[LayoutInflater#inflate(int,ViewGroup)]-----------------------
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {
    return inflate(resource, root, root != null);
}

---->[LayoutInflater#inflate(int,ViewGroup,boolean)]-----------------------
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
    final Resources res = getContext().getResources();
    ...
    //这里通过res获取了Xml资源解析器
    final XmlResourceParser parser = res.getLayout(resource);
    try {
        return inflate(parser, root, attachToRoot);
    } finally {
        parser.close();
    }
}

---->[Resources#getLayout]--------------
public XmlResourceParser getLayout(@LayoutRes int id) throws NotFoundException {
    return loadXmlResourceParser(id, "layout");//加载xml资源解析器
}

---->[Resources#loadXmlResourceParser]--------------
XmlResourceParser loadXmlResourceParser(@AnyRes int id, @NonNull String type)
        throws NotFoundException {
    final TypedValue value = obtainTempTypedValue();
    try {
        final ResourcesImpl impl = mResourcesImpl;
        impl.getValue(id, value, true);
        if (value.type == TypedValue.TYPE_STRING) {
            //ResourcesImpl的loadXmlResourceParser方法...
            return impl.loadXmlResourceParser(value.string.toString(), id,
                    value.assetCookie, type);
        }
        throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id)
                + " type #0x" + Integer.toHexString(value.type) + " is not valid");
    } finally {
        releaseTempTypedValue(value);
    }
}
|--关于解析器的加载,就追到这里吧,它是有ResourcesImpl#loadXmlResourceParser加载的

---->[LayoutInflater#inflate(XmlPullParser,ViewGroup,boolean)]-----------------------
|--看一个方法:一看名,二看参,三看返回,四看限 
|--返回了View,我们便要去追这个View在哪里的定义的,是怎么实例化的
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
    synchronized (mConstructorArgs) {
        final Context inflaterContext = mContext;
        final AttributeSet attrs = Xml.asAttributeSet(parser);
        Context lastContext = (Context) mConstructorArgs[0];
        mConstructorArgs[0] = inflaterContext;
--->    View result = root;
        try {//接下来便是对Xml的解析
            int type;
            while ((type = parser.next()) != XmlPullParser.START_TAG &&
                    type != XmlPullParser.END_DOCUMENT) {
                // Empty
            }
            if (type != XmlPullParser.START_TAG) {
                throw new InflateException(parser.getPositionDescription()
                        + ": No start tag found!");
            }
            final String name = parser.getName();
            ...
            if (TAG_MERGE.equals(name)) {//如果是标签是TAG_MERGE,即merge
               ...
                rInflate(parser, root, inflaterContext, attrs, false);
            } else {
                // Temp is the root view that was found in the xml
        --->    final View temp = createViewFromTag(root, name, inflaterContext, attrs);
                ViewGroup.LayoutParams params = null;
                if (root != null) {
                    ...
                    // Create layout params that match root, if supplied
                    params = root.generateLayoutParams(attrs);
                    if (!attachToRoot) {
                        // Set the layout params for temp if we are not
                        // attaching. (If we are, we use addView, below)
                        temp.setLayoutParams(params);
                    }
                }
                ...
                // Inflate all children under temp against its context.
                rInflateChildren(parser, temp, attrs, true);
                ...
                // We are supposed to attach all the views we found (int temp)
                // to root. Do that now.
                if (root != null && attachToRoot) {
        --->        root.addView(temp, params);
                }
                // Decide whether to return the root that was passed in or the
                // top view found in xml.
                if (root == null || !attachToRoot) {
    --->            result = temp;
                }
            }
        }...
        } finally {
            // Don't retain static reference on context.--不要保留对context的静态引用
            mConstructorArgs[0] = lastContext;
            mConstructorArgs[1] = null;
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
--->    return result;
    }
}

|--核心来看形成View的是"createViewFromTag(root, name, inflaterContext, attrs)"方法
|--意外收获是看到了第三参"attachToRoot"的作用,只有true才会被加入到ViewGroup的视图树上  

---->[LayoutInflater#createViewFromTag三参]-----------------------
private View createViewFromTag(View parent, String name, Context context, AttributeSet attrs) {
    return createViewFromTag(parent, name, context, attrs, false);
}

---->[LayoutInflater#createViewFromTag四参]-----------------------
View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,
        boolean ignoreThemeAttr) {
    if (name.equals("view")) {//view标签
        name = attrs.getAttributeValue(null, "class");
    }
    if (!ignoreThemeAttr) {
        final TypedArray ta = context.obtainStyledAttributes(attrs, ATTRS_THEME);
        final int themeResId = ta.getResourceId(0, 0);
        if (themeResId != 0) {
            context = new ContextThemeWrapper(context, themeResId);
        }
        ta.recycle();
    }
    if (name.equals(TAG_1995)) {//TAG_1995=blink
        // Let's party like it's 1995!
        return new BlinkLayout(context, attrs);
    }
    try {
        View view;//<------------↓ 下面划重点了,要考的 ↓------------
        if (mFactory2 != null) {//Factory2勾起了我的洪荒记忆...不过一般是空,除非自行设定
            view = mFactory2.onCreateView(parent, name, context, attrs);
        } else if (mFactory != null) {
            view = mFactory.onCreateView(name, context, attrs);
        } else {
            view = null;
        }
        if (view == null && mPrivateFactory != null) {
            view = mPrivateFactory.onCreateView(parent, name, context, attrs);
        }//-----------------Factory暂且不管------------
        if (view == null) {
            final Object lastContext = mConstructorArgs[0];
            mConstructorArgs[0] = context;
            try {//名字没点的...如</TextView> 上面的走这里
                if (-1 == name.indexOf('.')) {
                    view = onCreateView(parent, name, attrs);
                } else {//名字有点的...如自定义的控件走这里
                    view = createView(name, null, attrs);
                }
            } finally {
                mConstructorArgs[0] = lastContext;
            }
        }
        return view;
    } catch (InflateException e) {
      ...
    }
}
|--现在球传给了"onCreateView"和"createView" 两人

---->[LayoutInflater#onCreateView]-----------------------
protected View onCreateView(String name, AttributeSet attrs)
        throws ClassNotFoundException {
    |---看到这里估计你可以猜到,人家姓"android.view.",名name ,反射一下就ok了
    return createView(name, "android.view.", attrs);
}

---->[LayoutInflater#createView]-----------------------
public final View createView(String name, String prefix, AttributeSet attrs)
        throws ClassNotFoundException, InflateException {
    Constructor<? extends View> constructor = sConstructorMap.get(name);
    if (constructor != null && !verifyClassLoader(constructor)) {
        constructor = null;
        sConstructorMap.remove(name);
    }
    Class<? extends View> clazz = null;
    try {
        |-----划重点,通过反射创建View对象,这里貌似看鸿阳的换肤教程用到过---------
        if (constructor == null) {
            clazz = mContext.getClassLoader().loadClass(
                    prefix != null ? (prefix + name) : name).asSubclass(View.class);
            if (mFilter != null && clazz != null) {
                boolean allowed = mFilter.onLoadClass(clazz);
                if (!allowed) {
                    failNotAllowed(name, prefix, attrs);
                }
            }
            constructor = clazz.getConstructor(mConstructorSignature);
            constructor.setAccessible(true);
            sConstructorMap.put(name, constructor);
        } else {
            // If we have a filter, apply it to cached constructor
            if (mFilter != null) {
                // Have we seen this name before?
                Boolean allowedState = mFilterMap.get(name);
                if (allowedState == null) {
                    // New class -- remember whether it is allowed
                    clazz = mContext.getClassLoader().loadClass(
                            prefix != null ? (prefix + name) : name).asSubclass(View.class);
                    boolean allowed = clazz != null && mFilter.onLoadClass(clazz);
                    mFilterMap.put(name, allowed);
                    if (!allowed) {
                        failNotAllowed(name, prefix, attrs);
                    }
                } else if (allowedState.equals(Boolean.FALSE)) {
                    failNotAllowed(name, prefix, attrs);
                }
            }
        }
        Object lastContext = mConstructorArgs[0];
        if (mConstructorArgs[0] == null) {
            mConstructorArgs[0] = mContext;
        }
        Object[] args = mConstructorArgs;
        args[1] = attrs;
        final View view = constructor.newInstance(args);<-----创建ok了
        if (view instanceof ViewStub) {
            // Use the same context when inflating ViewStub later.
            final ViewStub viewStub = (ViewStub) view;
            viewStub.setLayoutInflater(cloneInContext((Context) args[0]));
        }
        mConstructorArgs[0] = lastContext;
        return view;<-----view滚了回去
    }
|---好了,基本的布局加载就这样,当然其中还有很多别的处理,这里不扯了
|-- 到这里你应该有个整体的脉络了,就这样,bye 
Copy the code

Postscript: Jie wen standard

1. Growth record and Errata of this paper
Program source code The date of The appendix
V0.1– The 2018-2-18 There is no

Release name: Get and See: [-view perimeter -] Framework layer

Jiwen link: juejin.cn/post/684490…

2. More about me
Pen name QQ WeChat
Zhang Feng Jie te Li 1981462002 zdl1994328

My github: github.com/toly1994328

I’m Jane books: www.jianshu.com/u/e4e52c116… My nuggets: juejin.cn/user/149189… Personal website: www.toly1994.com

3. The statement

1—- this article is originally written by Zhang Feng Jetelie, please note 2—- all programming enthusiasts are welcome to communicate with each other 3—- personal ability is limited, if there is any error, you are welcome to criticize and point out, will be modest to correct 4—- See here, I thank you here for your love and support