《Android编程权威指南》之HTTP与后台任务(三)

81 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第5天,点击查看活动详情

继续《Android编程权威指南》第 24 章,本章应用叫 PhotoGallery,现在开始继续完善应用啦!

四、应对设备配置改变

目前的进度呢,网络请求回来的数据已经搞定了 JSON 数据解析,从前面的打印日志也是可以看出来了。然后呢,当设备发生旋转的时候,又出现了问题了,每次旋转都会发出一个新的网络请求,原因就在于 PhotoGalleryFragment 在设备旋转时会被销毁再重建,这其实还挺浪费资源的,作为用户当然也是不可以容忍的。

显然,本章的内容就是要解决旋转设备多次请求的问题。

「其实我们实际应用大多都会禁用旋转的啦,不过也避免不了少部分应用没有禁用,那没有禁用就得多去适配一下设备旋转页面销毁重建的数据问题啦」

我们现在需要的效果呢,就是在 fragment 首页创建显示发起一次网络请求得到数据,在设备配置发生改变的时候,缓存好数据,然后感知 fragment 生命周期变化,在需要的时候时候直接获取缓存好的数据显示,而不是重新发起新的网络请求。

现在呢,解决方案就是使用 ViewModel。关于 ViewModel,前面的章节也有介绍。

ViewModel 概况:developer.android.com/topic/libra…

  • 首先,添加好 ViewModel 的依赖:
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1"
  • 新建 PhotoGalleryViewModel.kt
class PhotoGalleryViewModel : ViewModel() {

    val galleryItemLiveData: LiveData<List<GalleryItem>>

    init {
        galleryItemLiveData = FlickrFetchr().fetchPhotos()
    }
}
  • 更新 PhotoGalleryFragment 类,获取 PhotoGalleryViewModel 对象。

这里我们获取 ViewModel 对象的时候做点修改,再加入一个依赖:

implementation("androidx.fragment:fragment-ktx:1.5.4")

此依赖提供了一系列的扩展程序帮助我们简化 Fragment API,语法糖级别了,很好用儿,这样我们在获取 ViewModel 对象就可以这么写:

private val viewModel: PhotoGalleryViewModel by viewModels()

ViewModel 的官方文档也有说明:ViewModel 允许数据在 ViewModel 持有的状态和 ViewModel 触发的操作结束后继续存在。这种缓存意味着在常见的配置更改(例如屏幕旋转)完成后,您无需重新提取数据。

因此在 PhotoGalleryFragment 类中,我们只需要把请求网络数据的调用换成使用viewModel就行了。在 onViewCreated 中新增代码:

viewModel.galleryItemLiveData.observe(viewLifecycleOwner,
    Observer { gallerayItems ->
        Log.d(TAG, "Response received:$gallerayItems")
    })

现在再运行项目,旋转设备,就不会再发生多次网络请求啦!

五、在RecyclerView里显示结果

本章最后的任务了,在 RecyclerView 把图片显示出来,先照着敲一遍代码后,我们后续再来优化 RecyclerView 使用,主要有各种库封装好了,特好用。先将书中任务完成再来扩展。

  • 先定义 ViewHolder:
private class PhotoHolder(itemTextView: TextView) : RecyclerView.ViewHolder(itemTextView) {
    val bindTitle: (CharSequence) -> Unit = itemTextView::setText
}
  • 接下来添加一个 RecyclerView.Adapter:
    private class PhotoAdapter(private val galleryItems:List<GalleryItem>):RecyclerView.Adapter<PhotoHolder>(){
        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PhotoHolder {
            val textView  = TextView(parent.context)
            return PhotoHolder(textView)
        }

        override fun onBindViewHolder(holder: PhotoHolder, position: Int) {
            val galleryItem = galleryItems[position]
            holder.bindTitle(galleryItem.title)
        }

        override fun getItemCount(): Int {
            return galleryItems.size
        }
    }
  • 最后就是给 adapter 关联一下数据:
photoRecyclerView.adapter = PhotoAdapter(gallerayItems)
  • 好啦,运行起来:

发现一个问题,这里 viewModel 没被调用,所以网络请求是不会被调用的,它只有监听,并没有被初始化使用,Kotlin 中的 by 是一种委托模式,可以延迟初始化加载,这里示例代码的写法其实并不适用,所以最终还是不用 fragment 中的扩展去获取 viewModel 啦,改为书中的方式。在 onCreate 方法中初始化 viewModel:

viewModel = ViewModelProvider(this)[PhotoGalleryViewModel::class.java]

再次运行:

不过由于网络问题,数据一直请求失败,可能哪里还有小问题,所以就没有运行效果啦,不过今天内容大体上还是学完啦。

image.png

好啦,优化了一下,补下效果图。

其他

PhotoGallery 项目 Demo 地址:

github.com/visiongem/A…


🌈关注我吖~❤️

公众号:妮K妮K妮