1.简介

Retrofit对协程的反对十分的简陋。在kotlin中应用不合乎kotlin的优雅

interface TestServer {    @GET("banner/json")    suspend fun banner(): ApiResponse<List<Banner>>}//实现并行捕捉异样的网络申请 fun oldBanner(){        viewModelScope.launch {            //传统模式应用retrofit须要try catch            val bannerAsync1 = async {                var result : ApiResponse<List<Banner>>? = null                kotlin.runCatching {                   service.banner()                }.onFailure {                    Log.e("banner",it.toString())                }.onSuccess {                    result = it                 }                result            }            val bannerAsync2 = async {                var result : ApiResponse<List<Banner>>? = null                kotlin.runCatching {                    service.banner()                }.onFailure {                    Log.e("banner",it.toString())                }.onSuccess {                    result = it                }                result            }            bannerAsync1.await()            bannerAsync2.await()        }    }

一层嵌套一层,属实无法忍受。kotlin应该一行代码解决问题,才合乎kotlin的优雅

应用本框架后

interface TestServer {    @GET("banner/json")    suspend fun awaitBanner(): Await<List<Banner>>}   //实现并行捕捉异样的网络申请fun parallel(){     viewModelScope.launch {     val awaitBanner1 = service.awaitBanner().tryAsync(this)     val awaitBanner2 = service.awaitBanner().tryAsync(this)      //两个接口一起调用      awaitBanner1.await()      awaitBanner2.await()   }}

3.查看Retrofit源码

先看Retrofit create办法

