Jetpack--Navigation原理
Jetpack向開發(fā)者提供了導(dǎo)航組件來實(shí)現(xiàn)Activity或者Fragment的跳轉(zhuǎn),今天就分析一下使用Navigation來實(shí)現(xiàn)Fragment之間的跳轉(zhuǎn)。
一、相關(guān)類介紹
1. NavHost
NavHost的定義如下:
public interface NavHost {
/**
* Returns the {@link NavController navigation controller} for this navigation host.
*
* @return this host's navigation controller
*/
@NonNull
NavController getNavController();
}
NavHost只要求其實(shí)現(xiàn)類必須能夠通過getNavController()方法返回一個(gè)NavController對象。
而常用的NavHostFragment即為NavHost的一個(gè)實(shí)現(xiàn)類:
public class NavHostFragment extends Fragment implements NavHost {
...
private NavHostController mNavController;
private View mViewParent;
// State that will be saved and restored
private int mGraphId;
private boolean mDefaultNavHost;
@CallSuper
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final Context context = requireContext();
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);
....
}
}
可見,NavHostFragment返回的NavController的運(yùn)行時(shí)類型為NavHostController。
同時(shí)需要注意的是,NavHostFragment初始化NavController的時(shí)機(jī)在onCreate方法之中,再次之前調(diào)用getNavController方法返回的只是null。
2、NavController
該類是一個(gè)核心類,其內(nèi)部含有一個(gè)導(dǎo)航圖對象(NavGraph),該導(dǎo)航圖通常是通過NavInflater解析navigation資源文件生成的。NavController通過持有的導(dǎo)航圖來控制導(dǎo)航行為,顧名思義,該對象就是一個(gè)控制器,控制著導(dǎo)航圖、對Fragment或Activity生命周期的管理等,一些核心的屬性如下:
public class NavController {
private final Context mContext;
private Activity mActivity;
private NavInflater mInflater; // 負(fù)責(zé)解析導(dǎo)航文件的解析器
@SuppressWarnings("WeakerAccess") /* synthetic access */
NavGraph mGraph; // NavInflater解析導(dǎo)航資源文件生成的導(dǎo)航圖
private NavigatorProvider mNavigatorProvider = new NavigatorProvider(); // 該類只是一個(gè)輔助類,負(fù)責(zé)保存用到的Navigator對象
@SuppressWarnings("WeakerAccess") /* synthetic access */
final Deque<NavBackStackEntry> mBackStack = new ArrayDeque<>();
private LifecycleOwner mLifecycleOwner;
private NavControllerViewModel mViewModel;
private NavigatorProvider mNavigatorProvider = new NavigatorProvider();
private final LifecycleObserver mLifecycleObserver = new LifecycleEventObserver() {
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
if (mGraph != null) {
for (NavBackStackEntry entry : mBackStack) {
entry.handleLifecycleEvent(event);
}
}
}
};
}
通過上小結(jié)指導(dǎo),NavHostFragment的getNavController()返回的NavController對象的運(yùn)行時(shí)類型為:NavHostController
public class NavHostController extends NavController {
public NavHostController(@NonNull Context context) {
super(context);
}
@Override
public final void setLifecycleOwner(@NonNull LifecycleOwner owner) {
super.setLifecycleOwner(owner);
}
@Override
public final void setOnBackPressedDispatcher(@NonNull OnBackPressedDispatcher dispatcher) {
super.setOnBackPressedDispatcher(dispatcher);
}
@Override
public final void enableOnBackPressed(boolean enabled) {
super.enableOnBackPressed(enabled);
}
@Override
public final void setViewModelStore(@NonNull ViewModelStore viewModelStore) {
super.setViewModelStore(viewModelStore);
}
}
觀察NavHostController的源碼發(fā)現(xiàn),其并沒有什么自己的內(nèi)容,各個(gè)方法全部都是父類的實(shí)現(xiàn),對于該類,我們可以暫且忘記,僅僅當(dāng)做就是NavController對象即可。
3、Navigator
NavController只是管理導(dǎo)航圖,導(dǎo)航動作開始于其內(nèi)部,但是導(dǎo)航動作根本發(fā)生地卻是在Navigator之中,可以說,Navigator是真正的導(dǎo)航器。Navigator是真正執(zhí)行導(dǎo)航動作(頁面跳轉(zhuǎn))的地方,其為開發(fā)者自動維護(hù)了導(dǎo)航所涉及的前后狀態(tài)。
public abstract class Navigator<D extends NavDestination> {
@Retention(RUNTIME)
@Target({TYPE})
@SuppressWarnings("UnknownNullness") // TODO https://issuetracker.google.com/issues/112185120
public @interface Name {
String value();
}
@NonNull
public abstract D createDestination();
@Nullable
public abstract NavDestination navigate(@NonNull D destination, @Nullable Bundle args,
@Nullable NavOptions navOptions, @Nullable Extras navigatorExtras);
public abstract boolean popBackStack();
@Nullable
public Bundle onSaveState() {
return null;
}
public void onRestoreState(@NonNull Bundle savedState) {
}
public interface Extras {
}
}
可見,Navigator負(fù)責(zé)的任務(wù)主要就是創(chuàng)建目的地,以及navigate到目標(biāo)頁面。
Google為開發(fā)者提供了一些Navigator,在第一部分的NavHostFragment的onCreate()方法中涉及到NavController對象的初始化,以及NavController初始化完畢后調(diào)用了方法onCreateNavController(mNavController),這兩處都初始化并保存了一些Navigator對象:
NavHostFragment:onCreate()
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
....
mNavController = new NavHostController(context); // NavController構(gòu)造函數(shù)中添加了一些Navigator
....
onCreateNavController(mNavController); // 該方法內(nèi)部也添加了一些Navigator
....
}
我們先來看看第二個(gè)地方的代碼。
NavHostFragment:onCreateNavController(mNavController)
@CallSuper
protected void onCreateNavController(@NonNull NavController navController) {
navController.getNavigatorProvider().addNavigator(
new DialogFragmentNavigator(requireContext(), getChildFragmentManager()));
navController.getNavigatorProvider().addNavigator(createFragmentNavigator());
}
@Deprecated
@NonNull
protected Navigator<? extends FragmentNavigator.Destination> createFragmentNavigator() {
return new FragmentNavigator(requireContext(), getChildFragmentManager(),
getContainerId());
}
可見在該方法內(nèi)部實(shí)例化并添加了兩個(gè)Navigator的實(shí)現(xiàn)類:
- DialogFragmentNavigator
- FragmentNavigator
而通過名字就能知道,他們一個(gè)是負(fù)責(zé)DialogFragment導(dǎo)航,一個(gè)是負(fù)責(zé)Fragment導(dǎo)航的。
NavController:NavController()
public NavController(@NonNull Context context) {
mContext = context;
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));
}
而在NavController的構(gòu)造函數(shù)中也添加了兩種Navigator:
- NavGraphNavigator
- ActivityNavigator
可以肯定后者是為了Activity之間的導(dǎo)航提供支持,而前者猜測是為了為navigation資源文件中導(dǎo)航嵌套之間的導(dǎo)航提供支持的。
同時(shí)也可以知道,這兩種Navigator初始化的位置位于NavController的構(gòu)造器中,而與Fragment導(dǎo)航相關(guān)的構(gòu)造器的添加則位于NavHostFragment之中,合理而又分明。
4、輔助類
NavigatorProvider
該類是一個(gè)輔助類,作用只是保存NavController使用到的Navigator對象,在上面的展示中也顯示了NavController都是通過其mNavigatorProvider屬性來操控Navigator對象的。
public class NavigatorProvider {
....
private final HashMap<String, Navigator<? extends NavDestination>> mNavigators =
new HashMap<>();
@NonNull
public final <T extends Navigator<?>> T getNavigator(@NonNull Class<T> navigatorClass) {
String name = getNameForNavigator(navigatorClass);
return getNavigator(name);
}
@SuppressWarnings("unchecked")
@CallSuper
@NonNull
public <T extends Navigator<?>> T getNavigator(@NonNull String name) {
if (!validateName(name)) {
throw new IllegalArgumentException("navigator name cannot be an empty string");
}
Navigator<? extends NavDestination> navigator = mNavigators.get(name);
if (navigator == null) {
throw new IllegalStateException("Could not find Navigator with name \"" + name
+ "\". You must call NavController.addNavigator() for each navigation type.");
}
return (T) navigator;
}
@Nullable
public final Navigator<? extends NavDestination> addNavigator(
@NonNull Navigator<? extends NavDestination> navigator) {
String name = getNameForNavigator(navigator.getClass());
return addNavigator(name, navigator);
}
@CallSuper
@Nullable
public Navigator<? extends NavDestination> addNavigator(@NonNull String name,
@NonNull Navigator<? extends NavDestination> navigator) {
if (!validateName(name)) {
throw new IllegalArgumentException("navigator name cannot be an empty string");
}
return mNavigators.put(name, navigator);
}
}
很清楚,NavigatorProvider內(nèi)部通過一個(gè)HashMap來保存Navigator對象,key為Navigator的類名。
NavDestination
根據(jù)名字就能夠知道,該類代表著navigation資源文件中action中定義的destination fragment(這里的destination fragment的含義為導(dǎo)航圖xml文件中作為destination的fragment,即一個(gè)destination fragment可能含有多個(gè)action,也即一個(gè)destination fragment可以通過多個(gè)action指定app:destination屬性跳轉(zhuǎn)到多個(gè)其他的destination fragment);而顯然,每一個(gè)NavDestination都應(yīng)該隸屬于一個(gè)NavGraph:
public class NavDestination {
....
private final String mNavigatorName;
private NavGraph mParent; // 指明該NavDestination隸屬于的NavGraph對象
private int mId;
private String mIdName;
private SparseArrayCompat<NavAction> mActions; // 每一個(gè)Destination對應(yīng)的Fragment可能含有多個(gè)Action
private HashMap<String, NavArgument> mArguments;
}
NavGraph
該對象代表了一個(gè)導(dǎo)航圖,其內(nèi)部保存了所有的NavDestination節(jié)點(diǎn),前面說到,NavController內(nèi)部持有一個(gè)NavGraph,而NavGraph在NavController內(nèi)部的作用就是在NavController發(fā)出導(dǎo)航指令的時(shí)候,NavGraph通過指令傳遞的id來找到對應(yīng)的NavDestination節(jié)點(diǎn)。
public class NavGraph extends NavDestination implements Iterable<NavDestination> {
@SuppressWarnings("WeakerAccess") /* synthetic access */
final SparseArrayCompat<NavDestination> mNodes = new SparseArrayCompat<>();
private int mStartDestId;
private String mStartDestIdName;
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);
}
public final void addDestinations(@NonNull Collection<NavDestination> nodes) {
for (NavDestination node : nodes) {
if (node == null) {
continue;
}
addDestination(node);
}
}
public final void addDestinations(@NonNull NavDestination... nodes) {
for (NavDestination node : nodes) {
if (node == null) {
continue;
}
addDestination(node);
}
}
@Nullable
public final NavDestination findNode(@IdRes int resid) {
return findNode(resid, true);
}
@Nullable
final NavDestination findNode(@IdRes int resid, boolean searchParents) {
NavDestination destination = mNodes.get(resid);
// Search the parent for the NavDestination if it is not a child of this navigation graph
// and searchParents is true
return destination != null
? destination
: searchParents && getParent() != null ? getParent().findNode(resid) : null;
}
}
首先可以看到,mNodes對象就是保存該導(dǎo)航圖下所有NavDestination節(jié)點(diǎn)的容器。而且,NavGraph也提供了一些addDestination和findNode的方法用來保存和獲取NavDestination對象。
注:NavGraph的addDestination()方法的調(diào)用時(shí)機(jī)為NavHostFragment的onInflate中,在解析導(dǎo)航資源文件的時(shí)候會直接將解析得到的NavDestination加入到NavGraph中。
這里有一個(gè)稍微有一些繞的地方,那就是NavGraph的作用是保存導(dǎo)航圖中的NavDestination節(jié)點(diǎn),但是他自己本身卻是NavDestination的子類,總歸有些奇怪。個(gè)人認(rèn)為,這是一種不得已,因?yàn)樵趯?dǎo)航圖的資源文件中可以存在navigation嵌套的情況,而這種情況下勢必存在一個(gè)NavGraph作為NavDestination的角色出現(xiàn),所以NavGraph是NavDestination就顯得不得已而為之了。
NavAction
上面提到一個(gè)NavDestination也即一個(gè)可能的目的地,而一個(gè)目的地又可以有多個(gè)Action跳轉(zhuǎn)到其他的目的地,因此,在NavDestination中含有保存所有其下action的屬性。而該類顯然就是navigation資源文件中的Action的封裝了,且看:
public final class NavAction {
@IdRes
private final int mDestinationId;
private NavOptions mNavOptions;
private Bundle mDefaultArguments;
....
}
根據(jù)變量就能夠知道上述變量的作用。
NavOptions
該類為navigation資源文件中每一個(gè)Action標(biāo)簽下的一些選項(xiàng)屬性:
public final class NavOptions {
private boolean mSingleTop;
@IdRes
private int mPopUpTo;
private boolean mPopUpToInclusive;
@AnimRes @AnimatorRes
private int mEnterAnim;
@AnimRes @AnimatorRes
private int mExitAnim;
@AnimRes @AnimatorRes
private int mPopEnterAnim;
@AnimRes @AnimatorRes
private int mPopExitAnim;
}
小結(jié)
經(jīng)過上述的介紹,我們可以簡單得到一個(gè)UML圖:

