IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Kotlin Coroutine(协程): 四、+ Retrofit -> 正文阅读

[移动开发]Kotlin Coroutine(协程): 四、+ Retrofit


前言

Retrofit 从 2.6.0 版本开始, 内置了对 Kotlin Coroutines 的支持. 我们统一处理异常及响应状态码, 使用DSL 让代码更加漂亮整洁

先导包:

//协程
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.3"
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.3'

// Retrofit
implementation "com.squareup.retrofit2:retrofit:2.9.0"
implementation "com.squareup.retrofit2:converter-gson:2.9.0"
implementation "com.squareup.okhttp3:logging-interceptor:4.2.0"

一、准备工作

1.BaseResp

统一的返回实体, 包含 code, msg, data; 这种服务器响应结构比较常见

data class BaseResp<T>(
    // 响应状态码
    var code: Int = -1,
    // 响应信息
    var msg: String = "",
    // 数据实体
    var data: T? = null
)

2.Api

这里是一个验证码请求接口, 使用的 Json 提交参数

interface Api {
    @POST("api/getCode")
    suspend fun getCode(@Body body: RequestBody): BaseResp<JsonObject?>
}

3.ApiManager

使用 枚举单例模式; 包括 初始化 Retrofit, OkHttpClient, 添加请求Token, 请求日志打印.

/**
 * Retrofit 管理类;
 */
enum class ApiManager {
    INSTANCE;
    private val retrofit: Retrofit
    val mApi: Api
    private val mMediaTypeJson: MediaType?

    init {
        retrofit = Retrofit.Builder()
            .baseUrl("https://... 服务器地址")
            .client(initOkhttpClient())
            .addConverterFactory(GsonConverterFactory.create())
            .build()
        mApi = retrofit.create(Api::class.java)
        mMediaTypeJson = "application/json; charset=utf-8".toMediaTypeOrNull()
    }

    private fun initOkhttpClient(): OkHttpClient {
        return OkHttpClient.Builder()
            .retryOnConnectionFailure(true) //重试
            .connectTimeout(20, TimeUnit.SECONDS)
            .readTimeout(20, TimeUnit.SECONDS)
            .writeTimeout(20, TimeUnit.SECONDS)
            .addNetworkInterceptor(initTokenInterceptor())
            .addInterceptor(initLogInterceptor())
            .build()
    }

    private fun initTokenInterceptor(): Interceptor = object : Interceptor {
            override fun intercept(chain: Interceptor.Chain): Response {
                // 为请求添加 Token
                val token: String = SpUtils.getToken()
                val originalRequest = chain.request()
                return if (token.isNullOrEmpty()) {
                    chain.proceed(originalRequest)
                }else {
                    token = "Bearer $token"
                    val updateRequest = originalRequest.newBuilder().header("token", token).build()
                    chain.proceed(updateRequest)
                }
            }
        }


    /*
   * 日志拦截器
   * */
    private fun initLogInterceptor(): HttpLoggingInterceptor {
        if(!BuildConfig.DEBUG){   //生产环境不打日志
            return HttpLoggingInterceptor()
        }

        val interceptor = HttpLoggingInterceptor(object : HttpLoggingInterceptor.Logger {
            override fun log(message: String) {
                Log.i("Retrofit_network", message)
            }
        })
        interceptor.level = HttpLoggingInterceptor.Level.BODY
        return interceptor
    }

    // 请求数据转 Json 形式; 
    open fun getJsonBody(data: Any): RequestBody {
        val strEntity = mGson.toJson(data)
        return strEntity.toRequestBody(mMediaTypeJson)
    }
}

二、开始使用

1.简单使用

以下代码在 Activity 中;

lifecycleScope.launch {
    val param = ApiManager.INSTANCE.getJsonBody(mapOf("phone" to "13333333333", "codeType" to "1"))
    val data = ApiManager.INSTANCE.mApi.getCode(param).data
    binding.tvTitle.text = data.toString()
}

是不是非常简单, 协程内除了准备参数 就只剩两行代码.

上面代码并没有处理异常, 及请求失败的情况. 下面我们就开始封装请求

2.DSL

class RetrofitDSL<T> {
    internal lateinit var api: (suspend () -> BaseResp<T?>)
        private set
    internal var onSuccess: ((BaseResp<T?>) -> Unit)? = null
        private set
    internal var onFailed: ((msg: String?, code: Int) -> Unit)? = null
        private set
    internal var onComplete: (() -> Unit)? = null
        private set

    /**
     * 获取数据
     * @param block (T) -> Unit
     */
    fun api(block: suspend () -> BaseResp<T?>) {
        this.api = block
    }

    /**
     * 获取数据成功
     * @param block (T) -> Unit
     */
    fun onSuccess(block: (BaseResp<T?>) -> Unit) {
        this.onSuccess = block
    }

    /**
     * 获取数据失败
     * @param block (msg: String, errorCode: Int) -> Unit
     */
    fun onFailed(block: (msg: String?, code: Int) -> Unit) {
        this.onFailed = block
    }

    /**
     * 访问完成
     * @param block () -> Unit
     */
    fun onComplete(block: () -> Unit) {
        this.onComplete = block
    }
}

看到 onSuccess, onFailed 是不是有熟悉的感觉!

3.扩展函数

我们需要创建一个 .kt 文件; 为 协程作用域 添加扩展函数

fun <T> CoroutineScope.retrofit(
    dsl: RetrofitDSL<T>.() -> Unit
) {
    launch {
        val retrofitDsl = RetrofitDSL<T>()
        retrofitDsl.dsl()
        try {
            val result = retrofitDsl.api()
            when(val code = result.code){
                200 -> retrofitDsl.onSuccess?.invoke(result)
                ...  // 其他响应码
                else -> retrofitDsl.onFailed?.invoke(result.msg, code)
            }
        } catch (e: Exception) {
            retrofitDsl.onFailed?.invoke(e.message, -10)
        }
		... // 其他异常类型
		finally {
            retrofitDsl.onComplete?.invoke()
        }
    }
}

这里 只写了 Exception; 需要判断多种异常的话, 往 catch 后面加就行了. 同样, 响应码也随便加, 想怎么处理就怎么处理.

4.请求发起

以下代码在 ViewModel 中;

// 注意: <JsonObject> 与 Api 中配置的请求响应结果类型一致 BaseResp<JsonObject?> 
viewModelScope.retrofit<JsonObject> {	
    api { ApiManager.INSTANCE.mApi.getCode(param) }
    onSuccess {	// it = BaseResp<JsonObject?>
        _number.set(it.data?.getAsJsonPrimitive("code")?.asString)
    }
    onFailed { msg, _ ->
        Toast.makeText(getApplication(), msg, Toast.LENGTH_SHORT).show()
    }
    onComplete {
        Toast.makeText(getApplication(), "完事了", Toast.LENGTH_SHORT).show()
    }
}

onSuccess, onFailed, onComplete 都可以不写, 想写谁就写谁;

// 不关心请求结果的话; 一个 api 就够了
viewModelScope.retrofit<JsonObject> {
    api { ApiManager.INSTANCE.mApi.getCode(param) }
}

总结

好吧, 没有总结

上一篇: Kotlin Coroutine(协程): 三、了解协程

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-07-15 16:18:51  更:2021-07-15 16:19:43 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/6 9:17:27-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码