安卓MVVM—kotlin协程与Retrofit

本文将给大家演示如何在安卓项目使用协程+Retrofit进行网络请求,项目使用MVVM架构,引用了架构组件ViewModel和LiveData,包含了异常的封装处理,可以用在一个商用产品架构上。

1.引入依赖

//使用协程
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.2"
//OkHttp
implementation 'com.squareup.okhttp3:okhttp:4.2.2'
//retrofit2
implementation 'com.squareup.retrofit2:retrofit:2.6.2'
//retrofit协程适配器
implementation "com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2"
//Moshi(Json解析)
implementation 'com.squareup.moshi:moshi-kotlin:1.9.2'
kapt 'com.squareup.moshi:moshi-kotlin-codegen:1.9.2'
implementation 'com.squareup.retrofit2:converter-moshi:2.6.2'   //retrofit-moshi解析器

2.创建实体类

/**
 * 封装的网络请求结果实体类
 *
 * @author king
 * @date 2019-11-29 09:53
 */
sealed class DataResult {
    //成功
    data class Success(val result: T?) : DataResult()
    //失败
    data class Error(val errorCode:String?,val errorMessage: String?) : DataResult()
}
@JsonClass(generateAdapter = true)
data class MenuResult(val resultcode: String,
                      val reason: String,
                      val result: ResultBean,
                      val error_code: Int)

@JsonClass(generateAdapter = true)
data class ResultBean(val totalNum: String,
                      val pn: String,
                      val rn: String,
                      val data: List)

@JsonClass(generateAdapter = true)
data class FoodMenu(val id: String,
                    val title: String,
                    val tags: String,
                    val imtro: String,
                    val ingredients: String,
                    val burden: String,
                    val albums: List,
                    val steps: List)

@JsonClass(generateAdapter = true)
data class StepsBean(val img: String,
                     val step: String)

2.创建接口,用于请求网络接口

interface NetworkApi {

    @GET("/cook/query?key=${AppConfig.JUHE_KEY}")
    fun getFoodMenuAsync(@Query("menu") menu:String,
                         @Query("pn") pn:Int, @Query("rn") rn:Int) : Deferred>

}

因为要用到协程,所以返回了一个Deferred,而Response是Retrofit的网络请求结果,MenuResult为实体类

3.创建retrofit请求工具类

object ApiService {


    //构建OkHttpClient
    private val okHttpClient = OkHttpClient().newBuilder()
            .connectTimeout(60, TimeUnit.SECONDS)
            .proxy(Proxy.NO_PROXY)
            .readTimeout(60, TimeUnit.SECONDS)
            .writeTimeout(60, TimeUnit.SECONDS)
            .build()

    //构建retrofit
    private fun retrofit(): Retrofit = Retrofit.Builder()
            .client(okHttpClient)
            .baseUrl(AppConfig.BASE_API_URL)
            .addConverterFactory(MoshiConverterFactory.create())//moshi解析json适配器,自动将json解析为对象
            .addCallAdapterFactory(CoroutineCallAdapterFactory())//支持kotlin协程
            .build()

    //创建接口对象
    val api: NetworkApi = retrofit().create(NetworkApi::class.java)

    /**
     * 执行网络请求操作,并处理异常,这一方法也可忽略,在具体调用位置可以自己处理
     *
     * suspend声明只能在协程内执行
     */
    suspend fun  execute(call: suspend () -> Response): T? {
        val data = parseApiResult(call)
        var result: T? = null
        when (data) {
            is DataResult.Success -> result = data.result
            is DataResult.Error -> {
                //请求失败
                LogUtil.e(data.errorMessage)
            }
        }
        return result
    }

    /**
     * 执行并处理网络请求结果(包含异常处理)
     */
    private suspend fun  parseApiResult(call: suspend () -> Response): DataResult {
        try {
            val response = call.invoke()
            if (response.isSuccessful) {
                return DataResult.Success(response.body())
            }
            return DataResult.Error(response.code().toString(), response.message())
        }catch (e:Exception){
            LogUtil.e(e)
            return DataResult.Error("", e.message)
        }
    }

}

4.发起调用,并处理结果

class HomeViewModel : ViewModel() {

    val menuData = MutableLiveData>()
    val error = MutableLiveData()

    /**
     * 获取数据
     *
     * @param name
     * @param page
     * @param count
     */
    fun getMenuData(menu: String, page: Int, count: Int) {
        viewModelScope.launch(Dispatchers.Default) {
            //使用Retrofit接口
            val res = ApiService.execute(
                    call = { ApiService.api.getFoodMenuAsync(menu, page, count).await() })

//            val result = ApiService.execute (call= {
//               ApiService.api.getFoodMenuStr(menu, page, count).await()
//            })
//            LogUtil.i("结果$result")
//            val moshi = Moshi.Builder()
//                    .add(KotlinJsonAdapterFactory())
//                    .build()
//            val adapter = moshi.adapter(MenuResult::class.java)
//            val res = adapter.fromJson(result)

            if(res == null){
                //为空,说明请求失败了
                error.postValue(DataResult.Error("400", "Network Error"))
                return@launch
            }

            //请求成功
            if (res?.resultcode == "200") {
                //接口数据正常
                menuData.postValue(res?.result?.data)
            } else {
                error.postValue(DataResult.Error(res?.resultcode, res?.reason))
            }
        }
    }
}

以上就是全部使用示例,详情代码请参考:项目源码

你可能感兴趣的:(安卓开发,Kotlin)