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,如有侵权,请分割删除。