LayoutInflater 將布局文件(XML)實(shí)例化為一個(gè) View 對(duì)象竹挡。
通常我們會(huì)通過(guò) Activity#getLayoutInflater()
或者是 context.getSystemService(Context.LAYOUT_INFLATER_SERVICE)
來(lái)獲取一個(gè)標(biāo)準(zhǔn)的與當(dāng)前運(yùn)行的 Context 相關(guān)聯(lián)的 LayoutInflater 實(shí)例。
我們以 Activity#setContentView(@LayoutRes int layoutResID)
為例來(lái)看一下 LayoutInflater 的工作流程痴颊。
源碼:Android-29尉姨、AndroidX
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
}
1 AppCompatActivity#setContentView 流程
AppCompatActivity 是 AndroidX 兼容包下的 Activity 的實(shí)現(xiàn)基類
//#AppCompatActivity
@Override
public void setContentView(@LayoutRes int layoutResID) {
getDelegate().setContentView(layoutResID);
}
可以看到內(nèi)部通過(guò)代理來(lái)進(jìn)行設(shè)置,下面來(lái)看代理的實(shí)現(xiàn) getDelegate()
////#AppCompatActivity
@NonNull
public AppCompatDelegate getDelegate() {
if (mDelegate == null) {
mDelegate = AppCompatDelegate.create(this, this);
}
return mDelegate;
}
@NonNull
public static AppCompatDelegate create(@NonNull Activity activity,
@Nullable AppCompatCallback callback) {
return new AppCompatDelegateImpl(activity, callback);
}
通過(guò) getDelegate()
方法創(chuàng)建了代理的實(shí)現(xiàn)類 AppCompatDelegateImpl
,下面我們來(lái)看這個(gè)代理實(shí)現(xiàn)類中的 setContentView
方法:
@Override
public void setContentView(int resId) {
ensureSubDecor();
ViewGroup contentParent = mSubDecor.findViewById(android.R.id.content);
contentParent.removeAllViews();
LayoutInflater.from(mContext).inflate(resId, contentParent);
mAppCompatWindowCallback.getWrapped().onContentChanged();
}
可以看見(jiàn)伤为,AppCompatActivity#setContentView
內(nèi)部是通過(guò) LayoutInflater.from(mContext).inflate(resId, contentParent)
來(lái)加載布局的
2 LayoutInflater.from(mContext) 流程
在來(lái)看 from(mContext)
方法之前,我們先明確 mContext 的類型据途。
2.1 mContext 的類型
往上翻可以看到我們?cè)趧?chuàng)建代理對(duì)象的同時(shí)绞愚,將 Activity 作為參數(shù)傳入,也就是說(shuō) mContext 的類型是 AppCompatActivity 也就是 ContextThemeWrapper 類型颖医。
這里我們貼上一張 Context 相關(guān)的背景知識(shí):
2.2 LayoutInflater.from(mContext)
/**
* Obtains the LayoutInflater from the given context.
*/
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;
}
可以發(fā)現(xiàn)我們是通過(guò) context.getSystemService
來(lái)獲取 LayoutInflater “服務(wù)”位衩。
之前我們已經(jīng)明確了 mContext 的類型是 ContextThemeWrapper
類型,我們來(lái)看看它的 from 方法:
@Override
public Object getSystemService(String name) {
if (LAYOUT_INFLATER_SERVICE.equals(name)) {
if (mInflater == null) {
mInflater = LayoutInflater.from(getBaseContext()).cloneInContext(this);
}
return mInflater;
}
return getBaseContext().getSystemService(name);
}
這個(gè)的 getBaseContext 獲取到的是 mBase 這個(gè)屬性,它的類型是 ContextImpl熔萧,mBase 的賦值源于 Activity 在創(chuàng)建后的調(diào)用的 attach 方法糖驴,這里就不再展開了。
然后讓我們來(lái)看看 ContextImpl 的 getSystemService
方法:
@Override
public Object getSystemService(String name) {
return SystemServiceRegistry.getSystemService(this, name);
}
到這里看上去是真正要獲取服務(wù)的地方了佛致,我們根據(jù) ContextImpl 對(duì)象本身和服務(wù)名稱 從 SystemServiceRegistry 中獲取服務(wù)贮缕,我們可以把它理解為注冊(cè)表:
public static Object getSystemService(ContextImpl ctx, String name) {
ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
return fetcher != null ? fetcher.getService(ctx) : null;
}
跟進(jìn)到它的 getSystemService 方法中可以看到:
- 從 SYSTEM_SERVICE_FETCHERS 中獲取 ServiceFetcher
- 通過(guò) ServiceFetcher 獲取服務(wù)
服務(wù)在什么時(shí)候注冊(cè)?
在 SystemServiceRegistry 的靜態(tài)代碼塊中俺榆,對(duì)服務(wù)進(jìn)行了注冊(cè):
registerService(Context.LAYOUT_INFLATER_SERVICE, LayoutInflater.class,
new CachedServiceFetcher<LayoutInflater>() {
@Override
public LayoutInflater createService(ContextImpl ctx) {
return new PhoneLayoutInflater(ctx.getOuterContext());
}});
可以看到最終我們獲取到的 LayoutInflater 類型是 PhoneLayoutInflater 類型感昼。
在 ContextImpl 類中有一個(gè) mServiceCache 屬性,在聲明的時(shí)候已經(jīng)初始化:
//ContextImpl
// The system service cache for the system services that are cached per-ContextImpl.
@UnsupportedAppUsage
final Object[] mServiceCache = SystemServiceRegistry.createServiceCache();
由此可見(jiàn)罐脊,在 ContextImpl 創(chuàng)建后定嗓,服務(wù)就會(huì)進(jìn)行注冊(cè)蜕琴,根據(jù)注釋可知,每一個(gè) ContextImpl 對(duì)象都有自己的服務(wù)緩存宵溅。
ServiceFetcher如何獲取服務(wù)凌简?
我們?cè)诨剡^(guò)頭來(lái)看服務(wù)獲取的具體邏輯(我刪除了部分代碼):
public final T getService(ContextImpl ctx) {
final Object[] cache = ctx.mServiceCache;
final int[] gates = ctx.mServiceInitializationStateArray;
for (;;) {
boolean doInitialize = false;
synchronized (cache) {
// Return it if we already have a cached instance.
//①
T service = (T) cache[mCacheIndex];
if (service != null || gates[mCacheIndex] == ContextImpl.STATE_NOT_FOUND) {
return service;
}
if (doInitialize) {
// Only the first thread gets here.
T service = null;
try {
//②
service = createService(ctx);
newState = ContextImpl.STATE_READY;
} catch (ServiceNotFoundException e) {
onServiceNotFound(e);
} finally {
synchronized (cache) {
③
cache[mCacheIndex] = service;
gates[mCacheIndex] = newState;
cache.notifyAll();
}
}
return service;
}
}
}
- 查看 ContextImpl 的緩存,緩存命中則直接返回服務(wù)
- 緩存不存在的話則通過(guò) createService 方法創(chuàng)建服務(wù)
- 最后將服務(wù)緩存在 ContextImpl 中
2.3 PhoneLayoutInflater#cloneInContext
在上面 ContextThemeWrapper 獲取服務(wù)的代碼中 mInflater = LayoutInflater.from(getBaseContext()).cloneInContext(this);
我們?cè)讷@取 PhoneLayoutInflater 之后恃逻,還調(diào)用了 cloneInContext 方法雏搂,名字聽上去是克隆一個(gè)對(duì)象。
public LayoutInflater cloneInContext(Context newContext) {
return new PhoneLayoutInflater(this, newContext);
}
protected PhoneLayoutInflater(LayoutInflater original, Context newContext) {
super(original, newContext);
}
protected LayoutInflater(LayoutInflater original, Context newContext) {
mContext = newContext;
mFactory = original.mFactory;
mFactory2 = original.mFactory2;
mPrivateFactory = original.mPrivateFactory;
setFilter(original.mFilter);
initPrecompiledViews();
}
由此可見(jiàn)我們使用了一個(gè)新的 Context 替換了原先 LayoutInflater 中的 mContext 屬性辛块,根據(jù)上面的代碼可知畔派,我們?cè)谑褂?ContextImpl 創(chuàng)建了 PhoneLayoutInflater 之后,將其中的 mContext 替換為 ContextThemeWrapper润绵。
3 LayoutInflater#inflate
LayoutInflater.from(mContext).inflate(resId, contentParent);
由上面分析可知线椰,在獲取到 PhoneLayoutInflater 對(duì)象后,接著調(diào)用它的 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();
if (DEBUG) {
Log.d(TAG, "INFLATING from resource: \"" + res.getResourceName(resource) + "\" ("
+ Integer.toHexString(resource) + ")");
}
View view = tryInflatePrecompiled(resource, res, root, attachToRoot);
if (view != null) {
return view;
}
XmlResourceParser parser = res.getLayout(resource);
try {
return inflate(parser, root, attachToRoot);
} finally {
parser.close();
}
}
最終調(diào)用的 inflate 方法接收三個(gè)參數(shù):
- @LayoutRes int resource 這個(gè)參數(shù)就是我們 setContentView 方法傳入的 XML 資源文件
- @Nullable ViewGroup root 這是一個(gè)可選的父布局容器
- boolean attachToRoot 尘盼,這個(gè)參數(shù)決定是否將從 XML 加載的 View 對(duì)象添加進(jìn) root 中
根據(jù) setContentView 的調(diào)用可知憨愉,我們傳入的布局文件最后會(huì)被添加進(jìn) id 為 Content 的容器中
在 inflate 方法中我們創(chuàng)建了當(dāng)前 XML 資源文件的解析器,并將它傳入重載的 inflate 房中:
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
synchronized (mConstructorArgs) {
View result = root;
try {
//① merge 標(biāo)簽判斷
if (TAG_MERGE.equals(name)) {
if (root == null || !attachToRoot) {
...
}
rInflate(parser, root, inflaterContext, attrs, false);
} else {
// ②創(chuàng)建 XMl 文件中頂層標(biāo)簽中標(biāo)記的 View 對(duì)象
final View temp = createViewFromTag(root, name, inflaterContext, attrs);
ViewGroup.LayoutParams params = null;
if (root != null) {
// ③指定 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)
temp.setLayoutParams(params);
}
}
//④遞歸的將子視圖填充到 temp 中
context.rInflateChildren(parser, temp, attrs, true);
//⑤將根據(jù) XML 實(shí)例化的 View 添加到 root 中
if (root != null && attachToRoot) {
root.addView(temp, params);
}
//直接返回 temp 對(duì)象
if (root == null || !attachToRoot) {
result = temp;
}
}
} catch (XmlPullParserException e) {
....
}
return result;
}
}
這里的方法有點(diǎn)長(zhǎng)卿捎,我同樣省略一些代碼配紫,讓我們的視線集中在布局的解析上。
3.1 merge 標(biāo)簽
我們判斷 XMl 的根標(biāo)簽是不是 merge午阵,如果是 merge 標(biāo)記的話在直接調(diào)用遞歸填充方法 rInflater
躺孝,這里我們先不看對(duì) merge 標(biāo)簽的處理。
3.2 LayoutInflater#createViewFromTag
如果不是 merge 標(biāo)簽的話底桂,我們將調(diào)用 createViewFromTag
方法來(lái)創(chuàng)建 View 對(duì)象:
View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,
boolean ignoreThemeAttr) {
//...
try {
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) {
//...
}
}
這里也是分為三步:
- 嘗試調(diào)用
tryCreateView
方法創(chuàng)建 View - 如果不是自定義 View 則調(diào)用 onCreateView 方法
- 如果是自定義 View 則調(diào)用 createView
3.2.1 LayoutInflater#tryCreateView
public final View tryCreateView(@Nullable View parent, @NonNull String name,
@NonNull Context context,
@NonNull AttributeSet attrs) {
if (name.equals(TAG_1995)) {
// Let's party like it's 1995!
return new BlinkLayout(context, attrs);
}
View view;
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);
}
return view;
}
- 如果 XML 的標(biāo)記是 blink(TAG_1995) 的話植袍,會(huì)創(chuàng)建一個(gè) BlinkLayout 類型的對(duì)象,它會(huì)每隔 500ms 重繪一次籽懦,形成閃爍的效果于个。
- 嘗試通過(guò) mFactory2 對(duì)象的 onCreateView 來(lái)創(chuàng)建 View 對(duì)象
- 嘗試通過(guò) mFactory 對(duì)象的 onCreateView 來(lái)創(chuàng)建 View 對(duì)象
- 嘗試通過(guò) mPrivateFactory 對(duì)象的 onCreateView 來(lái)創(chuàng)建 View 對(duì)象
mFactory2、mPrivateFactory 對(duì)象都是 Factory2 類型暮顺,而 mFactory 對(duì)象是 Factory 類型厅篓,F(xiàn)actory2 是對(duì) Factory 接口的升級(jí),F(xiàn)actory2 繼承了 Factory 接口捶码,并且多了一個(gè) View onCreateView(View parent, String name,Context context, @NonNull AttributeSet attrs);
相比較于 Factory 的 onCreateView 而言多了一個(gè) View 類型的 parent 參數(shù)羽氮。
稍后我們?cè)賮?lái)看這幾個(gè)屬性是在何時(shí)被設(shè)置的,讓我們回到 createViewFromTag 方法
3.2.2 LayoutInflater#createView
如果我們沒(méi)有設(shè)置 mFactory2 這些屬性惫恼,那么 tryOnCreateView 這個(gè)方法返回的 View 對(duì)象就是 null档押,之后我們會(huì)根據(jù) XML 標(biāo)記是否包含 "." 來(lái)決定調(diào)用不同的方法。
如果標(biāo)簽不包含 "." ,說(shuō)明這是 Android 系統(tǒng)提供的 View 例如:TextView汇荐,ImageView...,這時(shí)我們會(huì)調(diào)用 onCreateView 方法盆繁,這個(gè)方法會(huì)調(diào)用一些列的重載方法掀淘,最后會(huì)調(diào)用 createView(String name, String prefix, AttributeSet attrs)
方法:
protected View onCreateView(String name, AttributeSet attrs)
throws ClassNotFoundException {
return createView(name, "android.view.", attrs);
}
public final View createView(String name, String prefix, AttributeSet attrs)
throws ClassNotFoundException, InflateException {
Context context = (Context) mConstructorArgs[0];
if (context == null) {
context = mContext;
}
return createView(context, name, prefix, attrs);
}
在 onCreateView 方法當(dāng)中,我們會(huì)調(diào)用 createView 方法油昂,并且限定了 prefix 這個(gè)入?yún)?"android.view"革娄,緊接著我們會(huì)調(diào)用最后重載的 createView 方法:
public final View createView(@NonNull Context viewContext, @NonNull String name,
@Nullable String prefix, @Nullable 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 {
if (constructor == null) {
// Class not found in the cache, see if it's real, and try to add it
clazz = Class.forName(prefix != null ? (prefix + name) : name, false,
mContext.getClassLoader()).asSubclass(View.class);
constructor = clazz.getConstructor(mConstructorSignature);
constructor.setAccessible(true);
sConstructorMap.put(name, constructor);
} else {
// If we have a filter, apply it to cached constructor
...
}
try {
final View view = constructor.newInstance(args);
if (view instanceof ViewStub) {
// Use the same context when inflating ViewStub later.
final ViewStub viewStub = (ViewStub) view;
viewStub.setLayoutInflater(cloneInContext((Context) args[0]));
}
return view;
} finally {
mConstructorArgs[0] = lastContext;
}
}
...
}
我省略一些異常捕獲和日志代碼,可以看到我們會(huì)嘗試從緩存中獲取當(dāng)前 View 的構(gòu)造函數(shù)冕碟,如果命中緩存的話拦惋,則直接通過(guò)反射創(chuàng)建 View 對(duì)象并返回,否則我們會(huì)通過(guò)入?yún)?name 和 prefix 來(lái)拼接 View 的全路徑類名安寺,然后在通過(guò)反射獲取它的構(gòu)造函數(shù)厕妖,放入緩存之后在創(chuàng)建 View 返回。
到這里我們可以說(shuō) LayoutInflater 是通過(guò)反射的方式將 XML 實(shí)例化成一個(gè) View 對(duì)象挑庶,這么說(shuō)沒(méi)錯(cuò)言秸,但是之前我們有提到過(guò),在反射創(chuàng)建 View 對(duì)象之前迎捺,會(huì)經(jīng)過(guò) mFactory 等對(duì)象的處理举畸,接下來(lái)讓我們肯看這些“工廠“是什么時(shí)候設(shè)置的。
4 LayoutInflater#setFactory2
之前我們分析的是 AppCompatActivity#setContentView 流程:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
}
在 setContentView 之前我們調(diào)用了父類的 onCreate 方法:
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
final AppCompatDelegate delegate = getDelegate();
delegate.installViewFactory();
delegate.onCreate(savedInstanceState);
super.onCreate(savedInstanceState);
}
這里可以看到我們調(diào)用了 delegate 對(duì)象的 installViewFactory 方法:
//AppCompatDelegateImpl
@Override
public void installViewFactory() {
LayoutInflater layoutInflater = LayoutInflater.from(mContext);
if (layoutInflater.getFactory() == null) {
LayoutInflaterCompat.setFactory2(layoutInflater, this);
} else {
if (!(layoutInflater.getFactory2() instanceof AppCompatDelegateImpl)) {
Log.i(TAG, "The Activity's LayoutInflater already has a Factory installed"
+ " so we can not install AppCompat's");
}
}
}
其中 LayoutInflaterCompat.setFactory2(layoutInflater, this);
對(duì) mFactory 進(jìn)行了設(shè)置:
//LayoutInflaterCompat
class AppCompatDelegateImpl extends AppCompatDelegate
implements MenuBuilder.Callback, LayoutInflater.Factory2{
//......
}
public static void setFactory2(
@NonNull LayoutInflater inflater, @NonNull LayoutInflater.Factory2 factory) {
inflater.setFactory2(factory);
//省略了兼容性代碼......
}
AppCompatDelegateImpl 實(shí)現(xiàn)了 Factory2 接口凳枝,我們通過(guò) setFactory2 方法將 this 賦值給 mFactory 和 mFactory2 對(duì)象抄沮,下面我們來(lái)看 Factory2 接口在 AppCompatDelegateImpl 中的實(shí)現(xiàn):
@Override
public final View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
return createView(parent, name, context, attrs);
}
public View createView(View parent, final String name, @NonNull Context context,
@NonNull AttributeSet attrs) {
//省略部分代碼......
mAppCompatViewInflater = new AppCompatViewInflater();
return mAppCompatViewInflater.createView(parent, name, context, attrs, inheritContext,
IS_PRE_LOLLIPOP, true, VectorEnabledTintResources.shouldBeUsed()
);
}
可以看到在 createView 方法中,我們構(gòu)建了一個(gè) AppCompatViewInflater 對(duì)象岖瑰,將創(chuàng)建 View 的操作交給了它叛买,接著我們看這個(gè)對(duì)象的 createView 方法:
final View createView(View parent, final String name, @NonNull Context context,
@NonNull AttributeSet attrs, boolean inheritContext,
boolean readAndroidTheme, boolean readAppTheme, boolean wrapContext) {
final Context originalContext = context;
// We can emulate Lollipop's android:theme attribute propagating down the view hierarchy
// by using the parent's context
if (inheritContext && parent != null) {
context = parent.getContext();
}
if (readAndroidTheme || readAppTheme) {
// We then apply the theme on the context, if specified
context = themifyContext(context, attrs, readAndroidTheme, readAppTheme);
}
if (wrapContext) {
context = TintContextWrapper.wrap(context);
}
View view = null;
// We need to 'inject' our tint aware Views in place of the standard framework versions
switch (name) {
case "TextView":
view = createTextView(context, attrs);
verifyNotNull(view, name);
break;
case "ImageView":
view = createImageView(context, attrs);
verifyNotNull(view, name);
break;
// 省略了后續(xù)代碼...
}
protected AppCompatTextView createTextView(Context context, AttributeSet attrs) {
return new AppCompatTextView(context, attrs);
}
- 可以看見(jiàn),為了能夠兼容锭环,使得 android 5.0 之前的版本能夠使用矢量圖功能聪全,我們會(huì)對(duì)originalContext 進(jìn)行包裝,通過(guò) TintContextWrapper 的
wrapper
方法將它包裝成 TintContextWrapper 類型 - 通過(guò)字符串匹配辅辩,直接使用 new 對(duì)象的方式难礼,創(chuàng)建相對(duì)應(yīng)的 View,節(jié)省了反射帶來(lái)的開銷
由此可見(jiàn)玫锋,我們雖然我們?cè)?XML 中聲明的是 TextView 類型蛾茉,但是為了向前兼容,系統(tǒng)實(shí)際創(chuàng)建的是 AppCompatTextView 類型撩鹿;另外通過(guò) View#getContext 方法獲取到的類型也不一定是 Activity 類型谦炬,也有可能是 TintContextWrapper 類型。
5 LayoutInflater#setPrivateFactory
經(jīng)過(guò)上面的流程分析,我們已經(jīng)知道 mFactory 和 mFactory2 屬性是什么時(shí)候被賦值的键思,那么 mPrivateFactory 又是什么時(shí)候被賦值的呢础爬?
/**
* @hide for use by framework
*/
@UnsupportedAppUsage
public void setPrivateFactory(Factory2 factory) {
if (mPrivateFactory == null) {
mPrivateFactory = factory;
} else {
mPrivateFactory = new FactoryMerger(factory, factory, mPrivateFactory, mPrivateFactory);
}
}
我們可以通過(guò) LayoutInflater#setPrivateFactory
方法設(shè)置 mPrivateFactory,注釋中說(shuō)這是個(gè)隱藏方法吼鳞,被 framework 層使用看蚜,這里給大家一個(gè)看源碼的網(wǎng)站,就是谷歌最近剛開源的 Code Serach赔桌,我們通過(guò)這個(gè)網(wǎng)站來(lái)查看一下
setPrivateFactory 方法的引用供炎。
如圖,我們?cè)?Activity attach 方法調(diào)用的時(shí)候通過(guò) mWindow.getLayoutInflater().setPrivateFactory(this);
對(duì) mPrivateFactory 進(jìn)行設(shè)置疾党,并且設(shè)置的值是 this音诫,說(shuō)明我們 Activity 也實(shí)現(xiàn)了 Factory2 接口。
下面我們來(lái)看 Factory2 接口在 Activity 中的實(shí)現(xiàn):
public View onCreateView(@Nullable View parent, @NonNull String name,
@NonNull Context context, @NonNull AttributeSet attrs) {
if (!"fragment".equals(name)) {
return onCreateView(name, context, attrs);
}
return mFragments.onCreateView(parent, name, context, attrs);
}
可以看到這里對(duì) XML 中的 fragment 標(biāo)記進(jìn)行了處理雪位,F(xiàn)ragmentController#onCreateView 方法會(huì)對(duì) Fragment 進(jìn)行實(shí)例化竭钝,并調(diào)用 Fragment 實(shí)現(xiàn)的 onCreateView 方法來(lái)返回 View 對(duì)象。
到此為止我們就知道了 XML 中的 fragment 標(biāo)簽是如何被處理的茧泪。
6. final
Android 系統(tǒng)通過(guò)給 LayoutInflater 設(shè)置工廠的方式蜓氨,自己決定 View 的實(shí)例化,以此來(lái)實(shí)現(xiàn)向前兼容队伟,利用 setFactory 方法我們還可以做到很多事情穴吹,比如全局字體的替換,給特定的 View 設(shè)置特定的背景...
同時(shí)我也只對(duì)最基礎(chǔ)的流程進(jìn)行分析嗜侮,里面對(duì) merge港令、include、viewStub 等標(biāo)簽的處理并沒(méi)有展開锈颗,其實(shí)這些標(biāo)簽也只是遞歸的進(jìn)行 View 的創(chuàng)建并添加進(jìn)容器而已顷霹。
參考鏈接: