一、Activity的setContentView流程
1.Activity.java中的setContView方法,首先调用getWindow()也就是PhoneWindow
2.getWindow()方法需要找activity的启动流程,ActivityThread.performLaunchActivity()方法
1)通过仪表类反射获取到activity对象
2)调用activity的attach方法,其里面就创建了PhoneWindow。
3)调用仪表类的callActivityOnCreate方法。
3.PhoneWindow的setContentView方法
1)调用installDecor()方法,创建DecorView(就是个frameLayout)拿到ContentParent(就是ViewGroup,xml布局上的一个布局view)。
- generateDecor(-1)方法就是创建了DecorView()对象
- generateLayout(mDecor)
-- 获取到xml里的属性设置信息,设置上去。
-- 把根据属性选则的xml布局添加到DecorView上。
-- 获取布局上的控件
2)把.xml渲染到Content上
二、布局结构
三、 AppCompatActivity的流程
1.AppCompatActivity的setContentView()调用的是AppCompatDelegateIml的setContentView()
AppCompatActivity.java
AppCompatDelegate.java
AppcompatDelegateIml.java
2.ensureSubDector()方法。
主要就是调用creatSubDecor().
3.creatSubDecor()方法。
-
获取xml配置的属性
-
获取PhoneWindow.(在oncreat里已经获取到了)
-
通过phoneWindow调用getDecorView()方法与上面的代码开始相同。
1)创建DecorView 2) 获取到xml里的属性设置信息,设置上去。 3)把根据属性选则的xml布局添加到DecorView上。 4)获取布局上的控件 -
创建subDecor的ViewGroup,并添加布局来替换DecorView中的Content,修改subDecor内的控件ID与DecorView中的viewGroup相同。(这里说的viewGroup是为了放activity的xml的)
PhoneWindow.java
四、LayoutInflater.from(mContext).inflate(id,布局,boolean)的解析。
1.LayoutInflate.inflate()
解析xml布局文件,为后续创建各个控件布局做准备。
LayoutInflate.java
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 {
advanceToRootNode(parser);
final String name = parser.getName();
if (DEBUG) {
System.out.println("**************************");
System.out.println("Creating root view: "
+ name);
System.out.println("**************************");
}
//判断xml的根标签是不是merge标签。
if (TAG_MERGE.equals(name)) {
//如果是,没有root或者attachToRoot为false,抛异常崩溃
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 that was found in the xml
//创建根视图,通过反射。
final View temp = createViewFromTag(root, name, inflaterContext, attrs);
ViewGroup.LayoutParams params = null;
//root不为空
if (root != null) {
if (DEBUG) {
System.out.println("Creating params from root: " +
root);
}
// Create layout params that match root, if supplied
//获取到父布局的参数。
params = root.generateLayoutParams(attrs);
//如果attachToRoot为false
if (!attachToRoot) {
// Set the layout params for temp if we are not
// attaching. (If we are, we use addView, below)
//把根布局的参数,传递给新创建的根视图。
temp.setLayoutParams(params);
}//否则不传入。
}
if (DEBUG) {
System.out.println("-----> start inflating children");
}
// Inflate all children under temp against its context.
rInflateChildren(parser, temp, attrs, true);
if (DEBUG) {
System.out.println("-----> done inflating children");
}
// We are supposed to attach all the views we found (int temp)
// to root. Do that now.
//root布局不为Null,attachToRoot,则调用addView()方法
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;
}
}
} 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(
getParserStateDescription(inflaterContext, attrs)
+ ": " + e.getMessage(), e);
ie.setStackTrace(EMPTY_STACK_TRACE);
throw ie;
} finally {
// Don't retain static reference on context.
mConstructorArgs[0] = lastContext;
mConstructorArgs[1] = null;
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
//返回根布局
return result;
}
}
2.创建子视图,通过递归的方式
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)) {
//include标签不能是root
if (parser.getDepth() == 0) {
throw new InflateException("<include /> cannot be the root element");
}
parseInclude(parser, context, parent, attrs);
} else if (TAG_MERGE.equals(name)) {
//如果子布局中有merge,则抛出异常。
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();
}
}
二、面试题目
1.requestWindowFeature(Window.FEATURE_NO_TITLE)要放在哪里?为什么?
答案:需要放在setContentView上面。
调用的是getWindow().requestFeature(featureId);
在PhoneWindow的setContentView最下面设置成了true.
2. PhoneWindow什么时候会创建
下面的创建都会创建一个PhoneWdindow
- Activity
- Dialog
- PopupWindow
- Toast
3.添加布局的写法个结果。
4. merge、include、viewstub的区别。
merge:只能当root_view,作用优化布局。
include:不能做root_view,include的布局,id会被替换成include上的id。
viewstub:根include差不多,可以懒加载,默认隐藏。