可以很明顯的看出NavController的管理作用:
- NavGraph管理著navigation資源文件中定義的destination fragment,并將其封裝成NavDestination;而NavDestination則將資源文件中的action標(biāo)簽封裝成NavAction對象,而NavAction對象中包含了可以在資源文件中定義的各個(gè)options設(shè)置;
- Navigator則負(fù)責(zé)導(dǎo)航動作的執(zhí)行,該對象通過NavigatorProvider對象的存取功能向NavController提供服務(wù)。
- NavController為導(dǎo)航發(fā)起的位置,協(xié)調(diào)NavGraph與Navigator共同完成導(dǎo)航功能。
二、導(dǎo)航原理
在使用導(dǎo)航組件進(jìn)行導(dǎo)航時(shí),簡單用法如下:
NavHostFragment navHost =(NavHostFragment) getActivity().getSupportFragmentManager().findFragmentById(R.id.nav_host_fragment_jounal_item_detail); navHost.getNavController().navigate(R.id.action_DetailFragment_to_ImageShowFragment, bundle);
其中要求layout文件中id對應(yīng)的fragment必須為Google提供的NavHostFragment,因?yàn)槟壳爸挥性揊ragment對象實(shí)現(xiàn)了NavHost接口,才能初始化NavController。
執(zhí)行跳轉(zhuǎn)時(shí),很顯然調(diào)用的是NavHostController的navigate方法,而上面展示到NavHostController并沒有對父類NavController中的方法進(jìn)行重寫,只是一個(gè)空殼子類,所以navigate方法的實(shí)現(xiàn)需要看NavController。
1、NavController::navigate
經(jīng)過回調(diào),最終會調(diào)用到含四個(gè)參數(shù)的navigate方法:
public void navigate(@IdRes int resId, @Nullable Bundle args, @Nullable NavOptions navOptions,
@Nullable Navigator.Extras navigatorExtras) {
// --------------------注釋 1-----------------------
NavDestination currentNode = mBackStack.isEmpty()
? mGraph
: mBackStack.getLast().getDestination();
if (currentNode == null) {
throw new IllegalStateException("no current navigation node");
}
@IdRes int destId = resId;
final NavAction navAction = currentNode.getAction(resId);
// ----------------------注釋 2-----------------------------
Bundle combinedArgs = null;
if (navAction != null) {
if (navOptions == null) {
// ------------------a---------------------
navOptions = navAction.getNavOptions();
}
destId = navAction.getDestinationId();
Bundle navActionArgs = navAction.getDefaultArguments();
if (navActionArgs != null) {
combinedArgs = new Bundle();
// -------------------b---------------------
combinedArgs.putAll(navActionArgs);
}
}
if (args != null) {
if (combinedArgs == null) {
combinedArgs = new Bundle();
}
// -----------------c------------------------
combinedArgs.putAll(args);
}
if (destId == 0 && navOptions != null && navOptions.getPopUpTo() != -1) {
popBackStack(navOptions.getPopUpTo(), navOptions.isPopUpToInclusive());
return;
}
...
// ---------------------注釋 3---------------------------
NavDestination node = findDestination(destId);
...
navigate(node, combinedArgs, navOptions, navigatorExtras);
}
該方法的流程被分為三個(gè)大的部分:
- 注釋1處:該部分的意義是首先獲取當(dāng)前位于的destination fragment對應(yīng)的NavDestination對象,該對象的獲取有兩種情況:首先是剛啟動時(shí),當(dāng)前的Fragment還是NavHostFragment,此時(shí)mBackStack還是空的,因此此時(shí)獲取到的是mGraph對象;之后,當(dāng)navigation資源文件中的startDestination對應(yīng)的NavDestination被添加到mBackStack后,獲取當(dāng)前destination fragment時(shí)就是從mBackStack中了。得到了當(dāng)前destination fragment之后,根據(jù)actionid獲取到對應(yīng)的NavAction對象(前面說過,NavDestination中將資源文件中action標(biāo)簽都封裝成了NavAction對象保存了起來);
- 注釋2處:這部分的含義為配置參數(shù),需要配置的參數(shù)由NavOptions與Argument,而二者配置的思想是一致的。通過a處的代碼可以發(fā)現(xiàn),最終的NavOptions中navigate方法的參數(shù)navOptions的優(yōu)先級是高于NavAction中的Options的(即xml文件中定義的靜態(tài)Options);而通過b和c處的代碼可以知道,最終的Argument是結(jié)合了navigate方法的參數(shù)args和NavAction中的defaultArgument的,但是由于args中的值是后面添加的,所以如果存在重復(fù)的話,args會覆蓋掉defaultArgument,所以依然是動態(tài)添加的內(nèi)容優(yōu)先級高;
- 注釋3處:這部分首先來獲取本次導(dǎo)航的目的destination fragment對應(yīng)的NavDestination對象,然后執(zhí)行另一個(gè)navigate方法。
2、NavController::navigate
該方法雖然同名,但是第一個(gè)參數(shù)變?yōu)榱四康膁estination fragment對應(yīng)的NavDestination:
private void navigate(@NonNull NavDestination node, @Nullable Bundle args,
@Nullable NavOptions navOptions, @Nullable Navigator.Extras navigatorExtras) {
....
Bundle finalArgs = node.addInDefaultArgs(args);
NavDestination newDest = navigator.navigate(node, finalArgs,
navOptions, navigatorExtras);
if (newDest != null) {
...
// And finally, add the new destination with its default args
NavBackStackEntry newBackStackEntry = new NavBackStackEntry(mContext, newDest,
newDest.addInDefaultArgs(finalArgs), mLifecycleOwner, mViewModel);
mBackStack.add(newBackStackEntry);
} else if (navOptions != null && navOptions.shouldLaunchSingleTop()) {
launchSingleTop = true;
NavBackStackEntry singleTopBackStackEntry = mBackStack.peekLast();
if (singleTopBackStackEntry != null) {
singleTopBackStackEntry.replaceArguments(args);
}
}
updateOnBackPressedCallbackEnabled();
if (popped || newDest != null || launchSingleTop) {
dispatchOnDestinationChanged();
}
}
該方法就顯得很簡單了:
首先是調(diào)用Navigator對象的navigate方法執(zhí)行導(dǎo)航動作;
-
根據(jù)上一步的結(jié)果進(jìn)行處理:
- 成功得到了一個(gè)NavDestination對象,說明該對象不是singleTop啟動或者是singleTop啟動但為首次啟動,這都說明了該對象應(yīng)該添加到mBackStack中,第一個(gè)if語句就是這個(gè)意思;
- 而else if則說明是singleTop啟動,并且mBackStack中已經(jīng)有了該NavDestination并且位于top位置,說明是singleTop啟動,那么執(zhí)行的動作是使用replaceArgument來替換掉原來的數(shù)據(jù)。
最后,代碼的關(guān)鍵就是看Navigator.navigate了,其內(nèi)部是如何執(zhí)行的?以及,何時(shí)返回null?
3、Navigator::navigate
根據(jù)上面的分析可知,我們本場景下的Navigator的具體類為:FragmentNavigator,為此,我們看看他的navigate方法實(shí)現(xiàn):
public NavDestination navigate(@NonNull Destination destination, @Nullable Bundle args,
@Nullable NavOptions navOptions, @Nullable Navigator.Extras navigatorExtras) {
....
// ------------------注釋 1---------------------
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();
// ----------------------注釋 2-----------------------
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);
}
// -------------------注釋 3-----------------------
ft.replace(mContainerId, frag);
ft.setPrimaryNavigationFragment(frag);
// ----------------------注釋 4-----------------------
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;
// --------------------------a----------------------------
if (initialNavigation) {
isAdded = true;
} else if (isSingleTopReplacement) {
// --------------------------b----------------------------
// 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 {
// --------------------------c----------------------------
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;
}
}
對于該方法,明顯也可以分為如下幾個(gè)部分:
- 步驟1:該部分很明確,就是通過類名利用反射來創(chuàng)建一個(gè)Fragment對象,然后為Fragment設(shè)置Argument,同時(shí)開啟一個(gè)Fragment事務(wù);
- 步驟2:這部分的含義是為Fragment配置動畫等信息;
- 步驟3:利用Fragment事務(wù)執(zhí)行replace方法,將目標(biāo)Fragment替換到頁面上;
- 步驟4:這一步又是專門對singleTop啟動的處理,首先isSingleTopReplacement變量的含義為:當(dāng)前導(dǎo)航的啟動模式為singleTop,并且目destination fragment就在mBackStack的top位置,所以此時(shí)調(diào)用了FragmentManager的popBackStack來撤銷上一次的replace事務(wù)操作(根據(jù)意義可知上一次replace操作證實(shí)將目標(biāo)destination fragment加入到了Fragment棧中,所以這里的意義是將其從棧中剔除,那么步驟3的replace就會再次將其加入到回退棧中,實(shí)現(xiàn)了singleTop),雖說FragmentManager管理的Fragment回退棧中的top位置的Fragment已經(jīng)重新replace了,但是在Navigator的mBackStack中對應(yīng)的NavDestination確沒有必要剔除再加入新的,所以這一步之后isAdd變量為false,這也導(dǎo)致了在NavController調(diào)用Navigator.navigate返回的最終是null,從而引發(fā)NavController在管理自己的mBackStack的行為的不同。而a和c處的代碼就是表明需要在Navigator的mBackStack中添加。
三、總結(jié)
總的來說,Navigation組件實(shí)現(xiàn)Fragment之間的跳轉(zhuǎn)本質(zhì)還是通過FragmentManager + FragmentTransaction的組合方式來實(shí)現(xiàn)的。
優(yōu)點(diǎn)
使用導(dǎo)航組件實(shí)現(xiàn)Fragment之間的跳轉(zhuǎn)的優(yōu)點(diǎn)在于:
- 邏輯清晰且代碼整潔:頁面之間的跳轉(zhuǎn)關(guān)系定義在navigation資源文件中,使得跳轉(zhuǎn)關(guān)系一目了然,避免了寫在Activity之中混亂不堪,分布雜亂的問題;同時(shí),封裝了的FragmentManager與FragmentTransaction也進(jìn)一步解放了開發(fā)者。
- 功能豐富:最明顯的就是提供了Fragment跳轉(zhuǎn)的singleTop啟動支持,通過上面的分析可以知道Navigation實(shí)現(xiàn)Fragment的singleTop功能有兩點(diǎn):首先,利用Navigator的mBackStack數(shù)據(jù)結(jié)構(gòu)來判斷是否存在目標(biāo)Fragment以及是否已經(jīng)位于top位置,這是FragmentManager的Fragment回退棧沒法做到的事情;其次,實(shí)現(xiàn)singleTop功能利用FragmentManager撤銷上一次FragmentTransaction和添加新一次FragmentTransaction的replace來實(shí)現(xiàn)(同樣可以發(fā)現(xiàn),F(xiàn)ragment的singleTop只有當(dāng)目標(biāo)Fragment正好位于top位置是才會生效,如果位于回退棧的非top位置,那么便不會有作用)。