public void onAttach(@NonNull Context context) {
super.onAttach(context);
// TODO This feature should probably be a first-class feature of the Fragment system,
// but it can stay here until we can add the necessary attr resources to
// the fragment lib.
if (mDefaultNavHost) {
getParentFragmentManager().beginTransaction()
.setPrimaryNavigationFragment(this)
.commit();
}
}
}
很简单的几行代码,却藏着不少信息:
- NavHostFragment是导航宿主Fragment,要实现路由跳转的Fragment都是它的child Fragment。这里就涉及到childFragmentManager,parentFragmentManager等知识了。
- mDefaultNavHost是要设置成true,NavHostFragment才能成为主导航Fragment。
- 从注释中,可以看出,setPrimaryNavigationFragment()虽然很不起眼,它即将升级为Fragment的一类公民了(first-class)。
2. FragmentManager 回退栈
栈是一种很简单的数据结构。它的特点是“后进先出”。在FragmentManager中回退栈定义如下:
public abstract class FragmentManager{
ArrayList mBackStack;
}
在FragmentTransaction中有addToBackStack(String name)方法,可以将某个方法加入回退栈中。
class FragmentActvitiy1: AppCompatActivity() {
fun addFragmentNotAddToBackStack() {
supportFragmentManager.commit {
setReorderingAllowed(true)
add(R.id.top_fragment_container_view)
}
supportFragmentManager.commit {
setReorderingAllowed(true)
add(R.id.top_fragment_container_view)
}
}
}
class FragmentActvitiy2: AppCompatActivity() {
fun addFragmentNotAddToBackStack() {
supportFragmentManager.commit {
setReorderingAllowed(true)
add(R.id.top_fragment_container_view)
addToBackStack(null)
}
supportFragmentManager.commit {
setReorderingAllowed(true)
add(R.id.top_fragment_container_view)
addToBackStack(null)
}
}
}
上述两段代码,唯一的区别就是FragmentActivity1没有调用addToBackStack()方法,而FragmentActivity2调用了。
FragmentActivity1 按返回键效果如下:
FragmentActivity2 按返回键效果如下:
3. FragmentManager处理返回原理
「handleOnBackPressed()」 处理逻辑如下:
当回退栈中有记录时,调用popBackStackImmediate(),该方法调用popBackStackImmediate(String name, int id, int flags)
**「代码1处」**就是处理当前FragmentManager有主导航Fragment时的返回场景。如果主导航Fragment不为空时,交由childManager处理返回。如果childMananger拦截了返回键处理则返回,否则继续让当前FragmentManager处理。具体场景,后文详解。
「代码2处」 popBackStackState(ArrayListrecords, ArrayListisRecordPop, String name, int id, int flags)的作用是将回退栈中的FragmentTransaction(BackStackRecord)放到records集合中,以备后用。
出栈分为4种情况(简单起见,id不考虑了,否则根据排列组合有8中情况)
| case | name | POP_BACK_STACK_INCLUSIVE |
| — | — | — |
| case1 | null | 0 |
| case2 | not null | 0 |
| case3 | not null | 1 |
| case4 | null | 1 |
POP_BACK_STACK_INCLUSIVE = 1时表示,根据name找到返回栈里面的BackStackRecord,一起出栈。
假设有回退栈如下。我们来走下四种case。
「case->popBackStack(null,0)」
「case2->popBackStack(“s2”,0)」
「case3->popBackStack(“s2”,1)」
「case4->popBackStack(null,1)」
「代码3处」 removeRedundantOperationsAndExecute(mTmpRecords, mTmpIsPop)真正执行出栈操作。最终执行到executeOpsTogether方法。
4. setPrimaryNavigationFragment
经过前面那么多的铺垫,终于来到讲解本文主角setPrimaryNavigationFragment了(下文简称:“主导航Fragment”)。前文例子,我们都是基于FragmentActivity平铺Fragment场景讲解的。如果Fragment嵌套Fragment,该如何处理返回栈呢。“主导航Fragment”就是为了解决嵌套Fragment而设计的。前文讲到“Fragment必须依附在NavHostFragment上”,其实就是嵌套Fragment了。
从上图我们看到有三种角色。HostActivity,HostFragment,Child Fragment(s)。
HostFragment就是通过成为“主导航Fragment”,接管Activity的处理返回操作,并且将返回操作交由ChildFragment(s)去处理。作用可谓“承上启下”。
文字描述始终有点晦涩难懂。上场景图解。
分两种场景。
| Case | HostFragment |
| — | — |
| Case1 | 不成为主导航Fragment |
| Case2 | 成为主导航Fragment |
「Case1 伪代码如下」
「Case2 伪代码如下」
「返回场景如下」
「Case1」
最后
针对于上面的问题,我总结出了互联网公司Android程序员面试涉及到的绝大部分面试题及答案,并整理做成了文档,以及系统的进阶学习视频资料。
(包括Java在Android开发中应用、APP框架知识体系、高级UI、全方位性能调优,NDK开发,音视频技术,人工智能技术,跨平台技术等技术资料),希望能帮助到你面试前的复习,且找到一个好的工作,也节省大家在网上搜索资料的时间来学习。
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门,即可获取!
最后
针对于上面的问题,我总结出了互联网公司Android程序员面试涉及到的绝大部分面试题及答案,并整理做成了文档,以及系统的进阶学习视频资料。
(包括Java在Android开发中应用、APP框架知识体系、高级UI、全方位性能调优,NDK开发,音视频技术,人工智能技术,跨平台技术等技术资料),希望能帮助到你面试前的复习,且找到一个好的工作,也节省大家在网上搜索资料的时间来学习。
[外链图片转存中…(img-usIUNd48-1715395564465)]
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门,即可获取!