Navigation初始化NavHostFragment过程
Navigation初始化NavHostFragment过程
在使用Navigation库是,其固定模式是在Activity的布局文件中会先放一个NavHostFragment,其必须包含一个属性app:navGraph,指向navigation类型的资源文件,该文件以树结构形式定义了所有fagment交互的方式。但是当该Activity启动完成后却没有发现NavHostFragment的踪影,因此通过源码来分析Navigation初始化NavHostFragment过程。以解决以下疑惑:
- NavHostFragment的作用是什么?
- navigation资源文件是怎么被解析的?
- 在使用<navigation>、<fragment>、<action>、<deeplink>、<include>时,其不同属性值作用是什么
- NavHostFragment是怎么消失的
google官方使用教程指出:navogation的初始化依赖于一个特殊的Fragment NavHostFragment,在布局中<fragment>标签中需要指明属性app:navGraph,不然会抛出异常,其值为navigation布局文件的名称,也即<navigation>下的内容。如下使用场景中,MainActivity的布局文件为:
<androidx.drawerlayout.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.android.codelabs.navigation.MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
// 省略部分布局
<fragment
android:id="@+id/my_nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:navGraph="@navigation/mobile_navigation" />
</LinearLayout>
</androidx.drawerlayout.widget.DrawerLayout>
mobile_navigation的文件为:
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
app:startDestination="@+id/home_dest">
<fragment
android:id="@+id/home_dest"
android:name="com.example.android.codelabs.navigation.HomeFragment"
android:label="@string/home"
tools:layout="@layout/home_fragment">
<action
android:id="@+id/next_action"
app:destination="@+id/flow_step_one_dest"
app:enterAnim="@anim/slide_in_right"
app:exitAnim="@anim/slide_out_left"
app:popEnterAnim="@anim/slide_in_left"
app:popExitAnim="@anim/slide_out_right" />
</fragment>
</navigation>
在<navigation>标签下属性app:startDestination指明了该<navigation>标签树中第一个显示的fragment,因此MainActivity启动后显示的Fragment的id为@+id/home_dest。
1. MainActivity的初始化
下面看一个从MainActicity启动并显示HomeFragment的过程。首先代码的入口是MainActivity中的onCreat(),会执行setContentView(R.layout.navigation_activity),其中会调用LayoutInflater.inflate()来解析xml布局文件。MainActivity继承于AppCompatActivity,间接继承于FragmentActivity,接着会执行FragmentActivity生命周期相关方法onCreateView()方法,在该方法中进一步调用dispatchFragmentsOnCreateView()方法来分发onCreateView给fragments:
final View dispatchFragmentsOnCreateView(@Nullable View parent, @NonNull String name,
@NonNull Context context, @NonNull AttributeSet attrs) {
return mFragments.onCreateView(parent, name, context, attrs);
}
这里的mFragments是一个FragmentController对象,其主要作用是对mHost进行代理,mHost是一个FragmentHostCallback对象:
//FragmentController.java
public View onCreateView(@Nullable View parent, @NonNull String name, @NonNull Context context,
@NonNull AttributeSet attrs) {
return mHost.mFragmentManager.onCreateView(parent, name, context, attrs);
}
//FragmentHostCallback.java
public abstract class FragmentHostCallback<E> extends FragmentContainer {
@Nullable private final Activity mActivity;
@NonNull private final Context mContext;
@NonNull private final Handler mHandler;
private final int mWindowAnimations;
final FragmentManagerImpl mFragmentManager = new FragmentManagerImpl();
}
可以看出FragmentHostCallback引用了mActivity、mContext等全局变量,且其在FragmentController的名字叫mHost,并由FragmentActivity传入该匿名对象,可知其作用就是将fragment的操作传递给其宿主FragmentActivity。最后fragmen的创建是在FragmentManagerImpl中完成的:
//FragmentManagerImpl.java
public View onCreateView(@Nullable View parent, @NonNull String name, @NonNull Context context,
@NonNull AttributeSet attrs) {
if (!"fragment".equals(name)) {
return null;
}
//fname的值就是xml中设置的属性值androidx.navigation.fragment.NavHostFragment
String fname = attrs.getAttributeValue(null, "class");
TypedArray a = context.obtainStyledAttributes(attrs, FragmentTag.Fragment);
if (fname == null) {
fname = a.getString(FragmentTag.Fragment_name);
}
int id = a.getResourceId(FragmentTag.Fragment_id, View.NO_ID);
String tag = a.getString(FragmentTag.Fragment_tag);
a.recycle();
int containerId = parent != null ? parent.getId() : 0;
if (containerId == View.NO_ID && id == View.NO_ID && tag == null) {
throw new IllegalArgumentException(attrs.getPositionDescription()
+ ": Must specify unique android:id, android:tag, or have a parent with an id for " + fname);
}
Fragment fragment = id != View.NO_ID ? findFragmentById(id) : null;
if (fragment == null && tag != null) {
fragment = findFragmentByTag(tag);
}
if (fragment == null && containerId != View.NO_ID) {
fragment = findFragmentById(containerId);
}
//第一次启动时,上面所有findFragmen操作都会为null
if (fragment == null) {
fragment = getFragmentFactory().instantiate(context.getClassLoader(), fname);
fragment.mFromLayout = true;
fragment.mFragmentId = id != 0 ? id : containerId;
fragment.mContainerId = containerId;
fragment.mTag = tag;
fragment.mInLayout = true;
fragment.mFragmentManager = this;
fragment.mHost = mHost;
fragment.onInflate(mHost.getContext(), attrs, fragment.mSavedFragmentState);
addFragment(fragment, true);
} else if (fragment.mInLayout) {
//省略
} else {
//省略
}
if (mCurState < Fragment.CREATED && fragment.mFromLayout) {
moveToState(fragment, Fragment.CREATED, 0, 0, false);
} else {
moveToState(fragment);
}
if (fragment.mView == null) {
throw new IllegalStateException("Fragment " + fname
+ " did not create a view.");
}
if (id != 0) {
fragment.mView.setId(id);
}
if (fragment.mView.getTag() == null) {
fragment.mView.setTag(tag);
}
return fragment.mView;
}
这里通findFragment均为null,因此会通过ClassLorder加载名为fname的类,并实例化一个对象。在执行NavHostFragment的onInflate()方法时主要解析了xml中属性名app:navGraph的值,并保存在mGraphId字段中,用于下一步生成跳转图对象。接着调用addFragment()在FragmentManagerImpl的调用链为:
2. NavHostFragment的初始化
下面看一下NavHostFragment类中onCreate()方法的实现:
//NavHostFragment.java
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final Context context = requireContext();
//2.1. 初始化NavHostController实例
mNavController = new NavHostController(context);
mNavController.setLifecycleOwner(this);
mNavController.setOnBackPressedDispatcher(requireActivity().getOnBackPressedDispatcher());
// Set the default state - this will be updated whenever
// onPrimaryNavigationFragmentChanged() is called
mNavController.enableOnBackPressed(
mIsPrimaryBeforeOnCreate != null && mIsPrimaryBeforeOnCreate);
mIsPrimaryBeforeOnCreate = null;
mNavController.setViewModelStore(getViewModelStore());
onCreateNavController(mNavController);
//在onSaveInstanceState()会以KEY_NAV_CONTROLLER_STATE为key缓存mNavController中的一个Bundle对象
Bundle navState = null;
if (savedInstanceState != null) {
navState = savedInstanceState.getBundle(KEY_NAV_CONTROLLER_STATE);
if (savedInstanceState.getBoolean(KEY_DEFAULT_NAV_HOST, false)) {
mDefaultNavHost = true;
requireFragmentManager().beginTransaction()
.setPrimaryNavigationFragment(this)
.commit();
}
}
if (navState != null) {
// Navigation controller state overrides arguments
mNavController.restoreState(navState);
}
if (mGraphId != 0) {
//2.2. 调用NavHostController实例解析mGraphId
//初始化时会走到这里
// Set from onInflate()
mNavController.setGraph(mGraphId);
} else {
// See if it was set by NavHostFragment.create()
final Bundle args = getArguments();
final int graphId = args != null ? args.getInt(KEY_GRAPH_ID) : 0;
final Bundle startDestinationArgs = args != null
? args.getBundle(KEY_START_DESTINATION_ARGS)
: null;
if (graphId != 0) {
mNavController.setGraph(graphId, startDestinationArgs);
}
}
}
这段代码比较长,分段看一下:
2.1. 初始化NavHostController实例
首先看一下new操作做了什么:
public NavController(@NonNull Context context) {
mContext = context;
//确保mActivity为Activity对象
while (context instanceof ContextWrapper) {
if (context instanceof Activity) {
mActivity = (Activity) context;
break;
}
context = ((ContextWrapper) context).getBaseContext();
}
mNavigatorProvider.addNavigator(new NavGraphNavigator(mNavigatorProvider));
mNavigatorProvider.addNavigator(new ActivityNavigator(mContext));
}
主要就是往类型为NavigatorProvider的mNavigatorProvider中添加一个NavGraphNavigator对象和一个ActivityNavigator对象,NavGraphNavigator即是初始化NavHostFragment时会用来跳转到app:startDestination属性值。添加的过程会是分别获取该类的注解Navigator.Name值做为key,相应对象做为value然后存储在NavigatorProvider中名为mNavigators的Map中。之后会设置mNavController的生命周期宿主、按Back键的分发器等,然后将该NavHostFragment的ViewModel的存储对象设置给了mNavController,在看一下onCreateNavController()中代码:
protected void onCreateNavController(@NonNull NavController navController) {
navController.getNavigatorProvider().addNavigator(
new DialogFragmentNavigator(requireContext(), getChildFragmentManager()));
navController.getNavigatorProvider().addNavigator(createFragmentNavigator());
}
protected Navigator<? extends FragmentNavigator.Destination> createFragmentNavigator() {
return new FragmentNavigator(requireContext(), getChildFragmentManager(), getId());
}
其操作和上面很相似,都是往mNavigatorProvider对象中添加用于DialogFragment跳转的DialogFragmentNavigator对象和用于Fragment跳转的对象
FragmentNavigator。
2.2. 调用NavHostController实例解析mGraphId
通过注释可知在onInflate中解析并保存了mGraphId值,所以现在需要解析mGraphId指代的<navigation>文件,内部代码:
//NavController.java
public void setGraph(@NavigationRes int graphResId, @Nullable Bundle startDestinationArgs) {
setGraph(getNavInflater().inflate(graphResId), startDestinationArgs);
}
可以看到用NavInflater解析该xml文件并生成一个NavGraph对象,并设置给NavController中的mGraph字段保存。看一下解析过程:
private NavDestination inflate(@NonNull Resources res, @NonNull XmlResourceParser parser,
@NonNull AttributeSet attrs, int graphResId)
throws XmlPullParserException, IOException {
Navigator navigator = mNavigatorProvider.getNavigator(parser.getName());
final NavDestination dest = navigator.createDestination();
dest.onInflate(mContext, attrs);
final int innerDepth = parser.getDepth() + 1;
int type;
int depth;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& ((depth = parser.getDepth()) >= innerDepth
|| type != XmlPullParser.END_TAG)) {
if (type != XmlPullParser.START_TAG) {
continue;
}
if (depth > innerDepth) {
continue;
}
final String name = parser.getName();
if (TAG_ARGUMENT.equals(name)) {
inflateArgumentForDestination(res, dest, attrs, graphResId);
} else if (TAG_DEEP_LINK.equals(name)) {
inflateDeepLink(res, dest, attrs);
} else if (TAG_ACTION.equals(name)) {
inflateAction(res, dest, attrs, parser, graphResId);
} else if (TAG_INCLUDE.equals(name) && dest instanceof NavGraph) {
final TypedArray a = res.obtainAttributes(attrs, R.styleable.NavInclude);
final int id = a.getResourceId(R.styleable.NavInclude_graph, 0);
((NavGraph) dest).addDestination(inflate(id));
a.recycle();
} else if (dest instanceof NavGraph) {
((NavGraph) dest).addDestination(inflate(res, parser, attrs, graphResId));
}
}
return dest;
}
通过前面分析mNavigatorProvider已经包含了四组键值对:
2.2.1. <navigation>标签解析成NavGraph对象
这里parser解析的xml文件的顶层标签是<navigation>,因此调用其getName()方法返回值为"navigation",这正好和NavGraphNavigator类的Navigator.Name注解值是一致的,因此在mNavigatorProvider中获取的navigator实际是一个NavGraphNavigator对象,通过其createDestination()方法获得的dest是一个NavGraph对象,在执行其onInflate()方法:
public void onInflate(@NonNull Context context, @NonNull AttributeSet attrs) {
super.onInflate(context, attrs);
TypedArray a = context.getResources().obtainAttributes(attrs,
R.styleable.NavGraphNavigator);
setStartDestination(
a.getResourceId(R.styleable.NavGraphNavigator_startDestination, 0));
mStartDestIdName = getDisplayName(context, mStartDestId);
a.recycle();
}
先是获取<navigation>标签下startDestination属性的资源id,并保存在mStartDestId字段中,然后通过mStartDestId获取mStartDestIdName,这里其值为com.example.android.codelabs.navigation:id/home_dest。因为dest类型为NavGraph,因此进入循环后会执行 ((NavGraph) dest).addDestination()方法:
//NavGraph.java
public final void addDestination(@NonNull NavDestination node) {
if (node.getId() == 0) {
throw new IllegalArgumentException("Destinations must have an id."
+ " Call setId() or include an android:id in your navigation XML.");
}
NavDestination existingDestination = mNodes.get(node.getId());
if (existingDestination == node) {
return;
}
if (node.getParent() != null) {
throw new IllegalStateException("Destination already has a parent set."
+ " Call NavGraph.remove() to remove the previous parent.");
}
if (existingDestination != null) {
existingDestination.setParent(null);
}
node.setParent(this);
mNodes.put(node.getId(), node);
}
通过node.setParent(this);可知<navagation>标签被解析成的NavGraph是所有节点的父亲节点,NavGraph内部类型为SparseArrayCompat的mNodes存储了所有子节点。
2.2.2. <fragment>标签解析成Destination对象
解析完<navagation>标签后,会递归调用inflate(),这一次parser.getName()返回的值为"fragment",因此此次获取的navigator的实际类型是FragmentNavigator,其createDestination()方法返回一个Destination对象,看一下该对象的onInflate()方法:
public void onInflate(@NonNull Context context, @NonNull AttributeSet attrs) {
super.onInflate(context, attrs);
TypedArray a = context.getResources().obtainAttributes(attrs,
R.styleable.FragmentNavigator);
String className = a.getString(R.styleable.FragmentNavigator_android_name);
if (className != null) {
setClassName(className);
}
a.recycle();
}
回顾前面定义<navigation>的xml文件可知这里的className = com.example.android.codelabs.navigation.HomeFragment,并把它保存在mClassName字段中。
2.2.3. <action>标签解析成NavAction对象
进入while循环parser.next()会指向<action>标签,因此循环内的name = “action”,因此会进入inflateAction()分支:
private void inflateAction(@NonNull Resources res, @NonNull NavDestination dest,
@NonNull AttributeSet attrs, XmlResourceParser parser, int graphResId)
throws IOException, XmlPullParserException {
final TypedArray a = res.obtainAttributes(attrs, R.styleable.NavAction);
final int id = a.getResourceId(R.styleable.NavAction_android_id, 0);
final int destId = a.getResourceId(R.styleable.NavAction_destination, 0);
NavAction action = new NavAction(destId);
NavOptions.Builder builder = new NavOptions.Builder();
builder.setLaunchSingleTop(a.getBoolean(R.styleable.NavAction_launchSingleTop, false));
builder.setPopUpTo(a.getResourceId(R.styleable.NavAction_popUpTo, -1),
a.getBoolean(R.styleable.NavAction_popUpToInclusive, false));
builder.setEnterAnim(a.getResourceId(R.styleable.NavAction_enterAnim, -1));
builder.setExitAnim(a.getResourceId(R.styleable.NavAction_exitAnim, -1));
builder.setPopEnterAnim(a.getResourceId(R.styleable.NavAction_popEnterAnim, -1));
builder.setPopExitAnim(a.getResourceId(R.styleable.NavAction_popExitAnim, -1));
action.setNavOptions(builder.build());
Bundle args = new Bundle();
final int innerDepth = parser.getDepth() + 1;
int type;
int depth;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& ((depth = parser.getDepth()) >= innerDepth
|| type != XmlPullParser.END_TAG)) {
if (type != XmlPullParser.START_TAG) {
continue;
}
if (depth > innerDepth) {
continue;
}
final String name = parser.getName();
if (TAG_ARGUMENT.equals(name)) {
inflateArgumentForBundle(res, args, attrs, graphResId);
}
}
if (!args.isEmpty()) {
action.setDefaultArguments(args);
}
dest.putAction(id, action);
a.recycle();
}
看到通过解析<action>标签下的属性,并构造一个类型为NavAction的对象action,中间的while循环主要是解析标签,最后以该<action>标签的id为key,解析的action对象为值,存储在dest对象内部的类型为SparseArrayCompat的mActions中。
3. 跳转到startDestination
在2.2这一步中将<navigation>资源文件解析成NavGraph,并调用setGragh()方法设置给mGraph:
//NavController.java
public void setGraph(@NonNull NavGraph graph, @Nullable Bundle startDestinationArgs) {
if (mGraph != null) {
// Pop everything from the old graph off the back stack
popBackStackInternal(mGraph.getId(), true);
}
mGraph = graph;
onGraphCreated(startDestinationArgs);
}
private void onGraphCreated(@Nullable Bundle startDestinationArgs) {
//省略部分代码
if (mGraph != null && mBackStack.isEmpty()) {
boolean deepLinked = !mDeepLinkHandled && mActivity != null
&& handleDeepLink(mActivity.getIntent());
if (!deepLinked) {
// Navigate to the first destination in the graph
// if we haven't deep linked to a destination
navigate(mGraph, startDestinationArgs, null, null);
}
}
}
可以看到在启动无deepLinked的情况下会调用navigate()方法进行跳转:
//NavController.java
private void navigate(@NonNull NavDestination node, @Nullable Bundle args,
@Nullable NavOptions navOptions, @Nullable Navigator.Extras navigatorExtras) {
boolean popped = false;
if (navOptions != null) {
if (navOptions.getPopUpTo() != -1) {
popped = popBackStackInternal(navOptions.getPopUpTo(),
navOptions.isPopUpToInclusive());
}
}
Navigator<NavDestination> navigator = mNavigatorProvider.getNavigator(
node.getNavigatorName());
Bundle finalArgs = node.addInDefaultArgs(args);
//跳转
NavDestination newDest = navigator.navigate(node, finalArgs,
navOptions, navigatorExtras);
if (newDest != null) {
if (!(newDest instanceof FloatingWindow)) {
// We've successfully navigating to the new destination, which means
// we should pop any FloatingWindow destination off the back stack
// before updating the back stack with our new destination
//noinspection StatementWithEmptyBody
while (!mBackStack.isEmpty()
&& mBackStack.peekLast().getDestination() instanceof FloatingWindow
&& popBackStackInternal(
mBackStack.peekLast().getDestination().getId(), true)) {
// Keep popping
}
}
// The mGraph should always be on the back stack after you navigate()
if (mBackStack.isEmpty()) {
mBackStack.add(new NavBackStackEntry(mGraph, finalArgs, mViewModel));
}
// Now ensure all intermediate NavGraphs are put on the back stack
// to ensure that global actions work.
ArrayDeque<NavBackStackEntry> hierarchy = new ArrayDeque<>();
NavDestination destination = newDest;
while (destination != null && findDestination(destination.getId()) == null) {
NavGraph parent = destination.getParent();
if (parent != null) {
hierarchy.addFirst(new NavBackStackEntry(parent, finalArgs, mViewModel));
}
destination = parent;
}
mBackStack.addAll(hierarchy);
// And finally, add the new destination with its default args
NavBackStackEntry newBackStackEntry = new NavBackStackEntry(newDest,
newDest.addInDefaultArgs(finalArgs), mViewModel);
mBackStack.add(newBackStackEntry);
}
updateOnBackPressedCallbackEnabled();
if (popped || newDest != null) {
dispatchOnDestinationChanged();
}
}
这里传入的参数node实际类型是NavGragh,navOptions = null,通过前面分析可知node.getNavigatorName()所得结果为“navigation”,这里navigator即为一个NavGraphNavigator对象,其navigation()代码为:
//NavGraphNavigator.java
public NavDestination navigate(@NonNull NavGraph destination, @Nullable Bundle args,
@Nullable NavOptions navOptions, @Nullable Extras navigatorExtras) {
int startId = destination.getStartDestination();
if (startId == 0) {
throw new IllegalStateException("no start destination defined via"
+ " app:startDestination for "
+ destination.getDisplayName());
}
NavDestination startDestination = destination.findNode(startId, false);
if (startDestination == null) {
final String dest = destination.getStartDestDisplayName();
throw new IllegalArgumentException("navigation destination " + dest
+ " is not a direct child of this NavGraph");
}
Navigator<NavDestination> navigator = mNavigatorProvider.getNavigator(
startDestination.getNavigatorName());
return navigator.navigate(startDestination, startDestination.addInDefaultArgs(args),
navOptions, navigatorExtras);
}
通过<navigation>xml文件可知这里的startId和<fragment>标签下的id是同一个,前文分析了NavGragh是所以节点的祖先节点,其内部字段mNodes以节点id为key保存了其子节点,因此调用NavGragh的findNode()方法实际拿到一个对应<fragment>标签的Destination对象startDestination,因此这里的navigator即是一个FragmentNavigator,进一步调用其navigate()方法:
//FragmentNavigator.java
public NavDestination navigate(@NonNull Destination destination, @Nullable Bundle args,
@Nullable NavOptions navOptions, @Nullable Navigator.Extras navigatorExtras) {
if (mFragmentManager.isStateSaved()) {
Log.i(TAG, "Ignoring navigate() call: FragmentManager has already"
+ " saved its state");
return null;
}
String className = destination.getClassName();
if (className.charAt(0) == '.') {
className = mContext.getPackageName() + className;
}
final Fragment frag = instantiateFragment(mContext, mFragmentManager,
className, args);
frag.setArguments(args);
final FragmentTransaction ft = mFragmentManager.beginTransaction();
int enterAnim = navOptions != null ? navOptions.getEnterAnim() : -1;
int exitAnim = navOptions != null ? navOptions.getExitAnim() : -1;
int popEnterAnim = navOptions != null ? navOptions.getPopEnterAnim() : -1;
int popExitAnim = navOptions != null ? navOptions.getPopExitAnim() : -1;
if (enterAnim != -1 || exitAnim != -1 || popEnterAnim != -1 || popExitAnim != -1) {
enterAnim = enterAnim != -1 ? enterAnim : 0;
exitAnim = exitAnim != -1 ? exitAnim : 0;
popEnterAnim = popEnterAnim != -1 ? popEnterAnim : 0;
popExitAnim = popExitAnim != -1 ? popExitAnim : 0;
ft.setCustomAnimations(enterAnim, exitAnim, popEnterAnim, popExitAnim);
}
ft.replace(mContainerId, frag);
ft.setPrimaryNavigationFragment(frag);
final @IdRes int destId = destination.getId();
final boolean initialNavigation = mBackStack.isEmpty();
// TODO Build first class singleTop behavior for fragments
final boolean isSingleTopReplacement = navOptions != null && !initialNavigation
&& navOptions.shouldLaunchSingleTop()
&& mBackStack.peekLast() == destId;
boolean isAdded;
if (initialNavigation) {
isAdded = true;
} else if (isSingleTopReplacement) {
// Single Top means we only want one instance on the back stack
if (mBackStack.size() > 1) {
// If the Fragment to be replaced is on the FragmentManager's
// back stack, a simple replace() isn't enough so we
// remove it from the back stack and put our replacement
// on the back stack in its place
mFragmentManager.popBackStack(
generateBackStackName(mBackStack.size(), mBackStack.peekLast()),
FragmentManager.POP_BACK_STACK_INCLUSIVE);
ft.addToBackStack(generateBackStackName(mBackStack.size(), destId));
}
isAdded = false;
} else {
ft.addToBackStack(generateBackStackName(mBackStack.size() + 1, destId));
isAdded = true;
}
ft.setReorderingAllowed(true);
ft.commit();
// The commit succeeded, update our view of the world
if (isAdded) {
mBackStack.add(destId);
return destination;
} else {
return null;
}
}
这里的className = com.example.android.codelabs.navigation.HomeFragment,通过调用instantiateFragment()使用ClassLoader初始化该HomeFragment对象,这里的mContainerId是NavHostFragment的id,也即是xml中定义的@+id/my_nav_host_fragment,然后给HomeFragment对象设置了argument参数、进入退出动画,然后采用replace方式将NavHostFragment替换为HomeFragment。因为是初始化,所以initialNavigation = true,因此isAdded = true。这里的destination对应HomeFragment,destId即为@+id/home_dest,最后将其存储在mBackStack中。至此,已经完成MainActivity初始化 -> NavHostFragment初始化 -> HomeFragment初始化 -> NavHostFragment被替换为HomeFragment这一过程的分析。