Jetpack之Navigation(2)
Jetpack之Navigation(1)
2.原理
初始化过程 NavHostFragment生命周期方法
1.create—NavHostFragment的创建
在NavHostFragment.create方法
- 初始化Bundle,并且将graphResId,startDestinationArgs存储在Bundle中。
- new NavHostFragment()返回NavHostFragment实例。
//NavHostFragment.java
@NonNull
public static NavHostFragment create(@NavigationRes int graphResId,
@Nullable Bundle startDestinationArgs) {
Bundle b = null;
if (graphResId != 0) {
b = new Bundle();
b.putInt(KEY_GRAPH_ID, graphResId);
}
if (startDestinationArgs != null) {
if (b == null) {
b = new Bundle();
}
b.putBundle(KEY_START_DESTINATION_ARGS, startDestinationArgs);
}
final NavHostFragment result = new NavHostFragment();
if (b != null) {
result.setArguments(b);
}
return result;
}
复制代码
2.onInflate—XML文件的解析
主要是解析布局文件的两个属性:defaultNavHost和navGraph,并且初始化全局变量。
NavHostFragment.onInflate方法 当Fragment以XML的方式静态加载时,最先会调用onInflate的方法(调用时机:Fragment所关联的Activity在执行setContentView时)。
//NavHostFragment.java
@CallSuper
@Override
public void onInflate(@NonNull Context context, @NonNull AttributeSet attrs,
@Nullable Bundle savedInstanceState) {
super.onInflate(context, attrs, savedInstanceState);
final TypedArray navHost = context.obtainStyledAttributes(attrs,
androidx.navigation.R.styleable.NavHost);
final int graphId = navHost.getResourceId(
androidx.navigation.R.styleable.NavHost_navGraph, 0);
if (graphId != 0) {
mGraphId = graphId; //navigation的图布局
}
navHost.recycle();
final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.NavHostFragment);
final boolean defaultHost = a.getBoolean(R.styleable.NavHostFragment_defaultNavHost, false);
if (defaultHost) {
mDefaultNavHost = true; //是否监听物理返回键
}
a.recycle();
}
复制代码
3.onCreateNavController—创建Navigator
在实现导航的时候,我们需要根据navigation配置文件生成NavGraph类,然后在根据每个不同的actionid,找到对应的NavDestination就可以实现页面导航跳转了。
创建Navigator
Navigator类的作用是:能够实例化对应的NavDestination,并且能够实现导航功能,拥有自己的回退栈。
@CallSuper
protected void onCreateNavController(@NonNull NavController navController) {
navController.getNavigatorProvider().addNavigator(
new DialogFragmentNavigator(requireContext(), getChildFragmentManager()));
//创建Navigator并绑定到NavigatorProvider中。
//mNavigatorProvider是NavController中的全局变量,内部通过HashMap键值对的形式保存Navigator类。
navController.getNavigatorProvider().addNavigator(createFragmentNavigator());
}
复制代码
- 其中mNavigatorProvider是NavController中的全局变量,内部通过HashMap键值对的形式保存Navigator类
- createFragmentNavigator方法,构建了FragmentNavigator对象,其中抽象类Navigator还有个重要的实现类ActivityNavigator和NavGraphNavigator。这个两个类的对象在NavController的构造方法中被添加。
//NavController.java
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));
}
复制代码
4.onCreate—导航初始化
无论是XML实现还是代码实现,都会执行Fragment的onCreate方法。NavController在这里被创建,并且NavHostFragment中有一个NavController对象。
@CallSuper
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
final Context context = requireContext();
//1.初始化NavController,NavController为导航的控制类,核心类
mNavController = new NavHostController(context);
mNavController.setLifecycleOwner(this);
mNavController.setOnBackPressedDispatcher(requireActivity().getOnBackPressedDispatcher());
mNavController.enableOnBackPressed(
mIsPrimaryBeforeOnCreate != null && mIsPrimaryBeforeOnCreate);
mIsPrimaryBeforeOnCreate = null;
mNavController.setViewModelStore(getViewModelStore());
onCreateNavController(mNavController);
Bundle navState = null;
//2.开始恢复状态
if (savedInstanceState != null) {
navState = savedInstanceState.getBundle(KEY_NAV_CONTROLLER_STATE);
if (savedInstanceState.getBoolean(KEY_DEFAULT_NAV_HOST, false)) {
mDefaultNavHost = true;
getParentFragmentManager().beginTransaction()
.setPrimaryNavigationFragment(this)
.commit();
}
mGraphId = savedInstanceState.getInt(KEY_GRAPH_ID);
}
if (navState != null) {
mNavController.restoreState(navState);
}
//3.设置导航图信息
if (mGraphId != 0) {
mNavController.setGraph(mGraphId);
} else {
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);
}
}
super.onCreate(savedInstanceState);
}
复制代码
5.onCreateView
NavHostFragment的视图就只有一个FragmentContainerView 继承 FrameLayout
//NavHostFragment.java
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
FragmentContainerView containerView = new FragmentContainerView(inflater.getContext());
//这行主要用于以代码方式添加fragment
containerView.setId(getContainerId());
return containerView;
}
复制代码
6.onViewCreated
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
if (!(view instanceof ViewGroup)) {
throw new IllegalStateException("created host view " + view + " is not a ViewGroup");
}
//把mNavController记录在view的tag中
Navigation.setViewNavController(view, mNavController);
if (view.getParent() != null) {
mViewParent = (View) view.getParent();
if (mViewParent.getId() == getId()) {
//把mNavController记录在view的tag中
Navigation.setViewNavController(mViewParent, mNavController);
}
}
}
复制代码
获取NavController
1.获取NavController
NavHostFragment.findNavController(fragment)
复制代码
//NavHostFragment.java
@NonNull
public static NavController findNavController(@NonNull Fragment fragment) {
Fragment findFragment = fragment;
while (findFragment != null) {
if (findFragment instanceof NavHostFragment) {
return ((NavHostFragment) findFragment).getNavController();
}
Fragment primaryNavFragment = findFragment.getParentFragmentManager()
.getPrimaryNavigationFragment();
if (primaryNavFragment instanceof NavHostFragment) {
return ((NavHostFragment) primaryNavFragment).getNavController();
}
findFragment = findFragment.getParentFragment();
}
View view = fragment.getView();
if (view != null) {
return Navigation.findNavController(view);
}
Dialog dialog = fragment instanceof DialogFragment
? ((DialogFragment) fragment).getDialog()
: null;
if (dialog != null && dialog.getWindow() != null) {
return Navigation.findNavController(dialog.getWindow().getDecorView());
}
throw new IllegalStateException("Fragment " + fragment
+ " does not have a NavController set");
}
复制代码
2.Navigation中findNavController
3.findViewNavController
通过view.tag查找NavController。内部调用了getViewNavController方法。
4.getViewNavController
getViewNavController方法 通过获取view的Tag,获取NavController对象,这里的tag ID和setViewNavController都是nav_controller_view_tag。
//Navigation.java
@NonNull
public static NavController findNavController(@NonNull View view) {
//3.
NavController navController = findViewNavController(view);
if (navController == null) {
throw new IllegalStateException("View " + view + " does not have a NavController set");
}
return navController;
}
@Nullable
private static NavController findViewNavController(@NonNull View view) {
while (view != null) {
NavController controller = getViewNavController(view);
if (controller != null) {
return controller;
}
ViewParent parent = view.getParent();
view = parent instanceof View ? (View) parent : null;
}
return null;
}
@Nullable
private static NavController getViewNavController(@NonNull View view) {
//4.这里的tag ID和setViewNavController都是nav_controller_view_tag。
Object tag = view.getTag(R.id.nav_controller_view_tag);
NavController controller = null;
if (tag instanceof WeakReference) {
controller = ((WeakReference) tag).get();
} else if (tag instanceof NavController) {
controller = (NavController) tag;
}
return controller;
}
复制代码
导航navigate
navigate
在构建和获取到NavController对象以及NavGraph之后。下面是使用它来实现真正的导航了。下面从navigate开始分析。在navigate方法内部会查询到NavDestination,然后根据不同的Navigator实现页面导航。
public void navigate(@IdRes int resId, @Nullable Bundle args, @Nullable NavOptions navOptions,
@Nullable Navigator.Extras navigatorExtras) {
NavDestination currentNode = mBackStack.isEmpty()
? mGraph
: mBackStack.getLast().getDestination();
if (currentNode == null) {
throw new IllegalStateException("no current navigation node");
}
@IdRes int destId = resId;
//2.根据id,获取对应的NavAction
final NavAction navAction = currentNode.getAction(resId);
Bundle combinedArgs = null;
if (navAction != null) {
if (navOptions == null) {
navOptions = navAction.getNavOptions();
}
//3.通过NavAction获取目的地id
destId = navAction.getDestinationId();
Bundle navActionArgs = navAction.getDefaultArguments();
if (navActionArgs != null) {
combinedArgs = new Bundle();
combinedArgs.putAll(navActionArgs);
}
}
if (args != null) {
if (combinedArgs == null) {
combinedArgs = new Bundle();
}
combinedArgs.putAll(args);
}
if (destId == 0 && navOptions != null && navOptions.getPopUpTo() != -1) {
popBackStack(navOptions.getPopUpTo(), navOptions.isPopUpToInclusive());
return;
}
if (destId == 0) {
throw new IllegalArgumentException("Destination id == 0 can only be used"
+ " in conjunction with a valid navOptions.popUpTo");
}
//4.利用目的地ID属性,通过findDestination方法,找到准备导航的目的地
NavDestination node = findDestination(destId);
if (node == null) {
final String dest = NavDestination.getDisplayName(mContext, destId);
if (navAction != null) {
throw new IllegalArgumentException("Navigation destination " + dest
+ " referenced from action "
+ NavDestination.getDisplayName(mContext, resId)
+ " cannot be found from the current destination " + currentNode);
} else {
throw new IllegalArgumentException("Navigation action/destination " + dest
+ " cannot be found from the current destination " + currentNode);
}
}
//5.开始导航
navigate(node, combinedArgs, navOptions, navigatorExtras);
}
private void navigate(@NonNull NavDestination node, @Nullable Bundle args,
@Nullable NavOptions navOptions, @Nullable Navigator.Extras navigatorExtras) {
boolean popped = false;
boolean launchSingleTop = false;
if (navOptions != null) {
if (navOptions.getPopUpTo() != -1) {
popped = popBackStackInternal(navOptions.getPopUpTo(),
navOptions.isPopUpToInclusive());
}
}
Navigator navigator = mNavigatorProvider.getNavigator(
node.getNavigatorName());
Bundle finalArgs = node.addInDefaultArgs(args);
NavDestination newDest = navigator.navigate(node, finalArgs,
navOptions, navigatorExtras);
...
}
复制代码
findDestination
如果回退栈为null返回NavGraph,不为null返回回退栈中的最后一项。
NavDestination findDestination(@IdRes int destinationId) {
if (mGraph == null) {
return null;
}
if (mGraph.getId() == destinationId) {
return mGraph;
}
//1.如果回退栈为null返回NavGraph,不为null返回回退栈中的最后一项。
NavDestination currentNode = mBackStack.isEmpty()
? mGraph
: mBackStack.getLast().getDestination();
NavGraph currentGraph = currentNode instanceof NavGraph
? (NavGraph) currentNode
: currentNode.getParent();
return currentGraph.findNode(destinationId);
}
复制代码
FragmentNavigator的实现
通过以上的分析,又来到了Navigator 的子类FragmentNavigator类。下面来看看FragmentNavigator.navigate的方法。
(1)调用instantiateFragment,通过反射机制构建Fragment实例
(2)处理进出场等动画逻辑
(3)最终调用FragmentManager来处理导航逻辑。
@Nullable
@Override
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;
}
//通过反射机制构建Fragment实例
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);
}
//FragmentManager来处理导航逻辑
ft.replace(mContainerId, frag);
ft.setPrimaryNavigationFragment(frag);
final @IdRes int destId = destination.getId();
final boolean initialNavigation = mBackStack.isEmpty();
final boolean isSingleTopReplacement = navOptions != null && !initialNavigation
&& navOptions.shouldLaunchSingleTop()
&& mBackStack.peekLast() == destId;
boolean isAdded;
if (initialNavigation) {
isAdded = true;
} else if (isSingleTopReplacement) {
if (mBackStack.size() > 1) {
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;
}
if (navigatorExtras instanceof Extras) {
Extras extras = (Extras) navigatorExtras;
for (Map.EntrysharedElement : extras.getSharedElements().entrySet()) {
ft.addSharedElement(sharedElement.getKey(), sharedElement.getValue());
}
}
ft.setReorderingAllowed(true);
ft.commit();
if (isAdded) {
mBackStack.add(destId);
return destination;
} else {
return null;
}
}
复制代码,>
ActivityNavigator
ActivityNavigator最终也是调用了startActivity方法,请自己阅读源码。
@Nullable
@Override
public NavDestination navigate(@NonNull Destination destination, @Nullable Bundle args,
@Nullable NavOptions navOptions, @Nullable Navigator.Extras navigatorExtras) {
....
if (navigatorExtras instanceof Extras) {
Extras extras = (Extras) navigatorExtras;
ActivityOptionsCompat activityOptions = extras.getActivityOptions();
if (activityOptions != null) {
ActivityCompat.startActivity(mContext, intent, activityOptions.toBundle());
} else {
mContext.startActivity(intent);
}
} else {
mContext.startActivity(intent);
}
...
}
复制代码
3.总结
- NavHostFragment 作为导航载体,在Activity的layout文件里被引用(或者在代码中动态),并且持有导航控制类NavController引用。
- NavController 将导航任务委托给Navigator类,Navigator类有两个重要的子类FragmentNavigator和ActivityNavigator子类。NavController类持有NavInflater类引用。
- NavInflater 负责解析Navgation文件,负责构建NavGraph导航图。
- NavDestination 存有各个目的地信息,在FragmentNavigator和ActivityNavigator内部分别对应一个Destination类,该类继承NavDestination。
- 在页面导航时,fragment的操作还是交由FragmentManager在操作,activity交由startActivity执行。
作者:贾里
链接:https://juejin.cn/post/6953624951194664968
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。