JetPack框架出来的时间也比较久了,我记得我是在19年开始接触相关的框架,当时觉得ViewModel、LiveData、Paging2、协程之类的很厉害,很牛逼,很好用。可惜,学是学了,只是当时的环境用不上。不过有意思的是我也学了一下Navigation,当时给的评价是“鸡肋”,学之无用,弃之可惜,而且当时的Navigation总是有这样那样的问题,所以后面就放弃使用了。不过最近在管理Fragment的时候总是听别人说Navigation有多厉害多厉害的,而且正好项目也要对部分代码进行重构,所以重新再学了一下Navigation。不动笔墨不读书,因此在这里记录一下我对Navigation的理解。
Navigation的使用就没必要放上来了,这种烂大街的东西,随手可以搜得到,下面直接讲讲原理吧。
Navigation中值得关注的有两个问题,第一个是:Navigation是如何进行初始化的,即在配置好相关的nav_graph、FragmentContainerView等等之后,它是怎么工作的;第二个是:我们调用的navigate()方法,它是如何实现它的功能的。
先分析第一个问题,以下面的代码为例:
<androidx.fragment.app.FragmentContainerView
android:id="@+id/main_fragment_container"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintTop_toTopOf="parent"
app:defaultNavHost="true"
app:navGraph="@navigation/nav_graph" />
这段代码在FragmentContainerView放置了一个NavHostFragment,NavHostFragment的创建方式有两种,第一种是通过代码方式:NavHostFragment.create() ,第二种方法是通过xml的方式,就比如上面的代码。先分析第一种方式:
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;
}
代码很简单,可以看到该方法只做了一件事,就是把导航图的id以及默认导航目的地的参数储存到一个Bundle中,并且放置到一个新创建的NavHostFragment里面。
再看看第二种方式,熟悉Fragment生命周期的都清楚,通过xml里面创建的Fragment,必然会走到onInflate方法中,其代码如下:
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;
}
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();
}
这里的代码也很简单,可以看到该方法主要做的也只有一件事:解析出xml里面的defaultNavHost属性和navGraph属性,并且将其记录起来。
根据Fragment的生命周期,其下一步会调到onCreate()方法中,其代码如下:
public void onCreate(@Nullable Bundle savedInstanceState) {
......
//①创建NavHostController对象
mNavController = new NavHostController(context);
......
//②创建对应的Navigator并储存到NavigatorProvider中
onCreateNavController(mNavController);
//③恢复数据
Bundle navState = null;
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) {
// Navigation controller state overrides arguments
mNavController.restoreState(navState);
}
//④将导航图id设置到NavController中
if (mGraphId != 0) {
// Set from onInflate()
mNavController.setGraph(mGraphId);
} else {
// See