This article is my attempt to advance the advanced Android road in the third common sense interpretation of the source code, the follow-up plan and Activity start process source analysis. This article source more, if time permits will record some relevant knowledge points interview questions. The Android SDK version is 9.0. If there is any inadequacy in the study notes, please give me your advice.
Update time :25 December 2019 19:25:42
Android Advanced (Layout Drawing Process 2)
preface
The previous two source code articles, HashMap and Handler, were relatively simple. My learning route is half a month in advance to plan and determine, so the previous article on the basis of Java and basic Android knowledge behind the Android interview knowledge advanced to the present source interpretation is also a gradual and more difficult process. So this article is mainly to view the interpretation of setContentView() source process of a learning summary/notes.
The length of this article and personal time issues are only recorded into the LayoutInflater layer for the time being, which is the simplest process of reading the source code. The next article will continue from this article to try to read the View drawing process in depth.
General flow chart
Picture reference address
Get into the business
1. The setContentView () entry
Go to the setContentView method to see the methods in AppCompatActivity
public void setContentView(@LayoutRes int layoutResID) {
this.getDelegate().setContentView(layoutResID);
}
@NonNull
public AppCompatDelegate getDelegate(a) {
if (this.mDelegate == null) {
this.mDelegate = AppCompatDelegate.create(this.this);
}
return this.mDelegate;
}
Copy the code
There is no need to write anything about this code, so we can look at the appcompatdelegate. create method. We find the AppCompatDelegate implementation class AppCompatDelegateImpl in the corresponding discovery.
2.AppCompatDelegateImpl
public void setContentView(View v) {
this.ensureSubDecor();
ViewGroup contentParent = (ViewGroup)this.mSubDecor.findViewById(16908290);
contentParent.removeAllViews();
contentParent.addView(v);
this.mOriginalWindowCallback.onContentChanged();
}
public void setContentView(int resId) {
this.ensureSubDecor();
ViewGroup contentParent = (ViewGroup)this.mSubDecor.findViewById(16908290);
contentParent.removeAllViews();
LayoutInflater.from(this.mContext).inflate(resId, contentParent);
this.mOriginalWindowCallback.onContentChanged();
}
public void setContentView(View v, LayoutParams lp) {
this.ensureSubDecor();
ViewGroup contentParent = (ViewGroup)this.mSubDecor.findViewById(16908290);
contentParent.removeAllViews();
contentParent.addView(v, lp);
this.mOriginalWindowCallback.onContentChanged();
}
Copy the code
So let’s just pick a method out of this code, and I’m going to use setContentView(int) as an example, Inflaters.from (this.mContext).inflate(resId, contentParent); Asynchronize the inflate part of the code with the ensureSubDecor () methods in part 3 of this article
2.1 ensureSubDecor
private void ensureSubDecor(a) {
if (!this.mSubDecorInstalled) {
// More on that below
this.mSubDecor = this.createSubDecor();
// If a title was installed before the layout, now propagate it
CharSequence title = this.getTitle();
if(! TextUtils.isEmpty(title)) {if (this.mDecorContentParent ! =null) {
this.mDecorContentParent.setWindowTitle(title);
} else if (this.peekSupportActionBar() ! =null) {
this.peekSupportActionBar().setWindowTitle(title);
} else if (this.mTitleView ! =null) {
this.mTitleView.setText(title); }}// Adjust the size on the device, and initialize some parameters
this.applyFixedSizeWindow();
// A blank method
this.onSubDecorInstalled(this.mSubDecor);
this.mSubDecorInstalled = true;
AppCompatDelegateImpl.PanelFeatureState st = this.getPanelState(0.false);
if (!this.mIsDestroyed && (st == null || st.menu == null)) {
this.invalidatePanelMenu(108); }}}Copy the code
The actions of some of the methods in the above code are explained in the comments, so let’s look at the createSubDecor method
2.2 createSubDecor
private ViewGroup createSubDecor(a) {
// Get the system TypedArray, system theme
TypedArray a = this.mContext.obtainStyledAttributes(styleable.AppCompatTheme);
if(! a.hasValue(styleable.AppCompatTheme_windowActionBar)) {// If no TypedArray object is found, an error is reported and the TypedArray object is released
a.recycle();
throw new IllegalStateException("You need to use a Theme.AppCompat theme (or descendant) with this activity.");
} else {
// Set various Windows properties
if (a.getBoolean(styleable.AppCompatTheme_windowNoTitle, false)) {
// requestWindowFeature
this.requestWindowFeature(1);
} else if (a.getBoolean(styleable.AppCompatTheme_windowActionBar, false)) {
this.requestWindowFeature(108);
}
if (a.getBoolean(styleable.AppCompatTheme_windowActionBarOverlay, false)) {
this.requestWindowFeature(109);
}
if (a.getBoolean(styleable.AppCompatTheme_windowActionModeOverlay, false)) {
this.requestWindowFeature(10);
}
// Get a judgment that the interface should be floating like a Dialog
this.mIsFloating = a.getBoolean(styleable.AppCompatTheme_android_windowIsFloating, false);
a.recycle();
// PhoneWindow is a subclass of Window, which prepares the root layout in PhoneWindow by creating a DecorView, as described below
this.mWindow.getDecorView();
LayoutInflater inflater = LayoutInflater.from(this.mContext);
ViewGroup subDecor = null;
// The rest of the article determines which layout is inflate according to the tag
if (!this.mWindowNoTitle) {
if (this.mIsFloating) {
subDecor = (ViewGroup)inflater.inflate(layout.abc_dialog_title_material, (ViewGroup)null);
this.mHasActionBar = this.mOverlayActionBar = false;
} else if (this.mHasActionBar) {
....
} else {
if (this.mOverlayActionMode) {
subDecor = (ViewGroup)inflater.inflate(layout.abc_screen_simple_overlay_action_mode, (ViewGroup)null);
} else {
subDecor = (ViewGroup)inflater.inflate(layout.abc_screen_simple, (ViewGroup)null);
}
if (VERSION.SDK_INT >= 21) {
ViewCompat.setOnApplyWindowInsetsListener(subDecor, new OnApplyWindowInsetsListener() {
public WindowInsetsCompat onApplyWindowInsets(View v, WindowInsetsCompat insets) {
int top = insets.getSystemWindowInsetTop();
int newTop = AppCompatDelegateImpl.this.updateStatusGuard(top);
if(top ! = newTop) { insets = insets.replaceSystemWindowInsets(insets.getSystemWindowInsetLeft(), newTop, insets.getSystemWindowInsetRight(), insets.getSystemWindowInsetBottom()); }returnViewCompat.onApplyWindowInsets(v, insets); }}); }else {
((FitWindowsViewGroup)subDecor).setOnFitSystemWindowsListener(new OnFitSystemWindowsListener() {
public void onFitSystemWindows(Rect insets) {
insets.top = AppCompatDelegateImpl.this.updateStatusGuard(insets.top); }}); }}// If the viewGroup is not assigned at the end, an error is reported
if (subDecor == null) {
throw newIllegalArgumentException(...) ; }else {
if (this.mDecorContentParent == null) {
this.mTitleView = (TextView)subDecor.findViewById(id.title);
}
ViewUtils.makeOptionalFitsSystemWindows(subDecor);
ContentFrameLayout contentView = (ContentFrameLayout)subDecor.findViewById(id.action_bar_activity_content);
// Get the Content layout object in PhoneWindow
ViewGroup windowContentView = (ViewGroup)this.mWindow.findViewById(16908290);
if(windowContentView ! =null) {
while(windowContentView.getChildCount() > 0) {
View child = windowContentView.getChildAt(0);
windowContentView.removeViewAt(0);
contentView.addView(child);
}
windowContentView.setId(-1);
contentView.setId(16908290);
if (windowContentView instanceof FrameLayout) {
((FrameLayout)windowContentView).setForeground((Drawable)null); }}this.mWindow.setContentView(subDecor);
contentView.setAttachListener(new OnAttachListener() {
public void onAttachedFromWindow(a) {}public void onDetachedFromWindow(a) {
AppCompatDelegateImpl.this.dismissPopups(); }});returnsubDecor; }}}Copy the code
Get the Theme of the interface, then set the DecorView in the PhoneWindow with mwindow.getDecorView (), and set the properties according to the different identifiers. So let’s take a look at the PhoneWindow implementation
3.PhoneWindow
The mWindow implementation class mentioned above is PhoneWindow, while MockWindow is also a descendant class, but MockWindow is more of a test class, so we can just read the corresponding method in PhoneWindow.
@Override
public void setContentView(int layoutResID) {
// Load a layout with the id of the layout, and then load the layout with the ID of content with findViewById(r.i.C.ontent)
// Assigns FrameLayout to mContentParent and adds the view to mDecor (DecorView)
if (mContentParent == null) {
installDecor();
} else if(! hasFeature(FEATURE_CONTENT_TRANSITIONS)) {// Not for the first time setContentView removes all views from the parent container and adds them again
mContentParent.removeAllViews();
}
// Whether the window needs to transition
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
getContext());
transitionTo(newScene);
} else {
/ / emphasis, in front of the source code also inflate method is used in many places, we follow up below
mLayoutInflater.inflate(layoutResID, mContentParent);
}
// mContentParent is essentially FrameLayout
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
if(cb ! =null && !isDestroyed()) {
cb.onContentChanged();
}
mContentParentExplicitlySet = true;
}
Copy the code
We know that the bottom layer of mContentParent is FrameLayout, we can basically understand the interface layout mechanism. The diagram below:
The ActionBarContextView above is the title, but some Settings don’t show the whole title, so this is just a case, and the FrameLayout below with the ID content is the mContentParent, The views you pass through the setContentView method will be placed on the FrameLayout with the CONTENT id, so that your Activity shows the layout view you wrote. Since mContentParent was empty when the Activity was first created, So the PhoneWindow. InstallDecor method is used.
3.1 installDecor
private void installDecor(a) {
mForceDecorInstall = false;
// Inherit FrameLayout, the top View of the window, which is the root View of the Activity's display View. It contains a TitleView and a ContentView
if (mDecor == null) {// Empty for the first time
// Create a DecorView (FrameLayout)
mDecor = generateDecor(-1); . }else {
mDecor.setWindow(this);
}
if (mContentParent == null) {// The first time setContentView is null
// The mContentParent is loaded from the frameworks\base\core\res\res\layout\ directory
// a layout with the ID content in the mDecor (DecorView) when the layout is loaded
// FrameLayout control. This FrameLayout control holds the View passed in by setContentViewmContentParent = generateLayout(mDecor); .// Check if there is a view with id = decor_content_parent (screen_action_bar.xml)
final DecorContentParent decorContentParent = (DecorContentParent) mDecor.findViewById(
R.id.decor_content_parent);
if(decorContentParent ! =null) {...if (mDecorContentParent.getTitle() == null) {
// Set the titlemDecorContentParent.setWindowTitle(mTitle); }... }else {
// Title view
mTitleView = (TextView) findViewById(R.id.title);
// Some layouts do not have a title control, that is, do not display the title
if(mTitleView ! =null) {
// Determine if there is a feature that does not display the title
if ((getLocalFeatures() & (1<< FEATURE_NO_TITLE)) ! =0) {
final View titleContainer = findViewById(R.id.title_container);
if(titleContainer ! =null) {
titleContainer.setVisibility(View.GONE);
} else {
mTitleView.setVisibility(View.GONE);
}
mContentParent.setForeground(null);
} else {// Display the titlemTitleView.setText(mTitle); }}}// Set the background
if (mDecor.getBackground() == null&& mBackgroundFallbackResource ! =0) { mDecor.setBackgroundFallback(mBackgroundFallbackResource); }... }}Copy the code
MDecor is a DecorView, inherited from FrameLayout, the root View of the Activity’s display View. It contains a TitleView and a ContentView. This is also empty the first time it loads, The generateDecor function is called to create mDecor (DecorView created through the new DecorView(Context, featureId, this, getAttributes()) method). We then create the mContentParent view using the generateLayout method and set the title. Let’s look at the main work of generateLayout.
3.2 generateLayout
protected ViewGroup generateLayout(DecorView decor) {...// Get various properties of Window to set flags and parameters
TypedArray a = getWindowStyle();
// Load a layout resource based on the previous flag and feature into the DecorView and return the View that can be used as a container
The layout file is in the frameworks\base\core\res\res\layout\ directory
int layoutResource;
int features = getLocalFeatures();
if ((features & (1<< FEATURE_SWIPE_TO_DISMISS)) ! =0) {
layoutResource = R.layout.screen_swipe_dismiss;
} else if ((features & ((1 << FEATURE_LEFT_ICON) | (1<< FEATURE_RIGHT_ICON))) ! =0) {
// Check whether it is a dialog
if (mIsFloating) {
...
} else {
layoutResource = R.layout.screen_title_icons;
}
removeFeature(FEATURE_ACTION_BAR);
// System.out.println("Title Icons!" );
} else if ((features & ((1 << FEATURE_PROGRESS) | (1<< FEATURE_INDETERMINATE_PROGRESS))) ! =0
&& (features & (1 << FEATURE_ACTION_BAR)) == 0) {
layoutResource = R.layout.screen_progress;
} else if ((features & (1<< FEATURE_CUSTOM_TITLE)) ! =0) {
// mIsFloating indicates whether it is dailog as mentioned earlier
if (mIsFloating) {
...
} else{ layoutResource = R.layout.screen_custom_title; }... }else if ((features & (1 << FEATURE_NO_TITLE)) == 0) {
if (mIsFloating) {
...
} else if ((features & (1<< FEATURE_ACTION_BAR)) ! =0) {
layoutResource = a.getResourceId(
R.styleable.Window_windowActionBarFullscreenDecorLayout,
R.layout.screen_action_bar);
} else {
layoutResource = R.layout.screen_title;
}
// System.out.println("Title!" );
} else if ((features & (1<< FEATURE_ACTION_MODE_OVERLAY)) ! =0) {
layoutResource = R.layout.screen_simple_overlay_action_mode;
} else {
layoutResource = R.layout.screen_simple;
}
mDecor.startChanging();
// Load the system Layout file according to the layoutResource (Layout ID) and add it to the DecorView
// the title view is created in the layoutinflater.inflate method, and the layout file with the ID layoutResource is loaded through the layoutinflater.inflate and assigned to the root layout
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
// contentParent is the FrameLayout used to add the Activity's layout, with related theme styles, above
// The mentioned FrameLayout id will be assigned to the PhoneWindow mContentParent when returned
ViewGroup contentParent = (ViewGroup) findViewById(ID_ANDROID_CONTENT);
if (contentParent == null) {
throw new RuntimeException("Window couldn't find content container view"); }... mDecor.finishChanging();return contentParent;
}
Copy the code
This part of the code is mostly described in the comments. It loads the layout content according to the incoming DecorView.
The second half of this article focuses on what the layoutinflater.inflate method does. The View and View rule PL will be explained in the next article
Let’s take a look at how layoutinflater.inflate instantiates the XML layout file into the corresponding View object
4 LayoutInflater.inflate
Instantiates a layout XML file into its corresponding {@link android.view.View}
objects. It is never used directly. Instead, use {@link android.app.Activity#getLayoutInflater()} or {@link Context#getSystemService} to retrieve a standard LayoutInflater instance that is already hooked up to the current context and correctly configured for the device you are running on.
Translation:
Instantiate the layout XML file as its corresponding {@link android.view.view}
Object. Never use it directly. On the contrary, Retrieve standard LayoutInflater instances using {@link Android.app.activity# getLayoutInflater ()} or {@Link Context# getSystemService} Already connected to the current context and configured correctly for the device you are running.
This note extends to several ways in which layoutinflater.inflate is initialized:
- Activity. GetLayoutInflater ();
- Context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) ;
- LayoutInflater.from(context);
By viewing the source Activity. GetLayoutInflater () will call to PhoneWindow constructor, in fact the final call is 3; 3 eventually calls 2 context.getSystemService (context.layout_inflater_service);
So let’s look at what the source code does with the method that the inflate eventually invokes:
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
synchronized (mConstructorArgs) {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate");
final Context inflaterContext = mContext;
final AttributeSet attrs = Xml.asAttributeSet(parser);
Context lastContext = (Context) mConstructorArgs[0];
mConstructorArgs[0] = inflaterContext;
View result = root;
try {
int type;
// Find the root node of the layout to determine whether it is the leading flag
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 it is a Merge tag, it must be attached to a RootView,
if (TAG_MERGE.equals(name)) {
if (root == null| |! attachToRoot) {throw new InflateException("<merge /> can be used only with a valid "
+ "ViewGroup root and attachToRoot=true");
}
rInflate(parser, root, inflaterContext, attrs, false);
} else {
// Temp is the root view found by the method
final View temp = createViewFromTag(root, name, inflaterContext, attrs);
ViewGroup.LayoutParams params = null;
if(root ! =null) {
// If Root is not null, LayoutParams will be generated based on the parameters of the current tag
params = root.generateLayoutParams(attrs);
if(! attachToRoot) {// Note that params only works when added to a Viewz;
// If null sets params to the root layouttemp.setLayoutParams(params); }}// Add children tag to temp
rInflateChildren(parser, temp, attrs, true);
// If Root is not null and is attachToRoot, add the created View to Root
if(root ! =null && attachToRoot) {
root.addView(temp, params);
}
// Return the root layout object
if (root == null| |! attachToRoot) { result = temp; }}}catch (XmlPullParserException e) {
final InflateException ie = new InflateException(e.getMessage(), e);
ie.setStackTrace(EMPTY_STACK_TRACE);
throw ie;
} catch (Exception e) {
final InflateException ie = new InflateException(parser.getPositionDescription()
+ ":" + e.getMessage(), e);
ie.setStackTrace(EMPTY_STACK_TRACE);
throw ie;
} finally {
mConstructorArgs[0] = lastContext;
mConstructorArgs[1] = null;
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
returnresult; }}Copy the code
If root is null, attachToRoot is useless, and setting any value is meaningless. If root is not null and attachToRoot is true, a parent layout, root, is assigned to the loaded layout file. If root is not null and attachToRoot is false, all layout properties on the outermost layer of the layout file are set. When the view is added to the parent view, these layout properties take effect automatically. When no attachToRoot parameter is set, if root is not null, attachToRoot defaults to true.
4.1 rInflate
void rInflate(XmlPullParser parser, View parent, Context context,
AttributeSet attrs, boolean finishInflate) throws XmlPullParserException, IOException {
final int depth = parser.getDepth();
int type;
boolean pendingRequestFocus = false;
while(((type = parser.next()) ! = XmlPullParser.END_TAG || parser.getDepth() > depth) && type ! = XmlPullParser.END_DOCUMENT) {if(type ! = XmlPullParser.START_TAG) {continue;
}
final String name = parser.getName();
if (TAG_REQUEST_FOCUS.equals(name)) {
pendingRequestFocus = true;
consumeChildElements(parser);
} else if (TAG_TAG.equals(name)) {
parseViewTag(parser, parent, attrs);
} else if (TAG_INCLUDE.equals(name)) {
// If an include tag appears here, an exception will be thrown
if (parser.getDepth() == 0) {
throw new InflateException("<include /> cannot be the root element");
}
parseInclude(parser, context, parent, attrs);
} else if (TAG_MERGE.equals(name)) {
// If the merge tag appears here, it will also throw an exception
throw new InflateException("<merge /> must be the root element");
} else {
final View view = createViewFromTag(parent, name, context, attrs);
final ViewGroup viewGroup = (ViewGroup) parent;
final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
// If the current View is a ViewGroup (wraps other views) then all its child views are inflate here
rInflateChildren(parser, view, attrs, true); viewGroup.addView(view, params); }}if (pendingRequestFocus) {
parent.restoreDefaultFocus();
}
if (finishInflate) {
// Call back the parent methodparent.onFinishInflate(); }}Copy the code
View objects are created using createViewFromTag and the rInflateChildren and rInflate methods. If a View is found to be a ViewGroup, constantly rotate to add all child views to the root layout *.
4.2 createViewFromTag
CreateViewFromTag () createViewFromTag() createViewFromTag()
View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,
boolean ignoreThemeAttr) {
if (name.equals("view")) {
name = attrs.getAttributeValue(null."class");
}
// Apply a theme wrapper, if allowed and one is specified.
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)) {
// Let's party like it's 1995!
return new BlinkLayout(context, attrs);
}
try {
View view;
// Call the corresponding onCreateView method if the check object exists.
if(mFactory2 ! =null) {
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) {
// mPrivateFactory calls its onCreateView method if it is not empty
view = mPrivateFactory.onCreateView(parent, name, context, attrs);
}
// Create your own view if none of the previous steps are copied
if (view == null) {
final Object lastContext = mConstructorArgs[0];
mConstructorArgs[0] = context;
try {
if (-1 == name.indexOf('. ')) {
// If the name of the View does not contain '.', it is a system control, and the following call chain will be preceded by 'Android.view.'.
view = onCreateView(parent, name, attrs);
} else {
// If the name contains '.', the createView method is called directly. OnCreateView is also called createView
view = createView(name, null, attrs); }}finally {
mConstructorArgs[0] = lastContext; }}return view;
} catch (InflateException e) {
throw e;
} catch (ClassNotFoundException e) {
.....
} catch(Exception e) { ..... }}Copy the code
The main code above is commented, but the only thing left unexplained is how the createView method gets the view from the control name and AttributeSet.
4.3 the 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 {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, name);
if (constructor == null) {
// Determine the cacheclazz = 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(mFilter ! =null) {
Boolean allowedState = mFilterMap.get(name);
if (allowedState == null) {
// New class -- remember whether it is allowedclazz = mContext.getClassLoader().loadClass( prefix ! =null ? (prefix + name) : name).asSubclass(View.class);
booleanallowed = 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;
// Get the view by reflection
final View view = constructor.newInstance(args);
if (view instanceof ViewStub) {
final ViewStub viewStub = (ViewStub) view;
viewStub.setLayoutInflater(cloneInContext((Context) args[0]));
}
mConstructorArgs[0] = lastContext;
return view;
} catch (NoSuchMethodException e) {
......
} catch (ClassCastException e) {
.....
} catch (ClassNotFoundException e) {
throw e;
} catch (Exception e) {
......
} finally{ Trace.traceEnd(Trace.TRACE_TAG_VIEW); }}Copy the code
Create views in LayoutInflater by reading the XML and using the Pull parsing method to retrieve the View tags. Create View objects by reflecting them with labels; In the case of a ViewGroup, the child View is iterated and repeated, and then added to the parent View; The method is synchronized -> rsynchronized -> createViewFromTag-> createView
At this point, the entire layout process is pretty much cleared up,