說在前面
本次源碼來自Android API 24 Platform
引子
我們通常都會在Activity#onCreate方法中調(diào)用Activity#setContentView响巢,傳入頁面layout,關(guān)聯(lián)Activity和布局文件。那么,Activity#setContentView到底發(fā)生了什么呢?系統(tǒng)是如何解析我們的xml文件的幌蚊?
源碼分析
Activity#setContentView
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
這里,很簡單,主要是 getWindow().setContentView(layoutResID) 這一句诗鸭。
首先, getWindow()返回了什么参滴?
Activity#getWindow
public Window getWindow() {
return 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, String referrer, IVoiceInteractor voiceInteractor,
Window window) {
attachBaseContext(context);
mFragments.attachHost(null /*parent*/);
//關(guān)鍵在這里
mWindow = new PhoneWindow(this, window);
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);
}
//代碼省略
...
}
在Activity#attach中强岸,創(chuàng)建了這個(gè)window,而且是Window的子類PhoneWindow砾赔。至于attach方法什么時(shí)候調(diào)用蝌箍,不是我們這個(gè)文章要討論的。(具體什么時(shí)候調(diào)用這個(gè)方法可以查看ActivityThread#performLaunchActivity)
PhoneWindow#setContentView
@Override
public void setContentView(int layoutResID) {
// Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
// decor, when theme attributes and the like are crystalized. Do not check the feature
// before this happens.
if (mContentParent == null) {
//創(chuàng)建并初始化DecorView暴心,并給mContentParent 賦值
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 {
//這里就是關(guān)鍵
mLayoutInflater.inflate(layoutResID, mContentParent);
}
//代碼省略
...
}
最后調(diào)用了mLayoutInflater.inflate方法妓盲,那mLayoutInflater是什么?
PhoneWindow#constructor
/**
* Constructor for main window of an activity.
*/
public PhoneWindow(Context context, Window preservedWindow) {
this(context);
//代碼省略
...
}
public PhoneWindow(Context context) {
super(context);
mLayoutInflater = LayoutInflater.from(context);
}
這里就引出了解析xml的關(guān)鍵:LayoutInflater
LayoutInflater#from
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;
}
從context.getSystemService得到LayoutInflater专普。
這個(gè)時(shí)候我們應(yīng)該想到悯衬,這個(gè)context到底是什么?我們查看Context代碼脆诉,發(fā)現(xiàn)Context只是一個(gè)抽象類甚亭。
PhoneWindow的構(gòu)造函數(shù)來自于Activity#attach方法贷币,這個(gè)context就是Activity。
所以我們來看看Activity#getSystemService
Activity#getSystemService
@Override
public Object getSystemService(@ServiceName @NonNull String name) {
if (getBaseContext() == null) {
throw new IllegalStateException(
"System services not available to Activities before onCreate()");
}
if (WINDOW_SERVICE.equals(name)) {
return mWindowManager;
} else if (SEARCH_SERVICE.equals(name)) {
ensureSearchManager();
return mSearchManager;
}
//因?yàn)榇舜挝覀儌魅氲膮?shù)是Context.LAYOUT_INFLATER_SERVICE亏狰,所以我們最終走到了這里
return super.getSystemService(name);
}
ContextThemeWrapper#getSystemService
@Override
public Object getSystemService(String name) {
if (LAYOUT_INFLATER_SERVICE.equals(name)) {
if (mInflater == null) {
//是的役纹,最后,從這里返回了LayoutInflater
mInflater = LayoutInflater.from(getBaseContext()).cloneInContext(this);
}
return mInflater;
}
return getBaseContext().getSystemService(name);
}
我覺得差不多已經(jīng)繞暈了暇唾,其實(shí)齐苛,PhoneWindow中的LayoutInflater#from最后在ContextThemeWrapper#getSystemService執(zhí)行了LayoutInflater.from(getBaseContext()).cloneInContext(this),而ContextThemeWrapper#getBaseContext返回的就是Context退子。這個(gè)Context來自于ContextWrapper#attachBaseContext方法浓利,這個(gè)方法又在Activity#attach中執(zhí)行。
Activity#attach
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) {
//就是這里了够挂。這個(gè)context是attach的參數(shù)旁仿,
attachBaseContext(context);
}
前面我們說過了,attach方法在ActivityThread#performLaunchActivity中調(diào)用
ActivityThread#performLaunchActivity
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
//代碼省略...
Activity activity = null;
try {
java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
//代碼省略...
}
//代碼省略...
try {
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
//代碼省略...
if (activity != null) {
// activity.attach的第一 個(gè)參數(shù)Context由這個(gè)方法創(chuàng)建
Context appContext = createBaseContextForActivity(r, activity);
//代碼省略...
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);
}
}
r.paused = true;
//代碼省略...
mActivities.put(r.token, r);
}
//代碼省略...
return activity;
}
private Context createBaseContextForActivity(ActivityClientRecord r, final Activity activity) {
//代碼省略
...
//這個(gè)就是我們想要的孽糖,Context實(shí)際創(chuàng)建的是Context的子類ContextImpl
ContextImpl appContext = ContextImpl.createActivityContext(
this, r.packageInfo, r.token, displayId, r.overrideConfig);
appContext.setOuterContext(activity);
Context baseContext = appContext;
//代碼省略
...
return baseContext;
}
感覺真的跑偏了哇枯冈,到這里,我們知道了Context办悟,實(shí)際上是其子類ContextImpl尘奏。
那么,回到剛開始病蛉,我們知道LayoutInflater#from方法的參數(shù)是ContextImpl. 我們可以去ContextImpl查找ContextImpl#getSystemService
ContextImpl#getSystemService
@Override
public Object getSystemService(String name) {
return SystemServiceRegistry.getSystemService(this, name);
}
無腦下一步
SystemServiceRegistry#getSystemService
private static final HashMap<String, ServiceFetcher<?>> SYSTEM_SERVICE_FETCHERS =
new HashMap<String, ServiceFetcher<?>>();
public static Object getSystemService(ContextImpl ctx, String name) {
ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
return fetcher != null ? fetcher.getService(ctx) : null;
}
private static <T> void registerService(String serviceName, Class<T> serviceClass,
ServiceFetcher<T> serviceFetcher) {
SYSTEM_SERVICE_NAMES.put(serviceClass, serviceName);
SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher);
}
這個(gè)很簡單炫加,就是單例的一種,創(chuàng)建一個(gè)對象铺然,保存在靜態(tài)HashMap中俗孝,獲取單例就從這個(gè)HashMap中獲取之前設(shè)置的單例對象。
而這個(gè)SystemServiceRegistry#registerService什么時(shí)候調(diào)用呢探熔?
我們可以看到驹针,SystemServiceRegistry中的靜態(tài)代碼塊中調(diào)用了這個(gè)方法。
static {
//代碼省略...
registerService(Context.LAYOUT_INFLATER_SERVICE, LayoutInflater.class,
new CachedServiceFetcher<LayoutInflater>() {
@Override
public LayoutInflater createService(ContextImpl ctx) {
return new PhoneLayoutInflater(ctx.getOuterContext());
}});
//代碼省略...
}
在靜態(tài)代碼塊中生成了一堆對象放到HashMap中诀艰,其中就包括我們需要的key是Context.LAYOUT_INFLATER_SERVICE的對象柬甥。
static abstract class CachedServiceFetcher<T> implements ServiceFetcher<T> {
private final int mCacheIndex;
public CachedServiceFetcher() {
mCacheIndex = sServiceCacheSize++;
}
@Override
@SuppressWarnings("unchecked")
public final T getService(ContextImpl ctx) {
final Object[] cache = ctx.mServiceCache;
synchronized (cache) {
// Fetch or create the service.
Object service = cache[mCacheIndex];
if (service == null) {
service = createService(ctx);
cache[mCacheIndex] = service;
}
return (T)service;
}
}
public abstract T createService(ContextImpl ctx);
}
在SystemServiceRegistry#getSystemService中,我們最終返回的是fetcher.getService(ctx)其垄,而fetcher就是剛剛new出來的對象苛蒲。
CachedServiceFetcher很簡單,CachedServiceFetcher#getService返回的對象就是CachedServiceFetcher#createService創(chuàng)建的對象绿满,在這里臂外,就是我們需要知道的PhoneLayoutInflater。
現(xiàn)在,我們臨時(shí)總結(jié)一下:
- LayoutInflater#from的參數(shù)context, 實(shí)際上是Context的子類ContextImpl.
- LayoutInflater#from方法返回的參數(shù)實(shí)際上是LayoutInflater的子類PhoneLayoutInflater
雖然返回的是PhoneLayoutInflater漏健,但是PhoneLayoutInflater并沒有重新實(shí)現(xiàn)inflate方法嚎货。所以接下來,我們繼續(xù)看LayoutInflater#inflate.
LayoutInflater#inflate
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {
return inflate(resource, root, root != null);
}
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
final Resources res = getContext().getResources();
//代碼省略...
final XmlResourceParser parser = res.getLayout(resource);
try {
return inflate(parser, root, attachToRoot);
} finally {
parser.close();
}
}
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 {
// Look for the root node.
int type;
//遍歷xml的節(jié)點(diǎn)蔫浆,找到第一個(gè)節(jié)點(diǎn)是開始標(biāo)簽或者結(jié)束標(biāo)簽的節(jié)點(diǎn)
while ((type = parser.next()) != XmlPullParser.START_TAG &&
type != XmlPullParser.END_DOCUMENT) {
// Empty
}
//如果找到的這個(gè)節(jié)點(diǎn)不是開始標(biāo)簽殖属,就拋出以異常
//上面的和下面的代碼,就是為了找到xml中的第一個(gè)開始標(biāo)簽瓦盛,從這個(gè)開始標(biāo)簽開始解析節(jié)點(diǎn)
if (type != XmlPullParser.START_TAG) {
throw new InflateException(parser.getPositionDescription()
+ ": No start tag found!");
}
final String name = parser.getName();
//代碼省略...
if (TAG_MERGE.equals(name)) {
//如果xml的節(jié)點(diǎn)標(biāo)簽是merge洗显,那么root 不能為空,attachToRoot也不能為false原环,
if (root == null || !attachToRoot) {
throw new InflateException("<merge /> can be used only with a valid "
+ "ViewGroup root and attachToRoot=true");
}
//忽略掉merge挠唆,將merge標(biāo)簽的子元素添加到root上
rInflate(parser, root, inflaterContext, attrs, false);
} else {
// Temp is the root view that was found in the xml
//生成根節(jié)點(diǎn),也就是布局文件的根節(jié)點(diǎn)View(開始標(biāo)簽)
final View temp = createViewFromTag(root, name, inflaterContext, attrs);
ViewGroup.LayoutParams params = null;
if (root != null) {
//代碼省略...
// Create layout params that match root, if supplied
//生成該View的LayoutParams
params = root.generateLayoutParams(attrs);
if (!attachToRoot) {
// Set the layout params for temp if we are not
// attaching. (If we are, we use addView, below)
//如果不需要添加到root嘱吗,直接設(shè)置為該View設(shè)置LayoutParams
temp.setLayoutParams(params);
}
}
//代碼省略...
// Inflate all children under temp against its context.
//遍歷開始標(biāo)簽玄组,將該標(biāo)簽的子元素添加進(jìn)temp
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的子元素,添加進(jìn)root中
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.
mConstructorArgs[0] = lastContext;
mConstructorArgs[1] = null;
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
return result;
}
}
我們調(diào)用LayoutInflater#inflate的時(shí)候谒麦,調(diào)用的是兩個(gè)參數(shù)的方法巧勤,其中,root來自于PhoneWindow#generateLayout, 查看這個(gè)方法弄匕,我們可以知道,root就是id為com.android.internal.R.id.content的元素沽瞭,必然不是null迁匠,那么attachToRoot就是true了。也就是驹溃,解析xml中的元素城丧,并把xml中的開始標(biāo)簽元素作為com.android.internal.R.id.content的子元素。
LayoutInflater#rInflateChildren和android.view.LayoutInflater#rInflate
final void rInflateChildren(XmlPullParser parser, View parent, AttributeSet attrs,
boolean finishInflate) throws XmlPullParserException, IOException {
rInflate(parser, parent, parent.getContext(), attrs, finishInflate);
}
//該方法的作用就是豌鹤,遍歷parent的子元素亡哄,并將子元素通過addView添加進(jìn)parent中
void rInflate(XmlPullParser parser, View parent, Context context,
AttributeSet attrs, boolean finishInflate) throws XmlPullParserException, IOException {
final int depth = parser.getDepth();
int type;
//循環(huán)遍歷子元素
while (((type = parser.next()) != XmlPullParser.END_TAG ||
parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
if (type != XmlPullParser.START_TAG) {
//如果不是開始標(biāo)簽,就跳過布疙。比如</LinearLayout>
continue;
}
final String name = parser.getName();
if (TAG_REQUEST_FOCUS.equals(name)) {
//解析requestFocus標(biāo)簽
parseRequestFocus(parser, parent);
} else if (TAG_TAG.equals(name)) {
//解析tag標(biāo)簽
parseViewTag(parser, parent, attrs);
} else if (TAG_INCLUDE.equals(name)) {
//解析include標(biāo)簽
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標(biāo)簽不能作為子元素標(biāo)簽蚊惯,必須是根標(biāo)簽
throw new InflateException("<merge /> must be the root element");
} else {
//創(chuàng)建當(dāng)前元素
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 (finishInflate) {
parent.onFinishInflate();
}
}
通過遞歸,完成所有標(biāo)簽的遍歷灵临,并將子元素通過addView添加進(jìn)該標(biāo)簽的父控件中截型。其中,創(chuàng)建單個(gè)標(biāo)簽所表示的View也是通過LayoutInflater#createViewFromTag來實(shí)現(xiàn)儒溉。
LayoutInflater#createViewFromTag
private View createViewFromTag(View parent, String name, Context context, AttributeSet attrs) {
return createViewFromTag(parent, name, context, attrs, false);
}
View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,
boolean ignoreThemeAttr) {
//如果標(biāo)簽的名字是view宦焦,那么找到標(biāo)簽里面的class屬性,作為View的類名
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);
//如果有設(shè)置theme屬性,則重現(xiàn)生成context
//ContextThemeWrapper就是ContextWrapper的子類波闹,里面多了mThemeResource這個(gè)屬性酝豪,
//而ContextWrapper就是Context的裝飾類
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;
//可以設(shè)置mFactory2 和mFactory ,實(shí)現(xiàn)特殊的解析邏輯
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) {
view = mPrivateFactory.onCreateView(parent, name, context, attrs);
}
if (view == null) {
final Object lastContext = mConstructorArgs[0];
//mConstructorArgs是長度為2的數(shù)組精堕,數(shù)組的第一個(gè)元素是context孵淘,
//如果沒有設(shè)置theme,這個(gè)context就是Activity中的context锄码,
//也就是創(chuàng)建Activity的時(shí)候生成的那個(gè)ContextImpl
mConstructorArgs[0] = context;
try {
if (-1 == name.indexOf('.')) {
//如果節(jié)點(diǎn)標(biāo)簽的名字沒有. 比如TextView夺英,就是沒有包名,前綴
view = onCreateView(parent, name, attrs);
} else {
//如果節(jié)點(diǎn)標(biāo)簽有包名滋捶,比如自定義控件痛悯,比如support中的控件(android.support.v7.widget.RecyclerView)
view = createView(name, null, attrs);
}
} finally {
mConstructorArgs[0] = lastContext;
}
}
return view;
}
//代碼省略...
}
該方法根據(jù)節(jié)點(diǎn)名稱創(chuàng)建節(jié)點(diǎn)View.
LayoutInflater#onCreateView
protected View onCreateView(View parent, String name, AttributeSet attrs)
throws ClassNotFoundException {
return onCreateView(name, attrs);
}
直接調(diào)用了兩個(gè)參數(shù)的LayoutInflater#onCreateView,上文我們說過重窟,關(guān)于LayoutInflater我們實(shí)際使用的是PhoneLayoutInflater, 而在PhoneLayoutInflater中载萌,我們重寫了兩個(gè)參數(shù)的onCreateView方法。
PhoneLayoutInflater#onCreateView
private static final String[] sClassPrefixList = {
"android.widget.",
"android.webkit.",
"android.app."
};
@Override protected View onCreateView(String name, AttributeSet attrs) throws ClassNotFoundException {
for (String prefix : sClassPrefixList) {
try {
View view = createView(name, prefix, attrs);
if (view != null) {
return view;
}
}
//代碼省略...
}
return super.onCreateView(name, attrs);
}
該方法就是給name加上包名巡扇,比如TextView扭仁,我們一般寫在xml中是TextView,而不是android.widget.TextView厅翔,那么乖坠,我們通過遍歷sClassPrefixList,給TextView加上包名刀闷,去生成View控件熊泵,如果加上android.widget.,LayoutInflater#createView可以生成View甸昏,那么就停止遍歷顽分,如果沒有生成,就繼續(xù)使用android.webkit.作包名施蜜,如果三個(gè)都不行卒蘸,使用LayoutInflater#onCreateView來生成View,而LayoutInflater#onCreateView也并沒什么特殊的翻默,只不過是包名作為android.view.來生成這個(gè)View.至此缸沃,我們知道,這個(gè)PhoneLayoutInflater#onCreateView冰蘑,會逐個(gè)使用android.widget.和泌, android.webkit., android.app.祠肥, android.view. 來作為View的包名武氓,去生成View梯皿,如果View可以生成,就意味著包名正確县恕,不再嘗試后面的包名东羹。
LayoutInflater#createView
public final View createView(String name, String prefix, AttributeSet attrs)
throws ClassNotFoundException, InflateException {
//如果sConstructorMap里面保存的有該類的構(gòu)造函數(shù),就直接使用
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) {
//如果sConstructorMap里面沒有保存的有該類的構(gòu)造函數(shù)忠烛,就通過反射去獲取該類的構(gòu)造
// Class not found in the cache, see if it's real, and try to add it
//如果有前綴(包名)属提,則拼接包名和類名,如果沒有前綴美尸,直接使用name冤议,
//通過ClassLoader,獲取類的Class對象
clazz = mContext.getClassLoader().loadClass(
prefix != null ? (prefix + name) : name).asSubclass(View.class);
//代碼省略...
//通過反射师坎,獲取類的構(gòu)造函數(shù)恕酸,并保存到sConstructorMap中
constructor = clazz.getConstructor(mConstructorSignature);
constructor.setAccessible(true);
sConstructorMap.put(name, constructor);
} else {
// If we have a filter, apply it to cached constructor
//代碼省略
}
//上文已經(jīng)說過了,mConstructorArgs是長度為2的數(shù)組胯陋,數(shù)組的第一個(gè)元素是context
Object[] args = mConstructorArgs;
//現(xiàn)在把a(bǔ)ttrs賦值給數(shù)組的第二個(gè)元素
args[1] = attrs;
//通過反射蕊温,生成View對象
final View view = constructor.newInstance(args);
if (view instanceof ViewStub) {
//針對ViewStub的特殊處理
// Use the same context when inflating ViewStub later.
final ViewStub viewStub = (ViewStub) view;
viewStub.setLayoutInflater(cloneInContext((Context) args[0]));
}
return view;
}
//代碼省略...
}