分析setContentView流程
frameworks/base/core/java/android/app/Activity.java路径下找到入口
public void setContentView(int layoutResID) {
getWindow().setContentView(layoutResID);
initActionBar();
}
Activity又调用了mWindow中的setContentView
public Window getWindow() {
return mWindow;
}
看看mWindow是什么东西,attach方法中找到了mWindow的初始化
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) {
//...
mWindow = PolicyManager.makeNewWindow(this);
//...
}
frameworks/base/core/java/com/android/internal/policy/PolicyManager.java
跟着来到PolicyManager这个类中
public final class PolicyManager {
//需要反射创建出来的对象
private static final String POLICY_IMPL_CLASS_NAME =
"com.android.internal.policy.impl.Policy";
private static final IPolicy sPolicy;
static {
//...
Class policyClass = Class.forName(POLICY_IMPL_CLASS_NAME);
sPolicy = (IPolicy)policyClass.newInstance();
//...
}
//...
//我们的mWindow对象又是调用了sPolicy.makeNewWindow(context),所以继续跟
public static Window makeNewWindow(Context context) {
return sPolicy.makeNewWindow(context);
}
//...
}
通过上面的代码能知道sPolicy.makeNewWindow(context)这个接口调用的方法实现类是
com.android.internal.policy.impl.Policy,我们去找。
frameworks/base/policy/src/com/android/internal/policy/impl/Policy.java
public class Policy implements IPolicy {
//...
//终于知道了 mWindow 的对象是PhoneWindow实现的.
public Window makeNewWindow(Context context) {
return new PhoneWindow(context);
}
//...
}
好,到了这里终于知道Activity中的getWindow()对象就是PhoneWindow,那么继续去查找PhoneWindow中的setContentView
frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindow.java
public class PhoneWindow extends Window implements MenuBuilder.Callback {
private LayoutInflater mLayoutInflater;
//...
@Override
public void setContentView(int layoutResID) {
if (mContentParent == null) {
installDecor();
} else {
mContentParent.removeAllViews();
}
//我们设置的activity_main.xml布局传到了这里,继续跟过去看看
mLayoutInflater.inflate(layoutResID, mContentParent);
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
}
//...
}
到了这里知道我们平时设置的activity_main.xml被传入了 mLayoutInflater.inflate(layoutResID, mContentParent);方法中,路径是:
frameworks/base/core/java/android/view/LayoutInflater.java
public abstract class LayoutInflater {
//...
//调用了下面的方法
public View inflate(int resource, ViewGroup root) {
return inflate(resource, root, root != null);
}
//...
public View inflate(int resource, ViewGroup root, boolean attachToRoot) {
if (DEBUG) System.out.println("INFLATING from resource: " + resource);
//获取xml解析器
XmlResourceParser parser = getContext().getResources().getLayout(resource);
try {
return inflate(parser, root, attachToRoot);
} finally {
parser.close();
}
}
//...
}
通过上面代码找到了Xml的解析器是 .getLayout(resource); 返回的,来看看代码
frameworks/base/core/java/android/content/res/Resources.java
public class Resources {
//...
final AssetManager mAssets;
//...
public XmlResourceParser getLayout(int id) throws NotFoundException {
return loadXmlResourceParser(id, "layout");
}
//...
//我们的activity_main.xml传到了这里
XmlResourceParser loadXmlResourceParser(int id, String type)
throws NotFoundException {
synchronized (mTmpValue) {
TypedValue value = mTmpValue;
getValue(id, value, true);
if (value.type == TypedValue.TYPE_STRING) {
return loadXmlResourceParser(value.string.toString(), id,
value.assetCookie, type);
}
//...
}
}
//...
//activity_main.xml又传到了getValue当中
public void getValue(int id, TypedValue outValue, boolean resolveRefs)
throws NotFoundException {
//AssetManager 对象中的 getResourceValue
boolean found = mAssets.getResourceValue(id, 0, outValue, resolveRefs);
if (found) {
return;
}
throw new NotFoundException("Resource ID #0x"
+ Integer.toHexString(id));
}
//...
}
frameworks/base/core/java/android/content/res/AssetManager.java
AssetManager 里的native方法不知道怎么调用的,没找到动态库加载,记录一下。
public final class AssetManager {
//...
/*package*/ final boolean getResourceValue(int ident,
int density,
TypedValue outValue,
boolean resolveRefs)
{
//这是一个本地方法,去获取资源的block,其实是以个index
int block = loadResourceValue(ident, (short) density, outValue, resolveRefs);
if (block >= 0) {
if (outValue.type != TypedValue.TYPE_STRING) {
return true;
}
//给TyepValue 根据id获取string
outValue.string = mStringBlocks[block].get(outValue.data);
return true;
}
return false;
}
//...
}
回到frameworks/base/core/java/android/view/LayoutInflater.java
中去查看return inflate(parser, root, attachToRoot);这个方法。
public abstract class LayoutInflater {
public View inflate(int resource, ViewGroup root, boolean attachToRoot) {
if (DEBUG) System.out.println("INFLATING from resource: " + resource);
XmlResourceParser parser = getContext().getResources().getLayout(resource);
try {
return inflate(parser, root, attachToRoot);
} finally {
parser.close();
}
}
public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) {
synchronized (mConstructorArgs) {
final AttributeSet attrs = Xml.asAttributeSet(parser);
Context lastContext = (Context)mConstructorArgs[0];
mConstructorArgs[0] = mContext;
View result = root;
try {
// Look for the root node.
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 (DEBUG) {
System.out.println("**************************");
System.out.println("Creating root view: "
+ name);
System.out.println("**************************");
}
if (TAG_MERGE.equals(name)) {
if (root == null || !attachToRoot) {
throw new InflateException(" can be used only with a valid "
+ "ViewGroup root and attachToRoot=true");
}
//解析xml生成View的主要代码
rInflate(parser, root, attrs, false);
} else {
// Temp is the root view that was found in the xml
View temp;
if (TAG_1995.equals(name)) {
temp = new BlinkLayout(mContext, attrs);
} else {
temp = createViewFromTag(root, name, attrs);
}
ViewGroup.LayoutParams params = null;
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);
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
rInflate(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.
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) {
InflateException ex = new InflateException(e.getMessage());
ex.initCause(e);
throw ex;
} catch (IOException e) {
InflateException ex = new InflateException(
parser.getPositionDescription()
+ ": " + e.getMessage());
ex.initCause(e);
throw ex;
} finally {
// Don't retain static reference on context.
mConstructorArgs[0] = lastContext;
mConstructorArgs[1] = null;
}
return result;
}
}
}
//...
/**
* Recursive method used to descend down the xml hierarchy and instantiate
* views, instantiate their children, and then call onFinishInflate().
*/
void rInflate(XmlPullParser parser, View parent, final AttributeSet attrs,
boolean finishInflate) throws XmlPullParserException, IOException {
final int depth = parser.getDepth();
int type;
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)) {
parseRequestFocus(parser, parent);
} else if (TAG_INCLUDE.equals(name)) {//这里是熟悉的include布局解析
if (parser.getDepth() == 0) {
throw new InflateException(" cannot be the root element");
}
//跟着进入这个解析include布局方法
parseInclude(parser, parent, attrs);
} else if (TAG_MERGE.equals(name)) {
throw new InflateException(" must be the root element");
} else if (TAG_1995.equals(name)) {
final View view = new BlinkLayout(mContext, attrs);
final ViewGroup viewGroup = (ViewGroup) parent;
final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
rInflate(parser, view, attrs, true);
viewGroup.addView(view, params);
} else {
//上面一些特殊的布局解析完了以后,就到了这里
//通过反射来创建我们的View
final View view = createViewFromTag(parent, name, attrs);
//获取这个view的父容器
final ViewGroup viewGroup = (ViewGroup) parent;
//生成这个ViewGroup 的属性
final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
//递归创建整个Xml 里的控件
rInflate(parser, view, attrs, true);
//view添加到当前父容器中
viewGroup.addView(view, params);
}
}
if (finishInflate) parent.onFinishInflate();
}
//....
private void parseInclude(XmlPullParser parser, View parent, AttributeSet attrs)
throws XmlPullParserException, IOException {
int type;
//父View必须是ViewGroup的子类
if (parent instanceof ViewGroup) {
final int layout = attrs.getAttributeResourceValue(null, "layout", 0);
//获取include中layout的属性值,如果属性值是0 就进一步判断 value值是不是null的,null就抛异常
if (layout == 0) {
final String value = attrs.getAttributeValue(null, "layout");
if (value == null) {
throw new InflateException("You must specifiy a layout in the"
+ " include tag: ");
} else {
throw new InflateException("You must specifiy a valid layout "
+ "reference. The layout ID " + value + " is not valid.");
}
} else {
//如果include的layout布局存在,就获取它的xml解析器
final XmlResourceParser childParser =
getContext().getResources().getLayout(layout);
try {
// 获取include标签中设置的属性
final AttributeSet childAttrs = Xml.asAttributeSet(childParser);
//如果是标签的开始就能往下执行
while ((type = childParser.next()) != XmlPullParser.START_TAG &&
type != XmlPullParser.END_DOCUMENT) {
// Empty.
}
//不是开始标签就抛异常
if (type != XmlPullParser.START_TAG) {
throw new InflateException(childParser.getPositionDescription() +
": No start tag found!");
}
//解析第一个标签
final String childName = childParser.getName();
//判断是否是merge标签
if (TAG_MERGE.equals(childName)) {
// Inflate all children.
rInflate(childParser, parent, childAttrs, false);
} else {
//这里的view就是创建了include进来的viewgroup子类根控件
final View view = createViewFromTag(parent, childName, childAttrs);
//这个是include layout的父容器
final ViewGroup group = (ViewGroup) parent;
ViewGroup.LayoutParams params = null;
try {// 获取include layout的父容器的属性
params = group.generateLayoutParams(attrs);
} catch (RuntimeException e) {
params = group.generateLayoutParams(childAttrs);
} finally {
if (params != null) {
//我们的include layout设置上属性
view.setLayoutParams(params);
}
}
//递归创建view,直到整个view树创建完成
// Inflate all children.
rInflate(childParser, view, childAttrs, true);
// Attempt to override the included layout's android:id with the
// one set on the tag itself.
TypedArray a = mContext.obtainStyledAttributes(attrs,
com.android.internal.R.styleable.View, 0, 0);
int id = a.getResourceId(com.android.internal.R.styleable.View_id, View.NO_ID);
// While we're at it, let's try to override android:visibility.
int visibility = a.getInt(com.android.internal.R.styleable.View_visibility, -1);
a.recycle();
//改变include layout的布局id,因为用include标签的时候都会带id,父容器会用这个id初始化
if (id != View.NO_ID) {
view.setId(id);
}
switch (visibility) {
case 0:
view.setVisibility(View.VISIBLE);
break;
case 1:
view.setVisibility(View.INVISIBLE);
break;
case 2:
view.setVisibility(View.GONE);
break;
}
//最后把include layout添加到它的父容器中。
group.addView(view);
}
} finally {
childParser.close();
}
}
} else {
throw new InflateException(" can only be used inside of a ViewGroup");
}
final int currentDepth = parser.getDepth();
while (((type = parser.next()) != XmlPullParser.END_TAG ||
parser.getDepth() > currentDepth) && type != XmlPullParser.END_DOCUMENT) {
// Empty
}
}
//....
到这里就知道整个xml里面的控件是怎么通过解析后拿到name以后进行反射出来了。