全网都刷爆了,不会只有你不知道吧——起初Jetpack Navigation把我逼疯了,可是后来真香!

2. Navigation大家都以为的缺陷


起初我用Navigation的时候,最头疼的是当按下返回键回到上个页面的时候整个页面被重建了,这是开发中不想要的结果,很多时候大家都会去寻求一种方式:将官方的replace方式替换为HideShow。起初也是想到这个方式,然后结合在网上得到的资料自己写了一个方式FragmentNavigatorHideShow

3. 然而这不是缺陷


但是很快啊,我发现这个方式(HideShow)存在严重的逻辑问题。

这里可以看到,有一些场景下,我们有某个页面可以打开和自己相同的页面,只不过是展示的数据不同而已。当我用hideshow的方式展示下个页面的时候,会发现打开的还是上个页面。当按下返回键之后,上个相同的页面不见了,新打开的页面和上个页面尽然是同一个对象,这肯定不符合业务逻辑。于是我又开始研究起replace的方式,当然我在使用这个Navigation的时候就采用了MVVM + ViewModel+LiveData,这时候我想起ViewModel是不受Fragment重建影响的。于是我打印了一下在使用replace方式下页面生命周期的变化。

HomeFragment进入MyFragment生命周期变化:

可以看到,在replace之后HomeFragment并没有执行onDestory而是执行了onDestoryView这也使得页面必须要重建。而onDestoryView不会导致 ViewModel的销毁。也就是说 ViewModel还在,ViewModel中的LiveData所保存的数据也是存在的。当我按下返回键,重新回到HomeFragment页面理所当然的执行了onViewCreated,此时代码中页面对ViewModel中的LiveData所观察数据又重新进行了observe观察,因为LiveData之前保存过数据所以这段代码也理所当然的被执行了。页面上也重新填充了数据。

override fun initLiveData() {

viewModel.liveData.observe(this) {

Log.d(TAG, "data change : $it ")

textView.text = it

}

}

这个时候,你会发现,页面好像没有重建一样。我这才理解了谷歌的用意。它这步棋下的很巧啊。

也里所当然的我抛弃了FragmentNavigatorHideShow,又拥抱回了谷歌爸爸。

说回上面那个问题,当一个页面中可以打开自己的时候,在FragmentNavigator源码中只要是导航到下一个目的地就会重新创建一个新的Fragment,上一个Fragment会被加入回退栈里,所以才可以在Fragment中打开一个新的自己,来展示不同的信息。而hideshow的方式会每次都去查找之前有没有创建过这个页面,如果有,就Show,如果没有就创建。所以才会导致自己打开自己,永远都是同一个Fragment对象。

4. 那么到底该如何正确使用


到底该如何正确使用Navigation,这也是我这段时间使用的一点点经验。

Fragment中的所有动态数据都由ViewModel中的LiveData保存。我们只监听LiveData的数据变化,这也符合MVVM 的架构麻,当然还有一个Model我没说Repository,这个我就不解释了。

Fragment之间传递的数据都交给Bundle页面重建的时候这些数据也会被保存,再次走一遍从Bundle中取数据的过程是完全不会报错的。所以页面上的数据不会被丢失了,而像RecyclerView,ViewPager之类的控件它们也会保存自己之前的状态,页面重建后,RecyclerView,ViewPager会记录自己滑动的位置的,这个不用担心,还有一点就是有一些控件,比如CoordinatorLayout你可能需要给它和它的子View控件一个Id才能保存滑动状态。

遵循这样的一个规则之后呢,就可以忽略这个页面重建的问题了。

5. Navigation的页面转场动画的一些问题


用过Navigation的都知道,页面转场动画要一个一个的添加,就像这样:

<fragment

android:id=“@+id/title_screen”

android:name=“com.example.android.navigationsample.TitleScreen”

android:label=“fragment_title_screen”

tools:layout=“@layout/fragment_title_screen”>

<action

android:id=“@+id/action_title_screen_to_register”

app:destination=“@id/register”

app:popEnterAnim=“@anim/slide_in_left”

app:popExitAnim=“@anim/slide_out_right”

app:enterAnim=“@anim/slide_in_right”

app:exitAnim=“@anim/slide_out_left”/>

<action

android:id=“@+id/action_title_screen_to_leaderboard”

app:destination=“@id/leaderboard”

app:popEnterAnim=“@anim/slide_in_left”

app:popExitAnim=“@anim/slide_out_right”

app:enterAnim=“@anim/slide_in_right”

app:exitAnim=“@anim/slide_out_left”/>

复制代码

每一个标签都要写一遍一样的代码,让我很头疼。于是我还是想到了,重写FragmentNavigator将所有的增加一个判断如果标签中没有设置专场动画,那么我就给这个Fragment添加上专场动画。

//我一开始设想的载源码位置处添加的动画操作

int enterAnim = navOptions != null ? navOptions.getEnterAnim() : 动画id;

int exitAnim = navOptions != null ? navOptions.getExitAnim() : 动画id;

int popEnterAnim = navOptions != null ? navOptions.getPopEnterAnim() : 动画id;

int popExitAnim = navOptions != null ? navOptions.getPopExitAnim() : 动画id;

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);

}

复制代码

然而我太天真了,我们想到的,谷歌爸爸都考虑过了。因为如果像我一样天真的加上这样的判断之后,你会发现,第一个默认Fragment也拥有了动画属性。而且做隐式链接跳转的时候,这个动画会非常影响观感。所以第一个默认Fragment不能有转场动画。当然后来我想到了判断返回栈是否存在为空,通过这个判断是否是第一个页面。但是我都能想到谷歌爸爸肯定也想到了。他们不这么做肯定是有原因的。于是我放弃了,老老实实的挨个复制粘贴,

