Retrofit+kotlin Coroutines(协程)+mvvm(Jetpack架构组件)实现更简洁的网络请求

前言

使用kotlin协程也有一段时间了,给我最大的感受就是完全可以替代Rxjava了,并且写起来更加的简洁。

6月份Retrofit发布的2.6.0版本内部支持了kotlin协程中的挂起(suspend)修饰符,这就意味着我们可以更加方便的用Retrofit结合kotlin协程来实现网络请求了。
在这里插入图片描述

之前我都是使用Rxjava2+Retrofit实现网络请求的功能,然后加入了AutoDispose来实现自动解绑以免发生内存泄漏的问题,感兴趣的可以看看AutoDispose代替RxLifecycle优雅的解决RxJava内存泄漏问题

那协程有没有自动解绑的东西呢。当然有了
目前google官方也给我们提供了androidx.lifecycle:lifecycle-viewmodel-ktx的依赖包,给ViewModel中扩展了一个作用域叫viewModelScope

viewModelScope是一个绑定到当前viewModel的作用域 当ViewModel被清除时会自动取消该作用域,所以不用担心内存泄漏为问题

那这样一来,我们完全可以使用Retrofit+Coroutines这个方案来代替之前用Retrofit+Rxjava+AutoDispose的方案了。

话不多说,直接上代码

我在网上找了个公开的API接口 https://www.apiopen.top/novelApi

我们就请求这个接口了

添加依赖

首先是添加需要的依赖包,如下

    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'androidx.core:core-ktx:1.1.0'
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.1"
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.1"
    implementation "androidx.lifecycle:lifecycle-extensions:2.1.0"
    implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.1.0"
    implementation "com.squareup.okhttp3:okhttp:4.2.0"
    implementation "com.squareup.retrofit2:retrofit:2.6.1"
    implementation "com.squareup.okhttp3:logging-interceptor:4.2.0"
    implementation "com.squareup.retrofit2:converter-gson:2.6.1"

代码

在看下面的代码之前,你可能需要先看一下下面几篇文章,如果你都知道的话 直接跳过即可

ViewModelLiveData 还不是很了解的话先看一下这篇文章
mvp过渡到mvvm(Android 架构组件)

关于一些好用的扩展方法,看一下这个
Kotlin基于RxJava的扩展方法(超级好用)
这里我们主要是看对数据类的扩展,节省一些无用代码

关于Retrofit的工厂类这里我就不放出来了,估计大家的代码都差不多,需要的可以看demo,这里我就不再浪费篇幅,直接用了。

实体类

响应基类

data class BaseResp<T>(
    var code: Int = 0,
    var msg: String = "",
    var `data`: T
)

小说数据实体类

package com.yzq.coroutineretofitmvvm.bean


import com.google.gson.annotations.SerializedName

data class Fiction(
    var bid: String = "",
    var bookname: String = "",
    var introduction: String = "",
    @SerializedName("book_info")
    var bookInfo: String = "",
    var chapterid: String = "",
    var topic: String = "",
    @SerializedName("topic_first")
    var topicFirst: String = "",
    @SerializedName("date_updated")
    var dateUpdated: Int = 0,
    var author: String = "",
    @SerializedName("author_name")
    var authorName: String = "",
    @SerializedName("top_class")
    var topClass: String = "",
    var state: String = "",
    var readCount: String = "",
    var praiseCount: String = "",
    @SerializedName("stat_name")
    var statName: String = "",
    @SerializedName("class_name")
    var className: String = "",
    var size: String = "",
    @SerializedName("book_cover")
    var bookCover: String = "",
    @SerializedName("chapterid_first")
    var chapteridFirst: String = "",
    var chargeMode: String = "",
    var digest: String = "",
    var price: String = "",
    var tag: List<String> = listOf(),
    @SerializedName("is_new")
    var isNew: Int = 0,
    var discountNum: Int = 0,
    @SerializedName("quick_price")
    var quickPrice: Int = 0,
    var formats: String = "",
    @SerializedName("audiobook_playCount")
    var audiobookPlayCount: String = "",
    var chapterNum: String = "",
    var isShortStory: Boolean = false,
    var userid: String = "",
    @SerializedName("search_heat")
    var searchHeat: String = "",
    @SerializedName("num_click")
    var numClick: String = "",
    @SerializedName("recommend_num")
    var recommendNum: String = "",
    @SerializedName("first_cate_id")
    var firstCateId: String = "",
    @SerializedName("first_cate_name")
    var firstCateName: String = "",
    var reason: String = ""
)

定义请求接口

这个没什么好说的,需要注意的就是我们的方法前面用 suspend 修饰

interface ApiService {
    @GET("https://www.apiopen.top/novelApi")
    suspend fun getFictions(): BaseResp<List<Fiction>>
}

用于解析响应数据的扩展方法

/*数据解析扩展函数*/
fun <T> BaseResp<T>.dataConvert(): T {
    if (code == 200) {
        return data
    } else {
        throw Exception(msg)
    }
}

ViewModel

注释也很详细了,这里就不多说了

class NetViewModel : ViewModel() {
    var fictions = MutableLiveData<List<Fiction>>()
    fun getFictions() {
        /*viewModelScope是一个绑定到当前viewModel的作用域  当ViewModel被清除时会自动取消该作用域,所以不用担心内存泄漏为问题*/
        viewModelScope.launch {
            try {
                /*withContext表示挂起块  配合Retrofit声明的suspend函数执行 该块会挂起直到里面的网络请求完成 最一行就是返回值*/
                val data = withContext(Dispatchers.IO) {

                    /*dataConvert扩展函数可以很方便的解析出我们想要的数据  接口很多的情况下下可以节省不少无用代码*/
                    RetrofitFactory.instance.getService(ApiService::class.java)
                        .getFictions().dataConvert()
                }

                /*给LiveData赋值  ui会自动更新*/
                fictions.value = data

            } catch (e: Exception) {


                /*请求异常的话在这里处理*/
                e.printStackTrace()

                Log.i("请求失败", "${e.message}")

            }


        }
    }


}

Activity

Activity中代码就很简单了,主要就是创建ViewModel示例,更新ui


class MainActivity : AppCompatActivity() {
    private lateinit var netViewModel: NetViewModel
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        /*创建viewmodel*/
        netViewModel = ViewModelProviders.of(this).get(NetViewModel::class.java)

        btn.setOnClickListener {
            /*请求数据*/
            netViewModel.getFictions()
        }

        /*数据发生变化时更新ui*/
        netViewModel.fictions.observe(this, Observer {
            tv.text = Gson().toJson(it)
        })
    }
}

下面我们来看看运行结果:

在这里插入图片描述

可以看到,网络请求的功能已经实现了。

那对比RxJava的实现方式,你觉得哪种方式更好呢?

好了,本篇文章到此结束。

demo


如果你觉得本文对你有帮助,麻烦动动手指顶一下,算是对本文的一个认可,如果文中有什么错误的地方,还望指正,转载请注明转自喻志强的博客 ,谢谢!

©️2020 CSDN 皮肤主题: 书香水墨 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值