This is the sixth day of my participation in the August More text Challenge. For details, see:August is more challenging
Borrow more text challenge to urge oneself in August, thank nugget!
The background,
Yesterday, when talking about the reuse mechanism of RecyclerView, said the event distribution and View of the general rendering process, today looked at the source code, make a record.
Ii. Process analysis
1, the entrance
If we create an Activity, the system inherits AppCompatAvtovity by default, not the Activity. Here, we change our custom Activity to an inherited Activity and go to setContentView as follows:
In the first line of code, we fetch a Window object, and then call the setContentView of the Window.
PhoneWindow setContentView PhoneWindow setContentView PhoneWindow setContentView PhoneWindow setContentView
The setContentView in PhoneWindow does two main things:
- Create a DecorView.
- The layout is inflated with layoutInflater.inflate.
What is a DecorView? In PhoneWindow, this is a top-level View that hosts our entire View tree. The source code is as follows:
Next is the real View’s render flowing through LayoutInflater.inflate.
2. Rendering process
Layoutinflaters have four different constructors, but rendering a view obviously ends up in one of these methods:
With three arguments, parser, root, and attachToRoot, we usually pass a layout file with the inflate method. The layout file is mapped to an int value. So when is it converted?
Look at the constructor when we call it
The focus of parsing has been highlighted. After getting the Parser, pass it as an argument to another constructor. Here’s a look at what function the parser is finally called for:
The process has collapsed, but it is important to note that there is a locking behavior during the transformation. Next, look at the function that was finally called (the unimportant code has been removed) :
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
Synchronized is used with the inflate procedure, which means that it is not possible for multiple threads to render the same View simultaneously
synchronized (mConstructorArgs) {
final Context inflaterContext = mContext;
final AttributeSet attrs = Xml.asAttributeSet(parser);
Context lastContext = (Context) mConstructorArgs[0];
mConstructorArgs[0] = inflaterContext;
View result = root;
try {
advanceToRootNode(parser);
final String name = parser.getName();
// 2. If the outermost node is a merge tag, the root value is not null and the attachToRoot value is true, otherwise an exception is raised
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");
}
/ / the key one
rInflate(parser, root, inflaterContext, attrs, false);
} else {
/ / 2
final View temp = createViewFromTag(root, name, inflaterContext, attrs);
ViewGroup.LayoutParams params = null;
// 3, root! = null
if(root ! =null) {
// Get the layout parameters
params = root.generateLayoutParams(attrs);
if (!attachToRoot) {
temp.setLayoutParams(params);
}
}
/ / key three
rInflateChildren(parser, temp, attrs, true);
// 4, root! = null && attachToRoot
if(root ! =null && attachToRoot) {
root.addView(temp, params);
}
/ / 5, and the third case, root = = null | |! attachToRoot
if (root == null| |! attachToRoot) { result = temp; }}}catch (XmlPullParserException e) {
} finally{}returnresult; }}Copy the code
The analysis is all in the code, so let’s summarize the analysis above.
- First, the outermost tag is checked, if merger is required, special treatment is required, if no exception is raised, then it will be executed
rInflate
。 - CreateViewFromTag creates the View object primarily by the node name.
- It’s going to react to incoming
root
和attachToRoot
There are three cases-
root ! If =null && attachToRoot is false, the outermost layout property is set.
-
root ! When =null && attachToRoot is true, a parent layout is assigned to the loaded layout.
-
If root == null, attachToRoot has no effect.
-
We don’t seem to have the details worked out yet. What happens with the rInflate() execution flow? How does createViewFromTag() create a View from a tag? Let’s first look at the createViewFromTag() process (the truncated code) :
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();
}
try {
// Focus on
View view = tryCreateView(parent, name, context, attrs);
if (view == null) {
final Object lastContext = mConstructorArgs[0];
mConstructorArgs[0] = context;
try {
if (-1 == name.indexOf('. ')) {
view = onCreateView(context, parent, name, attrs);
} else {
view = createView(context, name, null, attrs); }}finally {
mConstructorArgs[0] = lastContext; }}return view;
} catch (InflateException e) {
} catch (ClassNotFoundException e) {
} catch (Exception e) {
}
}
Copy the code
In createViewFromTag() we really need to focus on tryCreateView() :
The onCreateView() method of the Factory is used to create the View.
As you can see, the Factory provides a hook that prevents us from creating a View with the LayoutInflate. Therefore, we can use this method to replace the system View with a custom View.
So far, we’ve looked at the createViewFromTag() process, and now look at the associated execution process with 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 (parser.getDepth() == 0) {
throw new InflateException("<include /> cannot be the root element");
}
parseInclude(parser, context, parent, attrs);
} else if (TAG_MERGE.equals(name)) {
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);
rInflateChildren(parser, view, attrs, true); viewGroup.addView(view, params); }}if (pendingRequestFocus) {
parent.restoreDefaultFocus();
}
if(finishInflate) { parent.onFinishInflate(); }}Copy the code
The code is actually pretty easy to understand, but you get the View hierarchy here, and you call it recursively from the parent View.
Third, summary
To summarize, we need to inherit an Activity, not AppCompatActivity. What’s the difference? As you can see from the source code, AppCompatActivity is a subclass of Activity and will set a title bar in the interface compared to the Activity. Of course you can hide it, you just need to control it in your code.
To summarize the general process:
- In the Activity,
setContentView()
It’s actually getting an instance of PhoneWindow, and it actually ends up callingPhoneWindow
的setContentView()
. - In PhoneWindow, if the decorView is not initialized, it is initialized and then execution begins
LayoutInflate
的inflate()
Methods. - The View is created with the Tag name reflection creation in Inflate (), which is then displayed by recursively calling the parse View.
In addition, because the level is limited, the wrong place is inevitable, rather misleading others, welcome big guy to correct! Code word is not easy, thank you for your attention! 🙏