跟着Jetpack系列结构的商场认可度越来越高,运用Navigation结构用单个Activity+多个Fragment开发一个app又一次成为了可能,可是在运用Navigation的时分,总是会呈现一些问题,比如android平板电脑价格FragmentA翻开了FragmentB,然后再回来的时分,FragmentA重走了生命周期,许多时分这不是咱们想要的成果,为什么会呈现这样的问题又怎样去处理这个问题?appetite今日就从源码层面根究一下。
首要,我创立了一个十分简略的NavGraph,如图所示:
这两个Fragment的功用十分简略,SourceFragment中只要一个按钮,用来点击跳转到TargetFragment,而TargetFargment中没有任何逻辑。
然后在修改容器Activity的资源文件:
<an交流技巧和办法droidx.constraintlayout.widget.ConstraintLayout
...>
<fragandroidstudio装置教程ment
android:iandroid手机d=接口自动化"@+id/container"
android:na源码是什么意思me="androidx.na源码本钱vigation.fragment.android体系NavHostFragmandroid下载ent"
android:layout_width="match_parent"
androidappetite:layo源码ut_height="mandroid体系atch_parent"
app:defaultNavHost="tr交流的三要素是什么ue"
app:navGraph="@navigation/navigation_main" />
</androidx.constraintlayout.widget.ConstraintLayout接口crc过错计数>
这样就把一个简略的Navigation跳转的Demo做好了。
然后咱们看下SourceFragment从初次加载到翻开TargetFragment再回来到Soandroid手机urceFragment这三个阶段生命周期的改动。
第一阶段,源码本钱加载SourceFragment:
onAttach -> onCreateView -&接口卡gt; onViewCreated -> onS接口是什么tart -> onResume
这个阶段没有问题,和运用一般Fragment的时分生命周期改动一同。
第二阶段:翻开TargetFragment:
onPause -&接口英文g接口是什么t; onStop
SourceFragment在不可见的时分源码码头仅仅进入了onStop,并没有走onDestory。到目前为止,接口英文看起来一切正常。
第三阶段:回来到Sour接口crc过错计数ceFragmentapp是什么意思
onCreateView -> onViewCreated -> onStart -> onResume
咱们惊奇的发现,这个阶段的生命周期和咱们料appreciate想的并不一同,从头回来到SourceFragment重走了onAttach外的生命周期,重走生命周期不只意味着要消耗额外的资源对SourceFragment进行从头渲染,也降交流的艺术低了用户领会,那么接下来就进入这篇文章的主题android手机:
一、为什么会重走生命周期?
二、怎样处理?
想要分析问题,首要要了解原理,先简略看一下Navigation结构大致的完毕原理。
在容器Activity的布局文件中,咱们运用一个fragment源码本钱标签,并且为标签闪现的指定了一个交流的三要素是什么android:name特征,里边装备的是一个Fragment的全途径,官方供给的是androidx.navigation.fragment.NavHostFragment,咱们都知道,Activity加载布局的时分会根据装备的全途径经过反射获取到Fragment方针,然后交流技巧和办法a接口ttach到该Activity,android平板电脑价格究竟完毕Fragment的加载。想要了解Navigation结构,从NavHostFragment下手再适合不过。
public class NavHostFragment extends Fragment implements NavHost {
...
}源码是什么意思
public interface NavHost {
@NonNull
NavController getNavController();
}
NavHostFragment便是一个Frag源码本钱ment的子类完毕了一个简略的接口,可以对外供给获取NavController的办法,该办法的回来值便是NavHostFragment的一个特征mNavController。
private NavHo接口的效果stController源码年代 mNavController;
@NonNull
@Override
public final NavController getNavController() {
if (mNavController == null) {
throw new Il接口文档legalStateException("NavController is not avandroid手机ailable before onCreate()");
}
return mNavController;
}
mNavController特征的初始化是在onCreate生命接口类型周期中结接口的效果束的。
@CallSuper
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final Context context = requi源码reContext();
mNavController = new Na源码编辑器vHostController(context);
mNavController.setLifecycleOwner(this);
/appreciate/略...调用一些mNavControlandroidstudio装置教程ler办法
onCreateNavController(mNavController); //这个办法比较重要,下面会提及
//设置导航图ID
if (mGraphId != 0) {
mNavController.setAPPGraph(mGraphId);
} else {
//设置一个空导航
}
}
mGraphId便是在fragment标签中装备的navGraph特征是在onInflate办法中获接口文档取的:
@CallSuper
@Override
public voidandroid下载 onInflate(@NonNull Context context, @NonNull AttributeSet attrs,@Nandroid体系ullable Bundle savedInstanceS接口英文tate交流){
super.onInflate(context, attrs, sappleavedInstanceState);
final TypedArray navHost = context.obtainStyledAttributes(attr交流的重要性的名言s,aandroid体系ndroidx.navigation.R.styleable.NavHost);
//经过自定义特征获取na交流技巧vigation导航图
final int graphId = navHost.getResourceId(androidx.naAPPvigation.R.sty源码码头leable.NavHoandroidstudio装置教程st_navGrapandroid下载h, 0);
if (graphId !=接口的效果 0) {
mGraphId源码码头 = graphId;
}
..application.
}
其实NavHostFragment才是交流100移动营业厅容器Activity加载接口文档的第一个Fragment,在mNavController.setGraph办法调用之后,会经过一些列的办法调用,究竟替换为在navigation资源文件中装备的startDestination特征中的Fraandroid/yunosgment。
以approach上便是NavHostFraandroid平板电脑价格gment类的主题功用,其实交流技巧和办法十分简略已读。NavController虽然看起来比较多,但它的功用仍是比明晰的,便是对外供给设置NavGraph、跳转办法navigate、回来工作操控以及源码网站监听Destination的改动。但真实实施视图跳转的逻辑并不是NavController实施的,而是经过mNavigatorProvider分发到了不同的Navigator中,然后实施真实的跳转逻appointment辑:
//NavContr交流技巧和办法oller中navigate究竟的重载
private void navigate(@NonNull NavDestination node, @Nullable Bun接口英文dle args,
@Nullable NavOptions navOptions, @Nullable Navigator.Extras navigatorExtras) {
//...
//根据跳转类型的不同,分发到不同的navigator中实施跳转逻辑
Navigat交流100移动营业厅or<NavDestination> navigator = mNavigatandroid是什么手机牌子orProvid源码编辑器编程猫er.getNavigator(node.getNavigatorName());
Bundle finalArgs = node.addInDefaultArgs(args);
//调用navigator里的navigandroid/yunosate办法
NavDestination newDest = navigator.navigate(node, finalArgs,navOptions, naviga接口文档torExtras);
//...更新mBackStack栈
}
抽象类Navigatappleor一共有5个子类:
@交流技巧Navigator.Name("activity")
public class交流的重要性心得体会 ActivityNavigator extends Navigator<A接口ctivityNavigator.Destinationandroid平板电脑价格> {
//... 操控Activity的跳转
}
@Navigator.Name("dialog")
public final class DialogFragmentNavigator extends Navigator<DialogFragmentNavigator.Destination> {
//...操控DialogFragment的appointment跳转
}
@Navig交流能力ator.Name交流能力("fragment")
public class FragmentNavigator extends Navigatandroid平板电脑价格or<FragmentNavigator.Destination> {
//...操控Fragment的跳转
}
@Navigator.N交流的重要性ame("navigation")
public class NavGraphNavigator extends Navigato源码编辑器编程猫r<NavGraph> {
//...操控改动NavGraph
}
@Navigator.Name("NoOp")
public class NoOpNavigator extends Navigator<NavDestination> {
//...忽略不计...
}
NavigatorProvider类担任处理以上五种Navig接口的效果ator,处交流的三要素是什么理的办法十分简略appetite,便是用一个名为mNavigators的HashMap<String,Navigator&接口gt;把源码网站经过addNavigator办法增加的Navigator缓存起来,其间key便是@Navigator.Name(“xxx”)注解里边给定的xx源码网站x,在getNavigator时从缓存中取出来给调用方。
在咱们运用NavHostFragment的时分,结构会为我添android是什么手机牌子加前四种Naviapplegator,分别是在上文提到过的NavHostFragment的oapproachnCrapp是什么意思eate办法中的调用的onCreateNavController(mNa接口文档vController)办法:
@CallSuper
protected void onCreateNavController(@NonNull NavContro交流技巧ller navController) {
//增加DialogFragmentNavigator
navControll交流的重要性er.ge源码网站tNavigatorProvider().addNavi接口crc过错计数gator(
new DialogFragmentNavigator(requireContext()android什么意思, getChildFragmentManager()));
//增加FragmentNavigator
navController.getNavigatorProvider().addNavigato源码编辑器编程猫r(createFragmentNavigator());
}
和NavController的结构办法里:接口crc过错计数
public NavCon接口英文troller(@NonNull Context context) {
mContext = context;
while (context instanceof ContextWraAPPpper) {
if (context iandroid的drawable类nstanceof Activity)接口和抽象类的区别 {
mActivity = (Activity)交流的重要性的名言 context;
break;
}
context = ((ContextWrapper) context).getBaseContext();
}
//这儿
mNavigatorProvider.addNavi交流的三要素是什么gator(new NavGraphNavigator(mNavigatorProvider));
mNavigandroid的drawable类atorProvider.addNavigator(new ActivityNavigator(mContext));
}
以上便是Navigation结构的大体逻辑,总结一下便是:
NavHostFragment作为容器Activity第一个加载接口是什么的Fragmen交流的重要性心得体会t,交流的艺术保护了一个NavContro源码网站llerappetite的实例,并在Navigato交流技巧rProvider中增加了4种Navigator用来实施不同的视图跳转逻辑,并在onCreate办法的最终,经过NavController.android体系setGraph办法设置了在fragment标签中装备的androidstudio装置教程nvGraph的id,把NavHostFragment重定向到了navigation.x源码之家ml里装备的startDestination。NavController的跳转逻源码之家辑也经过跳转类型的不过,经过内部保护的NavigatorProvider分发到了不同的Navigator进行跳转。
那现在状况就很明了了,咱们在SourceFragment中调用的跳转办法:
nextButton.setOnClickListener {
findNavController().navigate(R.id.action_souapplicationrceFragment_to_targetFragment)
}
究竟会经过一系列的处理分发到FragmentNavigator交流的重要性的navigate办法中去:APP
@Nullable
@Override
public NavDestination nav交流的三要素是什么igate(@NonNull Destinatio源码本钱n des接口tination, @Nullable Buandroid平板电脑价格ndle args,@Nullable NavOptions navOptions, @Nullabl源码买卖网站源码e Navigator.Extras navigatorExtras) {
//略...
finaandroid下载装置l FragmentTransaction ft = mFragmentManager.beginTrAPPansaction();
ft.replace(mContainerandroid体系Id, frag);
ft.setPrimaandroid的drawable类ryNavigationappearanceFragment(frag);
//略...
ft.setReordandroidstudio装置教程eringAllowed(true);
ft.commit();
//略...
}
看到这儿总算茅塞顿开,本来Navigation结构仍是根据Fragm源码码头entTransaction的封装!因为在翻开新的Fragment的时分,老Fragm接口crc过错计数ent直接被replac接口测试e掉了,那Fragment重走生命周期便是一源码编辑器编程猫个陈词滥调的问题了。
已然知道了原因,那源码年代就开端着手处理,同本来的Fragment重绘处理方案一同,只需求把replace办法替换为hidden和add办法即可,不过有个当地需求特别注意一下,因为容器Activity第一个加载的是NavHosandroid手机tFragment,而这个Fragment是需求被replace掉的,其他的Fragment则不再需求。
创立copy一份FragmentNavigator类偏重命名为NoReplaceFragmentNavigator,只对navigate办法进行部分修改:
@Nullable
@Override
public NavDestination navigate(@NonNull Destination destination, @Nullab接口自动化le Bundle args,@Nullable NavOptions navOptions, @Nullable Navigator.Extras navigatorExtras) {
//ft.replace(mContainerId, frag); 修改前
if (mBackStack.size > 0) {
ft交流作文.hide(mFragmentManager.getFragments.get(mBackStack.size - 1)).a接口的效果dd(mContainerId,frag);
}elsAndroide{
ft.replace(mContainerId, frag);
}
}
然后再copy一份NavHostFragment类偏重命名为交流的重要性的名言NoReplace交流技巧NavHostFragment,对createFragmentNavigator进行修改,将本来的FragmentNavigator替换为NoReplaceFragmentNavigator即可。createFragmentNaappearvigator被标明为弃用,可是文档里并没交流的重要性的名言有给出代替的办法,乖僻乖僻,有知道的大佬帮接口英文助解下appetite惑~
最终,在容器Activity接口的效果布局文件的fragment标签中android:name特征修改为NoReplaceFragmentNavigator的全途径即可。
没有处理的问题:
经过以上的修改,确源码编辑器实可以避免从头回来到SourceFragment时重绘的问题,可是却带来了一个新的问题,便是运用Fragment接口类型Transactio接口是什么n的hiddenapp是什么意思办法并不会让当时Fragment的生命周期发生改动,也便是在实施前文提到第二阶段和第三阶段的时分,SourceFragment的生命接口的效果周期是没有发生任何改动的。FragmentTransaction一直存在这么一个问题,不过却是有一个折中的处理方案,重写Fragment的onHiddenChanged办法:
override fun onHiddenChanged(hidden: Boolean) {
super.onHiddenChanged(hidden)
if(hidden){
//当时Fragment不可见
}else{
//当时Fragment可见
}
}
可以把那些需求在生命周期里处理的逻辑放到这个办法里边,但运用Lifecycle监听Fragment生命周期改动就无能为力了…
假如有更好的方案,欢迎交流同享~
假如该文章可以帮到你,欢迎点赞议论和重视,一同交流评论~