6. Replace在重建Fragment的时候,过度动画卡顿


在使用 Navigation的时候,按下返回键回到上个页面,页面重建,这个时候会发现过度动画会有那么几百毫秒卡那么一下,一个转场动画也就400毫秒左右,卡那么一下效果是非常明显的。这也归功于Fragment重建的原因了,页面展示的数据量巨大的时候,重建时的绘制工作量也是相当的大,所以肯定会卡那么一下下啦。

那么延迟初始化数据能解决吗?

当然能,感觉多次一举,延迟初始化数据页面,数据会突然的从无到有,虽然不影响转场动画了,但是界面会闪一下,用户也会感到莫名其妙:这啥情况?页面怎么闪一下?

后来我仔细的观察发现,Activity进行跳转的时候会有那么100毫秒的延迟,Fragment进行切换的时候是没有延迟的,cua的一下,很快啊。

后来我发现了一个方法:

override fun onCreateAnimation(transit: Int, enter: Boolean, nextAnim: Int): Animation? {

return super.onCreateAnimation(transit, enter, nextAnim)

}

复制代码

吼~~ 我好像发现了什么ψ(`∇´)ψ。

我让它转场动画延迟几百毫秒执行不就行了,给Fragment重建填充数据时预留个时间,等它差不多重建ok了,再执行动画不就不卡了。

于是我再BaseFragment中重写了这个方法,并给动画100毫秒的延迟时间。

override fun onCreateAnimation(transit: Int, enter: Boolean, nextAnim: Int): Animation? {

if (enter) {

//如何nextAnim是-1或0那么就是没有设置转场动画,直接走super就行了

if (nextAnim > 0) {

val animation = AnimationUtils.loadAnimation(requireActivity(), nextAnim)

//延迟100毫秒执行让View有一个初始化的时间,防止初始化时刷新页面与动画刷新冲突造成卡顿

animation.startOffset = 100

animation.setAnimationListener(object : Animation.AnimationListener{

override fun onAnimationStart(animation: Animation?) {

}

override fun onAnimationEnd(animation: Animation?) {

onEnterAnimEnd()

}

override fun onAnimationRepeat(animation: Animation?) {

}

})

return animation

} else {

onEnterAnimEnd()

}

} else {

if (nextAnim > 0) {

val animation = AnimationUtils.loadAnimation(requireActivity(), nextAnim)

//延迟100毫秒执行让View有一个初始化的时间,防止初始化时刷新页面与动画刷新冲突造成卡顿

animation.startOffset = 100

return animation

}

}

return super.onCreateAnimation(transit, enter, nextAnim)

}

/**

  • 如果真的要延迟初始化,那么重写这个方法,等动画结束了再初始化

*/

fun onEnterAnimEnd(){

}

只需要监听进入动画就可以了,退出动画只给它延迟但不给任何的事件监听。

这样搞完之后,页面转场动画果然不卡了,延迟100毫秒也完全没有影响使用体验。效果很接近Activity转场。

我可真是个机灵鬼。

我分享的内容,和我是如何使用的就到这里,我不确定我的方式是不是完全的正确,但目前来看,项目跑的也是挺ok的。没出什么问题。

如果我有什么说的不正确的地方,大家也可以多多指点,交流一下经验。
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

最后

今天关于面试的分享就到这里,还是那句话,有些东西你不仅要懂,而且要能够很好地表达出来,能够让面试官认可你的理解,例如Handler机制,这个是面试必问之题。有些晦涩的点,或许它只活在面试当中,实际工作当中你压根不会用到它,但是你要知道它是什么东西。

最后在这里小编分享一份自己收录整理上述技术体系图相关的几十套腾讯、头条、阿里、美团等公司19年的面试题,把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分。

还有 高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料 帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。

【Android核心高级技术PDF文档,BAT大厂面试真题解析】

【算法合集】

【延伸Android必备知识点】

【Android部分高级架构视频学习资源】

**Android精讲视频领取学习后更加是如虎添翼!**进军BATJ大厂等(备战)!现在都说互联网寒冬,其实无非就是你上错了车,且穿的少(技能),要是你上对车,自身技术能力够强,公司换掉的代价大,怎么可能会被裁掉,都是淘汰末端的业务Curd而已!现如今市场上初级程序员泛滥,这套教程针对Android开发工程师1-6年的人员、正处于瓶颈期,想要年后突破自己涨薪的,进阶Android中高级、架构师对你更是如鱼得水,赶快领取吧!

《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门即可获取!

2)]

【延伸Android必备知识点】

[外链图片转存中…(img-ua0gZPPK-1712376294762)]

【Android部分高级架构视频学习资源】

**Android精讲视频领取学习后更加是如虎添翼!**进军BATJ大厂等(备战)!现在都说互联网寒冬,其实无非就是你上错了车,且穿的少(技能),要是你上对车,自身技术能力够强,公司换掉的代价大,怎么可能会被裁掉,都是淘汰末端的业务Curd而已!现如今市场上初级程序员泛滥,这套教程针对Android开发工程师1-6年的人员、正处于瓶颈期,想要年后突破自己涨薪的,进阶Android中高级、架构师对你更是如鱼得水,赶快领取吧!

《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门即可获取!
  • 22
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值