《Android编程权威指南》之HTTP与后台任务(挑战练习)

795 阅读3分钟

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

继续《Android编程权威指南》第 24 章,本章应用叫 PhotoGallery,现在继续将应用完善好。

九、挑战练习:自定义Gson反序列化器

这里是在说,我们的网络请求响应数据包的时候,包含了多层嵌套数据,然后让我们实现一个自定义反序列化器,把外层的 JSON 数据剔除。

接下来就是补充完整书中的提示代码:

class PhotoDeserializer : JsonDeserializer<PhotoResponse>{

    lateinit var photos : PhotoResponse

    override fun deserialize(json: JsonElement?, typeOfT: Type?, context: JsonDeserializationContext?): PhotoResponse {
        val photoJsonObject = json?.asJsonObject
        val gson = Gson()

        return gson.fromJson(photoJsonObject, PhotoResponse::class.java)
    }
}

FlickrApi.kt:

fun fetchPhotos(): Call<PhotoDeserializer>

FlickrFetchr.kt:

 init {

        val gsonPhotoDeserializer = GsonBuilder()
            .registerTypeAdapter(PhotoResponse::class.java, PhotoDeserializer())
            .create()

        val httpClientBuilder: OkHttpClient.Builder = OkHttpClient.Builder()
        httpClientBuilder
            .addInterceptor(HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY))

        val retrofit = Retrofit.Builder()
            .client(httpClientBuilder.build())
            .baseUrl("https://www.flickr.com/")
//            .addConverterFactory(GsonConverterFactory.create(GsonBuilder().setLenient().create()))
            .addConverterFactory(GsonConverterFactory.create(gsonPhotoDeserializer))
//            .addConverterFactory(MoshiConverterFactory.create(Moshi.Builder().addLast(KotlinJsonAdapterFactory()).build()))
            .build()
        flickrApi = retrofit.create(FlickrApi::class.java)
    }
    

fun fetchPhotos(): LiveData<List<GalleryItem>> {
    val responseLiveData: MutableLiveData<List<GalleryItem>> = MutableLiveData()
    val flickrRequest: Call<PhotoDeserializer> = flickrApi.fetchPhotos()

    val repository = Repository()
    repository.addFlickrCall(flickrRequest)

    flickrRequest.enqueue(object : Callback<PhotoDeserializer> {
        override fun onResponse(call: Call<PhotoDeserializer>, response: Response<PhotoDeserializer>) {
            Log.d(TAG, "Response received : ${response.body()}")

            val photoDeserializer: PhotoDeserializer? = response.body()
            val photoResponse: PhotoResponse? = photoDeserializer?.photos
            var galleryItems: List<GalleryItem> = photoResponse?.galleryItems ?: mutableListOf()
            galleryItems = galleryItems.filterNot { it.url.isBlank() }
            responseLiveData.value = galleryItems
        }

        override fun onFailure(call: Call<PhotoDeserializer>, t: Throwable) {
            Log.e(TAG, "Failed to fetch photos", t)
            if (call.isCanceled){
                Log.e(TAG, "request Canceled")
            }
        }
    })
    return responseLiveData
}

运行成功,结果是一样的。

十、挑战练习:分页

本章是说 getList 接口,默认就是返回一页 100 条数据,此接口还有个 page 参数,可以用来返回第二页、第三页等更多页的数据,「一般在我们实际项目中,还会有个 limit 参数,告诉后台一页返回多少条数据」,然后书中推荐我们研究下 Jetpack 分页库,不过项目中由于我引入了一个 RecyclerView Adapter 的库,叫 BaseRecyclerViewAdapterHelper.

地址:github.com/CymChad/Bas…

用它来做加载更多分页请求非常方便,具体实践就看下官方的实现方式吧。

由于要传个参数 page,因此 ViewModel 还需要稍微做下修改,手动去请求的啦,不用让它初始化就立马做一次请求。

然后书中推荐 Jecpack 分页库,关于分页库 Paging2 官方地址:

developer.android.com/topic/libra…

待我再研究一下再直接补充代码提交到 Github Demo 中。

十一、挑战练习:动态调整网格列

此章节作业要求就是,横屏的时候动态调整网格列数,使横屏的时候显示更多列标题。

然后本章给了个提示,简单实现就是再不同的设备配置或屏幕尺寸提供整数修饰资源,整数修饰资源放在 res/values 目录下,不过这种方式也有个劣势,它其实并不准确,不太好确定网割列分粒度,只能是凭经验多测试几次。

然后另外还有个提示就是,在 fragment 的视图创建就计算并设置好网格列数,注意不能在 onCreateView() 中计算网格列数,因为这个时候 RecyclerView 大小还没确定,建议实现 ViewTreeObserver.OnGlobalLayoutListener 监听器方法和计算列数的 onGlobalLayout() 函数,然后使用 addGoGlobalLayoutListener() 把监听器添加给 RecyclerView 视图。

提示很明显了,好好调试吧。

其他

PhotoGallery 项目 Demo 地址:

github.com/visiongem/A…


🌈关注我吖~❤️

公众号:妮K妮K妮