美文网首页
Kotlin-协程网络请求封装

Kotlin-协程网络请求封装

作者: Method | 来源:发表于2021-11-15 17:40 被阅读0次

    依赖

    implementation 'androidx.core:core-ktx:1.3.2'
    implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.3.1'
    implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1'
    implementation 'com.squareup.retrofit2:retrofit:2.9.0'
    implementation 'com.squareup.okhttp3:logging-interceptor:5.0.0-alpha.2'
    implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
    

    封装步骤

    1.配置Retrofit、okhttp

    /**
     * retrofit 封装
     */
    object RetrofitManager {
        private const val READ_OUT_TIME = 10_000L
        private const val WRITE_OUT_TIME = 10_1000L
        private const val CONNECT_OUT_TIME = 10_1000L
    
        private val mRetrofit by lazy {
            Retrofit.Builder().baseUrl(BASE_URL)
                .client(mClient)
                .addConverterFactory(GsonConverterFactory.create())
                .build()
        }
    
        /**
         * 配置client
         */
        private val mClient by lazy {
            OkHttpClient.Builder().readTimeout(READ_OUT_TIME, TimeUnit.SECONDS)
                .writeTimeout(WRITE_OUT_TIME, TimeUnit.SECONDS)
                .connectTimeout(CONNECT_OUT_TIME, TimeUnit.SECONDS)
                .addInterceptor(mLogInterceptor)
                .build()
        }
    
        /**
         * 日志打印拦截器
         */
        private val mLogInterceptor by lazy {
            HttpLoggingInterceptor().apply {
                level = HttpLoggingInterceptor.Level.BODY
            }
        }
    
        /**
         * 创建接口Api
         */
        fun <T> createApi(service: Class<T>): T {
            return mRetrofit.create(service)
        }
    }
    

    2.请求数据转换

    2.1 创建请求接口apiService

    interface UserApi {
        @POST("user/login")
        suspend fun reqLogin(
            @Query("username") username: String,
            @Query("password") password: String
        ): BaseResult<UserBean>
    }
    

    2.1返回数据封装

    /**
     * 通用网络请求返回格式
     */
    data class BaseResult<T>(val data:T?,val errorCode:Int,val errorMsg:String)
    

    2.2 返回数据转换

    真正的数据请求放在Repository(官方的方案还会有一层)

     /**
    * 数据转换
    * BaseResult --> ApiResult
    * 根据errorCode 转换成对应的Data 或者 转换成错误信息
    */
    rivate suspend fun <T> covert(
       result: BaseResult<T>
    : ApiResult<T> {
       val code = result.errorCode
       val errorMsg = result.errorMsg
       return when (code) {
           //正常状态
           ApiCode.RESULT_OK -> {
               val data = result.data
               if (data != null) {
                   ApiResult.Success(data)
               } else {
                   ApiResult.Error(ErrorType.EMPTY, errorMsg, errorCode = code)
               }
           }   
           //需要登录状态
           ApiCode.RESULT_LOGIN -> {
               ApiResult.Error(ErrorType.NEED_LOGIN, errorMsg, errorCode = code)
           }
           //接口错误状态
           else -> {
               ApiResult.Error(ErrorType.NET_ERROR, errorMsg, errorCode = code)
           }
       }
    

    BaseResult可能是失败,也可能是成功,所以要对这两种情况做区分。这里转换规则是把BaseResult<T> 转换成ApiResult,ApiResult是密封类,只有两个子类一个是Success,一个是Error。

    /**
     * 网络请求数据转换类型
     * Success、Error类型
     */
    sealed class ApiResult<out T> {
        data class Success<out T>(val data: T) : ApiResult<T>()
        data class Error(
            val errorType: ErrorType,
            val errorMsg: String? = null,
            val error: Throwable? = null,
            val errorCode: Int = 0
        ) : ApiResult<Nothing>()
    }
    

    请求异常状态封装

    比如出现链接超时等非接口问题,kotlin中需要用异常捕获来处理。

    private suspend fun <T> safeApiCall(call: suspend () -> ApiResult<T>): ApiResult<T> {
            if (!NetState.isOk()) {
                return ApiResult.Error(ErrorType.NET_ERROR, NET_ERROR_SHOW)
            }
            return try {
                call()
            } catch (e: Throwable) {
                e.printStackTrace()
                if (!NetState.isOk()) {
                    ApiResult.Error(ErrorType.NET_ERROR, NET_ERROR_SHOW, e)
                } else {
                    ApiResult.Error(ErrorType.ERROR, e.message, e)
                }
            }
        }
    

    data转换

    可以将接口返回的data数据重新组装成想要的类,比如data+请求的参数。

    suspend fun <R, T> request(
            converter: ((R) -> T)? = null,
            api: suspend () -> BaseResult<R>
        ): ApiResult<T> {
            val apiResult = safeApiCall {
                covert(api())
            }
            return if (apiResult is ApiResult.Error) {
                apiResult
            } else {
                if (converter != null) {
                    val result = converter((apiResult as ApiResult.Success<R>).data)
                    if (null != result) {
                        ApiResult.Success(result)
                    } else {
                        ApiResult.Error(ErrorType.CONVERTER_ERROR, CONVERTER_CONTENT_ERROR)
                    }
                } else {
                    apiResult as? ApiResult.Success<T> ?: ApiResult.Error(
                        ErrorType.CONVERTER_ERROR,
                        CONVERTER_CONTENT_ERROR
                    )
                }
            }
        }
    

    调用

    class UserRepository : BaseRepository() {
        val api by lazy { RetrofitManager.createApi(UserApi::class.java) }
    
        suspend fun reqData() =
            request(converter = { it }, api = { api.reqLogin("13511112222", "1112") })
    }
    

    viewmodel 调用

    val repo by lazy { UserRepository() }
        private val _toastLd by lazy { MutableLiveData<String>() }
        val toastLd : LiveData<String> = _toastLd
    
        private val _uiLd by lazy { MutableLiveData<Boolean>() }
        val uiLd : LiveData<Boolean> = _uiLd
        fun reqData() {
            viewModelScope.launch {
                val result = withContext(Dispatchers.IO){
                    repo.reqData()
                }
                checkResult(result,
                    success = {
                        _uiLd.value = true
    //                    saveUser()
                    },
                    error = {
                        it?.apply {
                            _toastLd.value = this
                        }
                    },
                    isEmpty = {
                        it.token == null
                    },
                    empty = {
                        _toastLd.value = "数据是空"
                    })
            }
        }
    

    checkResult 主要对封装的数据判断,并通知UI更新。

    fun <T> checkResult(
        result: ApiResult<T>,
        isEmpty: ((T) -> Boolean)? = null,
        empty: (() -> Unit)? = null,
        error: ((String?) -> Unit)? = null,
        netError: ((String?) -> Unit)? = null,
        needLogin: ((String?) -> Unit)? = null,
        success: ((T) -> Unit)? = null
    ) {
        when (result) {
            is ApiResult.Success<T> -> {
                if (isEmpty?.invoke(result.data) == true) {
                    empty?.invoke()
                } else {
                    success?.invoke(result.data)
                }
            }
            is ApiResult.Error -> {
                val errorMsg = result.errorMsg
                when (result.errorType) {
                    ErrorType.NET_ERROR -> {
                        netError?.invoke(errorMsg)
                    }
                    ErrorType.ERROR -> {
                        error?.invoke(errorMsg)
                    }
                    ErrorType.NEED_LOGIN -> {
                        needLogin?.invoke(errorMsg)
                    }
                    ErrorType.CONVERTER_ERROR -> {
                        error?.invoke(errorMsg)
                    }
                    ErrorType.EMPTY -> {
                        empty?.invoke()
                    }
                }
            }
        }
    

    相关文章

      网友评论

          本文标题:Kotlin-协程网络请求封装

          本文链接:https://www.haomeiwen.com/subject/ummqtrtx.html