在上一篇文章中咱们介绍了 AsyncLayoutInflater 使用的注意事项及改进方案。android
建议先回顾下以前五篇文章,这个系列的文章从前日后顺序看最佳:bash
本篇文章咱们来学习下 layout 中 fragment 标签的加载过程,本文基于 Android 8.1.0。微信
各位老司机确定对 Fragment 的使用都很是熟悉,咱们简单回顾下:Fragment 的添加方式有两种:静态添加和动态添加。而静态添加就是在布局中写上 Fragment 的相关引用,以下:app
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment
android:id="@+id/fragment"
android:name="com.example.MainFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</FrameLayout>
复制代码
这个 layout 文件是相对特殊的,由于这个 fragment 标签不是很常见,并且你们回忆下 LayoutInflater 的 inflate 流程,其中 inflate 方法的返回值是 View。而咱们看下 Fragment 的定义:ide
public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener {
}
复制代码
能够看到 Fragment 并非一个 View,那说明 fragment 标签就不是经过正常的反射来建立的,进一步说就是 fragment 标签的建立和普通的 view 不是一个流程。函数
问题:既然 fragment 标签的建立和普通的 view 不是一个流程,那 fragment 标签是怎么加载的呢?布局
首先咱们想下前提条件:fragment 标签仍然是处于布局文件中的。就是说 fragment 标签节点也会被 LayoutInflater 解析,只是被解析以后的流程和别的 view 不同了。一路跟踪流程,咱们来到了 LayoutInflater 的 createViewFromTag 方法:学习
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);
}
if (view == null) {
final Object lastContext = mConstructorArgs[0];
mConstructorArgs[0] = context;
try {
if (-1 == name.indexOf('.')) {
view = onCreateView(parent, name, attrs);
} else {
view = createView(name, null, attrs);
}
} finally {
mConstructorArgs[0] = lastContext;
}
}
复制代码
我贴出来这段代码是为了总结下经过 setContentView 这种方式建立出 View 的途径:ui
其中一、二、4方式相信看过前面几篇文章的小伙伴确定都很熟悉了:this
到了这里咱们知道经过 setContentView 这种方式建立出 View 的途径有4种,其中第4种咱们直接排除掉了,也就只剩下了前三种方式。
在咱们探索到底是这三种方式中的哪种以前,咱们先来熟悉下 mPrivateFactory。咱们看下它的定义及设值的地方:
private Factory2 mPrivateFactory;// 定义能够看出 mPrivateFactory 也实现了 Factory2
protected LayoutInflater(LayoutInflater original, Context newContext) {
mContext = newContext;
mFactory = original.mFactory;
mFactory2 = original.mFactory2;
mPrivateFactory = original.mPrivateFactory;
setFilter(original.mFilter);
}
/**
* @hide for use by framework
*/
public void setPrivateFactory(Factory2 factory) {
if (mPrivateFactory == null) {
mPrivateFactory = factory;
} else {
mPrivateFactory = new FactoryMerger(factory, factory, mPrivateFactory, mPrivateFactory);
}
}
复制代码
咱们就知道了 mPrivateFactory 实现了 Factory2 接口,设值方式有两种,一种是 framework 调用,还有一种是建立 LayoutInflater 的时候传入。
这三种方式有一个共同特色就是都和 Factory 相关。而使用 Factory 都会经过 LayoutInflater setFactory,既然咱们没有作事情就完成了对 fragment 标签的解析,那有理由相信是系统处理了。使用 Fragment 的时候须要继承 FragmentActivity 或者是 AppCompatActivity,这里就以 FragmentActivity 为例来分析,来搜下哪里调用了 setFactory 函数。
可是在 FragmentActivity 的继承链上的各个类咱们并无搜到 setFactory 或 setFactory2。这两个常规的设置没有找到,咱们再来找第三种方式 setPrivateFactory,最终在 Activity 搜到了,attach 方法中:
mWindow.getLayoutInflater().setPrivateFactory(this);
复制代码
而后咱们看下 Activity 的定义,实现了 LayoutInflater.Factory2 接口:
public class Activity extends ContextThemeWrapper
implements LayoutInflater.Factory2,
Window.Callback, KeyEvent.Callback,
OnCreateContextMenuListener, ComponentCallbacks2,
Window.OnWindowDismissedCallback, WindowControllerCallback,
AutofillManager.AutofillClient {
@Nullable
public View onCreateView(String name, Context context, AttributeSet attrs) {
return null;
}
public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
if (!"fragment".equals(name)) {
return onCreateView(name, context, attrs);
}
return mFragments.onCreateView(parent, name, context, attrs);
}
}
复制代码
能够看出来在 onCreateView 方法中会判断标签名字若是是 fragment 的话则会调用 mFragments.onCreateView 来建立 View。
接下来总结下流程:
mFragments 实际上是 FragmentController,而后细跟代码会走到 FragmentManager 的 onCreateView 方法:
@Override
public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
if (!"fragment".equals(name)) {
return null;
}
moveToState(fragment, Fragment.CREATED, 0, 0, false);
if (fragment.mView == null) {
throw new IllegalStateException("Fragment " + fname
+ " did not create a view.");
}
return fragment.mView;
}
复制代码
而后到了 moveToState 方法,注意传入的 newState 是 Fragment.CREATED。
void moveToState(Fragment f, int newState, int transit, int transitionStyle,
boolean keepActive) {
case Fragment.CREATED:
if (newState > Fragment.CREATED) {
if (DEBUG) Log.v(TAG, "moveto ACTIVITY_CREATED: " + f);
if (!f.mFromLayout) {
ViewGroup container = null;
if (f.mContainerId != 0) {
if (f.mContainerId == View.NO_ID) {
throwException(new IllegalArgumentException(
"Cannot create fragment "
+ f
+ " for a container view with no id"));
}
container = mContainer.onFindViewById(f.mContainerId);
if (container == null && !f.mRestored) {
String resName;
try {
resName = f.getResources().getResourceName(f.mContainerId);
} catch (NotFoundException e) {
resName = "unknown";
}
throwException(new IllegalArgumentException(
"No view found for id 0x"
+ Integer.toHexString(f.mContainerId) + " ("
+ resName
+ ") for fragment " + f));
}
}
f.mContainer = container;
// 重点:最关键的方法在这里
f.mView = f.performCreateView(f.performGetLayoutInflater(
f.mSavedFragmentState), container, f.mSavedFragmentState);
if (f.mView != null) {
f.mView.setSaveFromParentEnabled(false);
if (container != null) {
container.addView(f.mView);
}
if (f.mHidden) {
f.mView.setVisibility(View.GONE);
}
f.onViewCreated(f.mView, f.mSavedFragmentState);
dispatchOnFragmentViewCreated(f, f.mView, f.mSavedFragmentState,
false);
// Only animate the view if it is visible. This is done after
// dispatchOnFragmentViewCreated in case visibility is changed
f.mIsNewlyAdded = (f.mView.getVisibility() == View.VISIBLE)
&& f.mContainer != null;
}
}
f.performActivityCreated(f.mSavedFragmentState);
dispatchOnFragmentActivityCreated(f, f.mSavedFragmentState, false);
if (f.mView != null) {
f.restoreViewState(f.mSavedFragmentState);
}
f.mSavedFragmentState = null;
}
}
复制代码
而后到了 Fragment 的 performCreateView 方法:
View performCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
if (mChildFragmentManager != null) {
mChildFragmentManager.noteStateNotSaved();
}
mPerformedCreateView = true;
return onCreateView(inflater, container, savedInstanceState);
}
复制代码
在最后一行咱们再次看到了 onCreateView 方法,这个 onCreateView 就是 Fragment 的一个方法,咱们在开发中须要覆写的那个。Fragment 的 performCreateView() 方法的返回值是一个 View ,这个View 被返回给了 Activity 中的 onCreateView 方法;这样就实现了遇到 fragment 标签特殊处理并返回 view。
本文主要学习 layout 中 fragment 标签的建立过程,而且将思考、分析的过程也写了下来,但愿对你们阅读源码、思考问题有所帮助。
咱们再来回顾下 fragment 标签的建立过程:
今日头条各Android客户端团队招人火爆进行中,各个级别和应届实习生都须要,业务增加快、日活高、挑战大、待遇给力,各位大佬走过路过千万不要错过!
本科以上学历、对技术有热情,欢迎加个人微信详聊:KOBE8242011