public <T> T create(final Class<T> service) {    validateServiceInterface(service);    return (T)        Proxy.newProxyInstance(            service.getClassLoader(),            new Class<?>[] {service},            new InvocationHandler() {              private final Platform platform = Platform.get();              private final Object[] emptyArgs = new Object[0];              @Override              public @Nullable Object invoke(Object proxy, Method method, @Nullable Object[] args)                  throws Throwable {                // If the method is a method from Object then defer to normal invocation.                if (method.getDeclaringClass() == Object.class) {                  return method.invoke(this, args);                }                args = args != null ? args : emptyArgs;                return platform.isDefaultMethod(method)                    ? platform.invokeDefaultMethod(method, service, proxy, args)                    : loadServiceMethod(method).invoke(args);//具体调用              }            });  }

loadServiceMethod(method).invoke(args)进入这个办法看具体调用

咱们查看suspenForResponse中的adapt

@Override    protected Object adapt(Call<ResponseT> call, Object[] args) {      call = callAdapter.adapt(call);//如果用户不设置callAdapterFactory就应用DefaultCallAdapterFactory      //noinspection unchecked Checked by reflection inside RequestFactory.      Continuation<Response<ResponseT>> continuation =          (Continuation<Response<ResponseT>>) args[args.length - 1];      // See SuspendForBody for explanation about this try/catch.      try {        return KotlinExtensions.awaitResponse(call, continuation);      } catch (Exception e) {        return KotlinExtensions.suspendAndThrow(e, continuation);      }    }  }

前面间接交给协程去调用call。具体的okhttp调用在DefaultCallAdapterFactory。或者用户自定义的callAdapterFactory中

因而咱们这边能够自定义CallAdapterFactory在调用后不进行网络申请的拜访,在用户调用具体方法时候再进行网络申请拜访。

4.自定义CallAdapterFactory

Retrofit在调用后间接进行了网络申请,因而很不好操作。咱们把网络申请的控制权放在咱们手里,就能随便操作。

class ApiResultCallAdapterFactory : CallAdapter.Factory() {    override fun get(returnType: Type, annotations: Array<Annotation>, retrofit: Retrofit): CallAdapter<*, *>? {        //查看returnType是否是Call<T>类型的        if (getRawType(returnType) != Call::class.java) return null        check(returnType is ParameterizedType) { "$returnType must be parameterized. Raw types are not supported" }        //取出Call<T> 里的T,查看是否是Await<T>        val apiResultType = getParameterUpperBound(0, returnType)        // 如果不是 Await 则不禁本 CallAdapter.Factory 解决 兼容失常模式        if (getRawType(apiResultType) != Await::class.java) return null        check(apiResultType is ParameterizedType) { "$apiResultType must be parameterized. Raw types are not supported" }        //取出Await<T>中的T 也就是API返回数据对应的数据类型//        val dataType = getParameterUpperBound(0, apiResultType)        return ApiResultCallAdapter<Any>(apiResultType)    }}class ApiResultCallAdapter<T>(private val type: Type) : CallAdapter<T, Call<Await<T>>> {    override fun responseType(): Type = type    override fun adapt(call: Call<T>): Call<Await<T>> {        return ApiResultCall(call)    }}class ApiResultCall<T>(private val delegate: Call<T>) : Call<Await<T>> {    /**     * 该办法会被Retrofit解决suspend办法的代码调用,并传进来一个callback,如果你回调了callback.onResponse,那么suspend办法就会胜利返回     * 如果你回调了callback.onFailure那么suspend办法就会抛异样     *     * 所以咱们这里的实现是回调callback.onResponse,将okhttp的call delegate     */    override fun enqueue(callback: Callback<Await<T>>) {        //将okhttp call放入AwaitImpl间接返回,不做网络申请。在调用AwaitImpl的await时才真正开始网络申请        callback.onResponse(this@ApiResultCall, Response.success(delegate.toResponse()))    }}internal class AwaitImpl<T>(    private val call : Call<T>,) : Await<T> {    override suspend fun await(): T {        return try {            call.await()        } catch (t: Throwable) {            throw t        }    }}

通过下面自定义callAdapter后,咱们提早了网络申请,在调用Retrofit后并不会申请网络,只会将网络申请所须要的call的放入await中。

   @GET("banner/json")    suspend fun awaitBanner(): Await<List<Banner>>

咱们拿到的Await<List>并没有做网络申请。在这个实体类中蕴含了okHttp的call。

这时候咱们能够定义如下办法就能捕捉异样

suspend fun <T> Await<T>.tryAsync(    scope: CoroutineScope,    onCatch: ((Throwable) -> Unit)? = null,    context: CoroutineContext = SupervisorJob(scope.coroutineContext[Job]),    start: CoroutineStart = CoroutineStart.DEFAULT): Deferred<T?> = scope.async(context, start) {    try {        await()    } catch (e: Throwable) {        onCatch?.invoke(e)        null    }}

同样并行捕捉异样的申请,就能够通过如下形式调用,优雅简洁了很多

   /**     * 并行 async     */    fun parallel(){        viewModelScope.launch {            val awaitBanner1 = service.awaitBanner().tryAsync(this)            val awaitBanner2 = service.awaitBanner().tryAsync(this)            //两个接口一起调用            awaitBanner1.await()            awaitBanner2.await()        }    }

这时候咱们发现网络申请胜利了,解析数据失败。因为咱们在数据里面套了一层await。必定无奈解析胜利。

本着哪里谬误解决哪里的思路,咱们自定义Gson解析

5.自定义Gson解析

class GsonConverterFactory private constructor(private var responseCz : Class<*>,var responseConverter : GsonResponseBodyConverter, private val gson: Gson) : Converter.Factory() {    override fun responseBodyConverter(        type: Type, annotations: Array<Annotation>,        retrofit: Retrofit    ): Converter<ResponseBody, *> {        var adapter : TypeAdapter<*>? = null        //查看是否是Await<T>        if (Utils.getRawType(type) == Await::class.java && type is ParameterizedType){            //取出Await<T>中的T            val awaitType =  Utils.getParameterUpperBound(0, type)            if(awaitType != null){                adapter = gson.getAdapter(TypeToken.get(ParameterizedTypeImpl[responseCz,awaitType]))            }        }        //不是awiat失常解析,兼容失常模式        if(adapter == null){            adapter= gson.getAdapter(TypeToken.get(ParameterizedTypeImpl[responseCz,type]))        }        return responseConverter.init(gson, adapter!!)    }}class MyGsonResponseBodyConverter : GsonResponseBodyConverter() {    override fun convert(value: ResponseBody): Any {        val jsonReader = gson.newJsonReader(value.charStream())        val data = adapter.read(jsonReader) as ApiResponse<*>        val t = data.data        val listData = t as? ApiPagerResponse<*>        if (listData != null) {            //如果返回值值列表封装类,且是第一页并且空数据 那么给空异样 让界面显示空            if (listData.isRefresh() && listData.isEmpty()) {                throw ParseException(NetConstant.EMPTY_CODE, data.errorMsg)            }        }        // errCode 不等于 SUCCESS_CODE,抛出异样        if (data.errorCode != NetConstant.SUCCESS_CODE) {            throw ParseException(data.errorCode, data.errorMsg)        }        return t!!    }}

6.本框架应用

增加依赖

implementation "io.github.cnoke.ktnet:api:?"

写一个网络申请数据基类

open class ApiResponse<T>(    var data: T? = null,    var errorCode: String = "",    var errorMsg: String = "")

实现com.cnoke.net.factory.GsonResponseBodyConverter

class MyGsonResponseBodyConverter : GsonResponseBodyConverter() {    override fun convert(value: ResponseBody): Any {        val jsonReader = gson.newJsonReader(value.charStream())        val data = adapter.read(jsonReader) as ApiResponse<*>        val t = data.data        val listData = t as? ApiPagerResponse<*>        if (listData != null) {            //如果返回值值列表封装类,且是第一页并且空数据 那么给空异样 让界面显示空            if (listData.isRefresh() && listData.isEmpty()) {                throw ParseException(NetConstant.EMPTY_CODE, data.errorMsg)            }        }        // errCode 不等于 SUCCESS_CODE,抛出异样        if (data.errorCode != NetConstant.SUCCESS_CODE) {            throw ParseException(data.errorCode, data.errorMsg)        }        return t!!    }}

进行网络申请

interface TestServer {    @GET("banner/json")    suspend fun awaitBanner(): Await<List<Banner>>}val okHttpClient = OkHttpClient.Builder()            .addInterceptor(HeadInterceptor())            .addInterceptor(LogInterceptor())            .build()val retrofit = Retrofit.Builder()            .client(okHttpClient)            .baseUrl("https://www.wanandroid.com/")            .addCallAdapterFactory(ApiResultCallAdapterFactory())            .addConverterFactory(GsonConverterFactory.create(ApiResponse::class.java,MyGsonResponseBodyConverter()))            .build()val service: TestServer = retrofit.create(TestServer::class.java)lifecycleScope.launch {       val banner = service.awaitBanner().await()}

异步申请同步申请,异样捕捉参考如下try结尾的会捕捉异样,非try结尾不会捕捉。

fun banner(){    lifecycleScope.launch {        //独自解决异样 tryAwait会解决异样,如果异样返回空        val awaitBanner = service.awaitBanner().tryAwait()        awaitBanner?.let {            for(banner in it){                Log.e("awaitBanner",banner.title)            }        }        /**         * 不解决异样 异样会间接抛出,对立解决         */        val awaitBannerError = service.awaitBanner().await()    }}/** * 串行 await */fun serial(){    lifecycleScope.launch {        //先调用第一个接口await        val awaitBanner1 = service.awaitBanner().await()        //第一个接口实现后调用第二个接口        val awaitBanner2 = service.awaitBanner().await()    }}/** * 并行 async */fun parallel(){    lifecycleScope.launch {        val awaitBanner1 = service.awaitBanner().async(this)        val awaitBanner2 = service.awaitBanner().async(this)        //两个接口一起调用        awaitBanner1.await()        awaitBanner2.await()    }}

相干教程

Android根底系列教程:

Android根底课程U-小结_哔哩哔哩_bilibili

Android根底课程UI-布局_哔哩哔哩_bilibili

Android根底课程UI-控件_哔哩哔哩_bilibili

Android根底课程UI-动画_哔哩哔哩_bilibili

Android根底课程-activity的应用_哔哩哔哩_bilibili

Android根底课程-Fragment应用办法_哔哩哔哩_bilibili

Android根底课程-热修复/热更新技术原理_哔哩哔哩_bilibili

本文转自 https://juejin.cn/post/7051437444062773285,如有侵权,请分割删除。