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

作者:年小个大
链接:https://juejin.cn/post/6925331537399382030

1. Navigation到底该如何正确的使用

相信大家对 Navigation都有所耳闻,我不细说怎么用了,官方的讲解也很详细。我是想说一下到底该如何更好的使用这个组件。

这个组件其实是需要配合官方的MVVM架构使用的,ViewModel+LiveData结合才能更好的展现出Navigation的优势。

在官方的讲解示例中没有用到ViewModel和LiveData,官方只是演示了Navigation怎么用怎么在页面之间传值,和这个组件的一些特性之类的。但真正用好还是要结合ViewModel和LiveData。

2. Navigation大家都以为的缺陷

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

3. 然而这不是缺陷

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

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

这里可以看到,有一些场景下,我们有某个页面可以打开和自己相同的页面,只不过是展示的数据不同而已。当我用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,这个我就不解释了。

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

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

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

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

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



        
        
    
复制代码

每一个标签都要写一遍一样的代码,让我很头疼。于是我还是想到了,重写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的。没出什么问题。

如果我有什么说的不正确的地方,大家也可以多多指点,交流一下经验。

尾声

Jetpack架构组件 并不是一项复杂的技术,很多开发者都可以快速上手。但也正是简单易懂,开发者却很容易忽视注解背后的底层技术。在面试和实际架构的过程中,对技术理解肤浅、缺少细节成为无数开发者的致命伤。最近整理收集了Jetpack架构组件 基础到实战底层学习手册,对于上面这些实战问题讲解很透彻,今天分享给大家。
全网都刷爆了,不会只有你不知道吧——起初Jetpack Navigation把我逼疯了,可是后来真香!_第3张图片

【点击获取】

Jetpack架构组件从入门到精通学习手册入门篇

这几个模块是 Jetpack架构组件 入门篇, 主要介绍 Jetpack架构组件 特性,分类、应用架构 、实战本节内容主要如下:
全网都刷爆了,不会只有你不知道吧——起初Jetpack Navigation把我逼疯了,可是后来真香!_第4张图片

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

Jetpack架构组件实战到原理手册—Data Binding篇

Google在2018年推出 Android Jetpack ,本人最近在学习 Android Jetpack ,如果你有研究过 Android Jetpack ,你会发现Livedata,ViewModel和Livecycles等一系列 Android Jetpack 组件非常适用于实现MVVM,因此,在进行 Android Jetpack 的下一步研究之前, 我们有必要学习一下MVVM设计模式以及Android中实现MVVM的 Data Binding 组件。
全网都刷爆了,不会只有你不知道吧——起初Jetpack Navigation把我逼疯了,可是后来真香!_第6张图片

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

Jetpack架构组件实战到原理手册— ViewModel & LiveData篇

由于 ViewModel 和 LiveData 关联性比较强且使用简单(其实 LiveData 可以和很多组件一起使用), 故打算一次性介绍这两个Android Jetpack 组件。
全网都刷爆了,不会只有你不知道吧——起初Jetpack Navigation把我逼疯了,可是后来真香!_第8张图片

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

Jetpack架构组件实战到原理手册— Room 篇

我们在日常的工作中,免不了和数据打交道,因此,存储数据便是一项很重要的工作,在此之前,我使用过GreenDao、DBFlow等优秀的ORM数据库框架,但是,这些框架都不是谷歌官方的,现在,我们有了谷歌官方的Room 数据库框架,看看它能够给我们带来什么?
全网都刷爆了,不会只有你不知道吧——起初Jetpack Navigation把我逼疯了,可是后来真香!_第10张图片

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

Jetpack架构组件实战到原理手册— Paging 篇

我相信几乎所有的Android开发者都会遇到在 RecyclerView 加载大量数据的情况,如果是在数据库请求,需要消耗数据库资源并且需要花费较多的时间,同样的,如果是发送网络请求,则需要消耗带宽和更多的时间,无论处于哪一种情形,对于用户的体验都是糟糕的。在这两种情形中,如果采用分段加载则缩短了时间,给用户带来了良好的体验。
全网都刷爆了,不会只有你不知道吧——起初Jetpack Navigation把我逼疯了,可是后来真香!_第12张图片

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

Jetpack架构组件实战到原理手册— WorkManger 篇

Android中处理后台任务的选择挺多的,比如 Service 、 DownloadManager 、 AlarmManager 、 JobScheduler等,那么选择 WorkManager 的理由是什么呢?
全网都刷爆了,不会只有你不知道吧——起初Jetpack Navigation把我逼疯了,可是后来真香!_第14张图片

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

Jetpack架构组件实战到原理手册— Lifecycle篇

一直以来,解藕都是软件开发永恒的话题。在Android开发中,解藕很大程度上表现为系统组件的生命周期与普通组件之间的解藕,因为普通组件在使用过程中需要依赖系统组件的的生命周期。举个例子,我们经常需要在页面的onCreate()方法中对组件进行初始化,然后在onStop()中停止组件,或者在onDestory()方法中对进行进行销毁。事实上,这样的工作非常繁琐,会让页面和页面耦合度变高,但又不得不做,因为如果不即时的释放资源,有可能会导致内存泄露。
全网都刷爆了,不会只有你不知道吧——起初Jetpack Navigation把我逼疯了,可是后来真香!_第16张图片

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

Jetpack架构组件实战到原理手册— Compose 最全上手指南

Jetpack Compose 是一个用于构建原生Android UI 的现代化工具包,它基于声明式的编程模型,因此你可以简单地描述UI的外观,而Compose则负责其余的工作-当状态发生改变时,你的UI将自动更新。由于Compose基于Kotlin构建,因此可以与Java编程语言完全互操作,并且可以直接访问所有Android和Jetpack API。它与现有的UI工具包也是完全兼容的,因此你可以混合原来的View和现在新的View,并且从一开始就使用Material和动画进行设计。
全网都刷爆了,不会只有你不知道吧——起初Jetpack Navigation把我逼疯了,可是后来真香!_第18张图片

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

Jetpack架构组件实战到原理手册— App Startup 篇

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

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

Android Jetpack最新更新组件介绍

Hilt 是一个帮助你简化 依赖注入 操作的 Android 类库,它让你可以专注于定义和注入的重要部分, 而无需担心管理所有的 DI 设置。基于 Dagger 之上,Hilt 继承了它的编译期正确性,也提升了运行时性能和可扩展性。Hilt 增加了对 Jetpack 类库和Android Framework 类的集成。例如,要注入 ViewModel 的参数的话,你可以在 ViewModel 的构造函数上添加@ViewModelInject 注解,并在 Fragment 上添加@AndroidEntryPoint 注解。
全网都刷爆了,不会只有你不知道吧——起初Jetpack Navigation把我逼疯了,可是后来真香!_第22张图片

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

Android Jetpack项目实战(从0搭建Jetpack版的WanAndroid客户端)

在接触Android Jetpack组件时, 就深深被其巧妙的设计和强大的功能所吸引,暗自告诉自己一定要学会这些组件,而网上并不能找到系统的学习资料,于是利用每天的时间访问Google Develper网站,把Jetpac的每个组件从使用到源码进行了系统的学习和总结。
全网都刷爆了,不会只有你不知道吧——起初Jetpack Navigation把我逼疯了,可是后来真香!_第24张图片

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

总结

这份学习笔记手册主要分为如下:
全网都刷爆了,不会只有你不知道吧——起初Jetpack Navigation把我逼疯了,可是后来真香!_第26张图片

学习资料可以免费分享给大家,需要完整版的朋友,【点击获取】.

你可能感兴趣的:(jetpack,程序人生,网络互联,Android,移动开发)