Coroutine + Retrofit 封装网络请求

2023-11-01 20:01

本文主要是介绍Coroutine + Retrofit 封装网络请求,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Coroutine + Retrofit 封装网络请求

参考文章

1 Retrofit+Kotlin Coroutine重构Android网络框架

2 OkHttp踩坑记:为何 response.body().string() 只能调用一次?

1 定义实体类

本文以wanandroid项目服务器提供的接口https://www.wanandroid.com/user/login为例来描述客户端需要定义的实体类。

服务器返回的数据格式如下:

{"data": {"admin": false,"chapterTops": [],"coinCount": 126,"collectIds": [26980],"email": "","icon": "","id": 150842,"nickname": "13163268087","password": "","publicName": "13163268087","token": "","type": 0,"username": "13163268087"},"errorCode": 0,"errorMsg": ""
}

1.1 定义的实体类如下

data class BaseResponse<T>(val errorCode: Int, val errorMsg: String, val data: T)data class User (val id: Int,val username: String?,val nickname: String?,val token: String?,val icon: String?,val email: String?,val password: String?,val signatrue: String?,val sex: String?,val birthday: String?
)

另外我们要考虑到服务器返回的业务数据出错的情况,针对本例,即json数据的errorCode不为0,且data字段为null的情况。服务器返回成功的数据用BaseResponse定义,服务器返回失败的数据,可以用自定义的Failure结构体定义,该结构体包含了errorCode和errorMsg两个字段。为此我们定义如下的实体类。

sealed class ApiResult<out T> {data class Success<out T>(val data: T):ApiResult<T>()data class Failure(val errorCode:Int, val errorMsg:String):ApiResult<Nothing>()
}

同时定义了业务异常的实体类,如下:

/*** 客户端本地定义的网络请求的 errorCode 和 errorMsg** 这里的errorCode < 0,为了与服务器返回的errorCode做区分,服务器返回的 errorCode > 0*/
object ApiError {//数据是nullval dataIsNull = Error(-1,"data is null")//http status code 不是 成功val httpStatusCodeError = Error(-2,"Server error. Please try again later.")//未知异常val unknownException = Error(-3,"unknown exception")
}data class Error(val errorCode: Int, val errorMsg: String)

2 Retrofit实例的获取与配置

2.1 定义api接口

interface ApiInterface {/*** 登录** @param username 用户名* @param password 密码*/@FormUrlEncoded@POST("/user/login")suspend fun login2(@Field("username") username: String,@Field("password") password: String): ApiResult<BaseResponse<User>>}

/user/login 该接口请求返回的数据类型是ApiResult<BaseResponse>,是挂起函数,suspend。该接口的实例需要是单例的。如下定义获取ApiInterface的实例。

object HttpManager {val service: ApiInterface by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {build()}private fun build(): ApiInterface {// 1 log 拦截器。val loggingInterceptor = HttpLoggingInterceptor { message ->Log.d("HttpManager", "log: wyj message:$message")}if (BuildConfig.DEBUG) {loggingInterceptor.level = HttpLoggingInterceptor.Level.BODY} else {loggingInterceptor.level = HttpLoggingInterceptor.Level.NONE}// 2 OkHttp的缓存设置,缓存目录和缓存大小。val cache = Cache(File(MyApplication.application.cacheDir, "cache"),1024 * 1024 * 50)val okHttpClient = OkHttpClient.Builder().addInterceptor(loggingInterceptor).addInterceptor(HeaderInterceptor()) // 3 读取cookie的缓存,在请求头部设置cookie的字段。.addInterceptor(CookieInterceptor()) //4 将服务器返回的cookie存到本地.addInterceptor(BusinessErrorInterceptor()) // 5 服务器返回错误的业务数据,进行拦截以Failure的形式回掉.cache(cache).build()return Retrofit.Builder().client(okHttpClient).baseUrl("https://www.wanandroid.com").addConverterFactory(GsonConverterFactory.create()).addCallAdapterFactory(ApiResultCallAdapterFactory())// 6 将Call转换为自定义数据类型的CallAdapter.build().run {create(ApiInterface::class.java)}}
}

2.2 服务器返回错误的业务数据的处理

服务器返回错误的业务数据指的是服务器相应状态码仍然是200,但是业务数据是错误的,本地而言,errorCode不为0,且data数据为null,这种场景,可以通过定义BusinessErrorInterceptor拦截返回的response,解析业务数据,判断为错误的业务数据后,抛出异常,从而结束请求,回调Callback的onFailure函数。可以定义一个继承IOException的异常来,并封装errorCode和errorMsg字段。

class BusinessErrorInterceptor :Interceptor{override fun intercept(chain: Interceptor.Chain): Response {var response = chain.proceed(chain.request())//1 http status不是成功的情况下,我们不处理if (!response.isSuccessful){return response}        val responseBody = response.body()!!val source = responseBody.source()source.request(Long.MAX_VALUE) // Buffer the entire body.var buffer = source.bufferval contentType = responseBody.contentType()val charset: Charset = contentType?.charset(UTF_8) ?: UTF_8val resultString = buffer.clone().readString(charset)val jsonObject = JSONObject(resultString)if (!jsonObject.has("errorCode")) {return response}val errorCode = jsonObject.optInt("errorCode", -1000)//2 对于业务成功的情况不做处理if (errorCode == 0) {return response}val errorMsg = jsonObject.optString("errorMsg", "some error msg")// 3 抛出异常,结束网络请求,回调Callback#onFailure函数。throw ApiException(errorCode, errorMsg)}}

ApiException需要继承IOException,只有IOException才会由Interceptor自定义并回调Callback#onFailure函数。

class ApiException(val errorCode:Int,val errorMsg:String): IOException()

以下是OkHttp库中的RealCall的代码。

 final class RealCall implements Call {...final class AsyncCall extends NamedRunnable {...@Override protected void execute() {boolean signalledCallback = false;transmitter.timeoutEnter();try {Response response = getResponseWithInterceptorChain();signalledCallback = true;responseCallback.onResponse(RealCall.this, response);} catch (IOException e) {// 1 拦截器中抛出的IOException 才能自定义。if (signalledCallback) {// Do not signal the callback twice!Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);} else {responseCallback.onFailure(RealCall.this, e);}} catch (Throwable t) { // 2 拦截器中的非IOException,返回给Callback#onFailure函数的异常类型已写死。cancel();if (!signalledCallback) {IOException canceledException = new IOException("canceled due to " + t);canceledException.addSuppressed(t);responseCallback.onFailure(RealCall.this, canceledException);}throw t;} finally {client.dispatcher().finished(this);}}}...   }

2.3 自定义CallAdapter

http响应的数据类型为T,本例中对应BaseResponse,CallAdapter可以将自定义的Call<ApiResult<BaseResponse>>和Call<BaseResponse>关联起来,前者的接口实现完全委托给后者,从而使得接口的返回值为自定义的ApiResult<BaseResponse>,即在BaseResponse基础上包装了一层。

如下CallAdapter的源码:

/*** Adapts a {@link Call} with response type {@code R} into the type of {@code T}. Instances are* created by {@linkplain Factory a factory} which is {@linkplain* Retrofit.Builder#addCallAdapterFactory(Factory) installed} into the {@link Retrofit} instance.*/
public interface CallAdapter<R, T> {/*** Returns the value type that this adapter uses when converting the HTTP response body to a Java* object. For example, the response type for {@code Call<Repo>} is {@code Repo}. This type is* used to prepare the {@code call} passed to {@code #adapt}.** <p>Note: This is typically not the same type as the {@code returnType} provided to this call* adapter's factory.*/Type responseType();/*** Returns an instance of {@code T} which delegates to {@code call}.** <p>For example, given an instance for a hypothetical utility, {@code Async}, this instance* would return a new {@code Async<R>} which invoked {@code call} when run.** <pre><code>* &#64;Override* public &lt;R&gt; Async&lt;R&gt; adapt(final Call&lt;R&gt; call) {*   return Async.create(new Callable&lt;Response&lt;R&gt;&gt;() {*     &#64;Override*     public Response&lt;R&gt; call() throws Exception {*       return call.execute();*     }*   });* }* </code></pre>*/T adapt(Call<R> call);/*** Creates {@link CallAdapter} instances based on the return type of {@linkplain* Retrofit#create(Class) the service interface} methods.*/abstract class Factory {/*** Returns a call adapter for interface methods that return {@code returnType}, or null if it* cannot be handled by this factory.*/public abstract @Nullable CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit);/*** Extract the upper bound of the generic parameter at {@code index} from {@code type}. For* example, index 1 of {@code Map<String, ? extends Runnable>} returns {@code Runnable}.*/protected static Type getParameterUpperBound(int index, ParameterizedType type) {return Utils.getParameterUpperBound(index, type);}/*** Extract the raw class type from {@code type}. For example, the type representing {@code* List<? extends Runnable>} returns {@code List.class}.*/protected static Class<?> getRawType(Type type) {return Utils.getRawType(type);}}
}

CallAdapter.Factory 是根据定义的接口返回值类型(ApiResult<BaseResponse>)得到自定义的CallAdapter的实例。

自定义CallAdapter、CallAdapter.Factory以及自定义的Call如下:

class ApiResultCallAdapterFactory : CallAdapter.Factory() {override fun get(returnType: Type,annotations: Array<Annotation>,retrofit: Retrofit): CallAdapter<*, *>? {check(getRawType(returnType) == Call::class.java) { "$returnType must be retrofit2.Call." }check(returnType is ParameterizedType) { "$returnType must be parameterized. Raw types are not supported" }//取出Call<T> 里的T,检查是否是ApiResult<T>val apiResultType = getParameterUpperBound(0, returnType)check(getRawType(apiResultType) == ApiResult::class.java) { "$apiResultType must be ApiResult." }check(apiResultType is ParameterizedType) { "$apiResultType must be parameterized. Raw types are not supported" }//取出ApiResult<T>中的T 也就是API返回数据对应的数据类型val dataType = getParameterUpperBound(0, apiResultType)return ApiResultCallAdapter<Any>(dataType)}}class ApiResultCallAdapter<T>(private val type: Type) : CallAdapter<T, Call<ApiResult<T>>> {override fun responseType(): Type = typeoverride fun adapt(call: Call<T>): Call<ApiResult<T>> {return ApiResultCall(call)}
}class ApiResultCall<T>(private val delegate: Call<T>) : Call<ApiResult<T>> {/*** 该方法会被Retrofit处理suspend方法的代码调用,并传进来一个callback,如果你回调了callback.onResponse,那么suspend方法就会成功返回* 如果你回调了callback.onFailure那么suspend方法就会抛异常** 所以我们这里的实现是永远回调callback.onResponse,只不过在请求成功的时候返回的是ApiResult.Success对象,* 在失败的时候返回的是ApiResult.Failure对象,这样外面在调用suspend方法的时候就不会抛异常,一定会返回ApiResult.Success 或 ApiResult.Failure*/override fun enqueue(callback: Callback<ApiResult<T>>) {//delegate 是用来做实际的网络请求的Call<T>对象,为OkHttpCall,网络请求的成功失败会回调不同的方法delegate.enqueue(object : Callback<T> {/*** 网络请求成功返回,会回调该方法(无论status code是不是200)*/override fun onResponse(call: Call<T>, response: Response<T>) {if (response.isSuccessful) {//http status 是200+//这里担心response.body()可能会为null(还没有测到过这种情况),所以做了一下这种情况的处理,// 处理了这种情况后还有一个好处是我们就能保证我们传给ApiResult.Success的对象就不是null,这样外面用的时候就不用判空了val apiResult = if (response.body() == null) {ApiResult.Failure(ApiError.dataIsNull.errorCode,ApiError.dataIsNull.errorMsg)} else {ApiResult.Success(response.body()!!)}callback.onResponse(this@ApiResultCall, Response.success(apiResult))} else {//http status错误val failureApiResult = ApiResult.Failure(ApiError.httpStatusCodeError.errorCode,ApiError.httpStatusCodeError.errorMsg)callback.onResponse(this@ApiResultCall, Response.success(failureApiResult))}}/*** 在网络请求中发生了异常,会回调该方法** 对于网络请求成功,但是业务失败的情况,我们也会在对应的Interceptor中抛出异常,这种情况也会回调该方法*/override fun onFailure(call: Call<T>, t: Throwable) {val failureApiResult =if (t is ApiException) {//Interceptor里会通过throw ApiException 来直接结束请求 同时ApiException里会包含错误信息ApiResult.Failure(t.errorCode, t.errorMsg)} else {ApiResult.Failure(ApiError.unknownException.errorCode,ApiError.unknownException.errorMsg)}callback.onResponse(this@ApiResultCall, Response.success(failureApiResult))}})}override fun clone(): Call<ApiResult<T>> = ApiResultCall(delegate.clone())override fun execute(): Response<ApiResult<T>> {throw UnsupportedOperationException("ApiResultCall does not support synchronous execution")}override fun isExecuted(): Boolean {return delegate.isExecuted}override fun cancel() {delegate.cancel()}override fun isCanceled(): Boolean {return delegate.isCanceled}override fun request(): Request {return delegate.request()}override fun timeout(): Timeout {return delegate.timeout()}
}

(1) 重新定义了自定义泛型参数的Call类 ApiResultCall,泛型参数类型为接口的返回直接类型ApiResult<BaseResponse>,ApiResultCall的实现完全委托给Call<BaseResponse>,比如重写了enqueue、execute等核心方法。

(2) 重新定义了CallAdapter 为ApiResultCallAdapter,用来关联ApiResultCall<ApiResult<BaseResponse>>和Call<BaseResponse>,后者的实例类型为OkHttpCall(下文会分析)。

(3) 重新定义了CallAdapter.Factory,为ApiResultCallAdapterFactory。用来构造自定义的CallAdapter实例。

当Retrofit加载接口时候,会解析接口中声明的注解,解析过程中会根据Retrofit配置的CallAdapter.Factory来构建CallAdapter,进而关联两个Call。

2.3.1 自定义Call的enqueue方法的实现

ApiResultCall<ApiResult<BaseResponse>>的enqueue方法完全委托给OkHttpCall<BaseResponse>。

class ApiResultCall<T>(private val delegate: Call<T>) : Call<ApiResult<T>> {/*** 该方法会被Retrofit处理suspend方法的代码调用,并传进来一个callback,如果回调了callback.onResponse,那么suspend方法就会成功返回* 如果回调了callback.onFailure那么suspend方法就会抛异常** 所以这里的实现是永远回调callback.onResponse,只不过在请求成功的时候返回的是ApiResult.Success对象,* 在失败的时候返回的是ApiResult.Failure对象,这样外面在调用suspend方法的时候就不会抛异常,一定会返回ApiResult.Success 或 ApiResult.Failure*/override fun enqueue(callback: Callback<ApiResult<T>>) {//delegate 是用来做实际的网络请求的Call<T>对象,为OkHttpCall,网络请求的成功失败会回调不同的方法delegate.enqueue(object : Callback<T> {/*** 网络请求成功返回,会回调该方法(无论status code是不是200)*/override fun onResponse(call: Call<T>, response: Response<T>) {if (response.isSuccessful) {//http status 是200+//这里担心response.body()可能会为null(还没有测到过这种情况),所以做了一下这种情况的处理,// 处理了这种情况后还有一个好处是我们就能保证我们传给ApiResult.Success的对象就不是null,这样外面用的时候就不用判空了val apiResult = if (response.body() == null) {ApiResult.Failure(ApiError.dataIsNull.errorCode,ApiError.dataIsNull.errorMsg)} else {ApiResult.Success(response.body()!!)}callback.onResponse(this@ApiResultCall, Response.success(apiResult))} else {//http status错误val failureApiResult = ApiResult.Failure(ApiError.httpStatusCodeError.errorCode,ApiError.httpStatusCodeError.errorMsg)callback.onResponse(this@ApiResultCall, Response.success(failureApiResult))}}/*** 在网络请求中发生了异常,会回调该方法* 对于网络请求成功,但是业务失败的情况,我们也会在对应的Interceptor中抛出异常,这种情况也会回调该方法*/override fun onFailure(call: Call<T>, t: Throwable) {val failureApiResult =if (t is ApiException) {//Interceptor里会通过throw ApiException 来直接结束请求 同时ApiException里会包含错误信息ApiResult.Failure(t.errorCode, t.errorMsg)} else {ApiResult.Failure(ApiError.unknownException.errorCode,ApiError.unknownException.errorMsg)}callback.onResponse(this@ApiResultCall, Response.success(failureApiResult))}})}}

3 Retrofit加载接口的流程

Retrofit加载调用对应的接口会先加载接口然后调用。该操作会执行到如下代码:

3.1 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];@Overridepublic @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;// 1 当接口实例调用对应的接口会执行loadServiceMethod(method).invoke(args),先加载接口,然后调用。return platform.isDefaultMethod(method)? platform.invokeDefaultMethod(method, service, proxy, args): loadServiceMethod(method).invoke(args);}});}

Retrofit通过create构建接口实例的过程中会构建代理对象得到InvocationHandler,当通过接口实例调用对应的接口时候会触发invoke方法。然后加载对应的接口并调用。

3.2 Retrofit#loadServiceMethod

  ServiceMethod<?> loadServiceMethod(Method method) {// 1如果缓存中有以加载好的ServiceMethod实例则直接返回ServiceMethod<?> result = serviceMethodCache.get(method);if (result != null) return result;synchronized (serviceMethodCache) {result = serviceMethodCache.get(method);if (result == null) {// 2 解析对应的方法,得到ServiceMethod实例后,缓存起来。result = ServiceMethod.parseAnnotations(this, method);serviceMethodCache.put(method, result);}}return result;}

如果缓存中有以加载好的ServiceMethod实例则直接返回。否则先解析对应的方法,得到ServiceMethod实例后,缓存起来以便以后重复利用。

3.3 ServiceMethod.parseAnnotations

  static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);Type returnType = method.getGenericReturnType();if (Utils.hasUnresolvableType(returnType)) {throw methodError(method,"Method return type must not include a type variable or wildcard: %s",returnType);}if (returnType == void.class) {throw methodError(method, "Service methods cannot return void.");}return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);}

会调用HttpServiceMethod.parseAnnotations静态方法得到HttpServiceMethod的实例。

3.4 HttpServiceMethod#parseAnnotations

  static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(Retrofit retrofit, Method method, RequestFactory requestFactory) {// 1 接口如果声明的是suspend则isKotlinSuspendFunction为true。boolean isKotlinSuspendFunction = requestFactory.isKotlinSuspendFunction;boolean continuationWantsResponse = false;boolean continuationBodyNullable = false;Annotation[] annotations = method.getAnnotations();Type adapterType;if (isKotlinSuspendFunction) {Type[] parameterTypes = method.getGenericParameterTypes();// 2 responseType为ApiResult<BaseResponse<User>>Type responseType =Utils.getParameterLowerBound(0, (ParameterizedType) parameterTypes[parameterTypes.length - 1]);if (getRawType(responseType) == Response.class && responseType instanceof ParameterizedType) {// Unwrap the actual body type from Response<T>.responseType = Utils.getParameterUpperBound(0, (ParameterizedType) responseType);continuationWantsResponse = true;} else {// TODO figure out if type is nullable or not// Metadata metadata = method.getDeclaringClass().getAnnotation(Metadata.class)// Find the entry for method// Determine if return type is nullable or not}adapterType = new Utils.ParameterizedTypeImpl(null, Call.class, responseType);annotations = SkipCallbackExecutorImpl.ensurePresent(annotations);} else {adapterType = method.getGenericReturnType();}// 3 创建CallAdapter。触发CallAdapter.Factory的构建CallAdapter的流程。CallAdapter<ResponseT, ReturnT> callAdapter =createCallAdapter(retrofit, method, adapterType, annotations);// 4 responseType为BaseResponse<User>Type responseType = callAdapter.responseType();if (responseType == okhttp3.Response.class) {throw methodError(method,"'"+ getRawType(responseType).getName()+ "' is not a valid response body type. Did you mean ResponseBody?");}if (responseType == Response.class) {throw methodError(method, "Response must include generic type (e.g., Response<String>)");}// TODO support Unit for Kotlin?if (requestFactory.httpMethod.equals("HEAD") && !Void.class.equals(responseType)) {throw methodError(method, "HEAD method must use Void as response type.");}Converter<ResponseBody, ResponseT> responseConverter =createResponseConverter(retrofit, method, responseType);okhttp3.Call.Factory callFactory = retrofit.callFactory;if (!isKotlinSuspendFunction) {return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);} else if (continuationWantsResponse) {//noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.return (HttpServiceMethod<ResponseT, ReturnT>)new SuspendForResponse<>(requestFactory,callFactory,responseConverter,(CallAdapter<ResponseT, Call<ResponseT>>) callAdapter);} else {//noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.// 5 构建SuspendForBody。return (HttpServiceMethod<ResponseT, ReturnT>)new SuspendForBody<>(requestFactory,callFactory,responseConverter,(CallAdapter<ResponseT, Call<ResponseT>>) callAdapter,continuationBodyNullable);}}

HttpServiceMethod#parseAnnotations方法的执行流程。

(1) 判断接口方法是否是suspend方法。

(2) 根据方法得到返回值类型。

(3) 由Retrofit配置的CallAdapter.Factory构建CallAdapter实例。

(4) 构建SuspendForBody的实例该类是ServiceMethod的派生类。

3.5 Retrofit构建CallAdapter

  public CallAdapter<?, ?> callAdapter(Type returnType, Annotation[] annotations) {return nextCallAdapter(null, returnType, annotations);}public CallAdapter<?, ?> nextCallAdapter(@Nullable CallAdapter.Factory skipPast, Type returnType, Annotation[] annotations) {...int start = callAdapterFactories.indexOf(skipPast) + 1;// 1 遍历配置的CallAdapter.Factory集合,由Factory构建CallAdapter实例。for (int i = start, count = callAdapterFactories.size(); i < count; i++) {CallAdapter<?, ?> adapter = callAdapterFactories.get(i).get(returnType, annotations, this);if (adapter != null) {return adapter;}}StringBuilder builder =new StringBuilder("Could not locate call adapter for ").append(returnType).append(".\n");if (skipPast != null) {builder.append("  Skipped:");for (int i = 0; i < start; i++) {builder.append("\n   * ").append(callAdapterFactories.get(i).getClass().getName());}builder.append('\n');}builder.append("  Tried:");for (int i = start, count = callAdapterFactories.size(); i < count; i++) {builder.append("\n   * ").append(callAdapterFactories.get(i).getClass().getName());}throw new IllegalArgumentException(builder.toString());}

遍历配置的CallAdapter.Factory集合,由Factory构建CallAdapter实例。针对本例,会调用ApiResultCallAdapterFactory#get方法构建

ApiResultCallAdapter实例。

3.6 ServiceMethod#invoke

得到SuspendForBody对象后随即执行invoke方法。

abstract class HttpServiceMethod<ResponseT, ReturnT> extends ServiceMethod<ReturnT> {...@Overridefinal @Nullable ReturnT invoke(Object[] args) {1 构建OkHttpCall对象,然后执行adapt方法。Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);return adapt(call, args);}...static final class SuspendForBody<ResponseT> extends HttpServiceMethod<ResponseT, Object> {...@Overrideprotected Object adapt(Call<ResponseT> call, Object[] args) {// 1 调用CallAdapter#adapt方法关联 Call<ApiResult<BaseResponse<User>>>和OkHttpCall<BaseResponse<User>>call = callAdapter.adapt(call);//noinspection unchecked Checked by reflection inside RequestFactory.Continuation<ResponseT> continuation = (Continuation<ResponseT>) args[args.length - 1];...// 2 isNullable为false,调用KotlinExtensions.awaittry {return isNullable? KotlinExtensions.awaitNullable(call, continuation): KotlinExtensions.await(call, continuation);} catch (Exception e) {return KotlinExtensions.suspendAndThrow(e, continuation);}}}  
}

ServiceMethod#invoke方法执行流程如下:

(1) 调用CallAdapter#adapt方法将自定义的Call和OkHttpCall关联起来。

3.7 KotlinExtensions

suspend fun <T : Any> Call<T>.await(): T {return suspendCancellableCoroutine { continuation ->continuation.invokeOnCancellation {cancel()}// 1 会调用自定义的ApiResultCall的enqueue方法。enqueue(object : Callback<T> {override fun onResponse(call: Call<T>, response: Response<T>) {// 2 如果响应码是200,本例而言isSuccessful始终为true,见2.3.1分析。if (response.isSuccessful) {// 3 如果body为null则抛出KotlinNullPointerException异常,本例body不会为null。见2.3.1分析。val body = response.body()if (body == null) {val invocation = call.request().tag(Invocation::class.java)!!val method = invocation.method()val e = KotlinNullPointerException("Response from " +method.declaringClass.name +'.' +method.name +" was null but response body type was declared as non-null")continuation.resumeWithException(e)} else {// 4 返回值为ApiResultcontinuation.resume(body)}} else {// 5 抛出HttpException的异常。continuation.resumeWithException(HttpException(response))}}override fun onFailure(call: Call<T>, t: Throwable) {// 6 抛出异常。continuation.resumeWithException(t)}})}
}

由自定义的ApiResultCall调用enqueue函数,将请求交给OkHttpCall执行。执行完的结果会通过Callback的回调函数回调,并得到返回值

ApiResult或抛出异常。本例而言不会抛出异常。

3.8 OkHttpCall执行异步网络请求

  @Overridepublic void enqueue(final Callback<T> callback) {...okhttp3.Call call;Throwable failure;synchronized (this) {if (executed) throw new IllegalStateException("Already executed.");executed = true;call = rawCall;failure = creationFailure;if (call == null && failure == null) {try {// 1 构建okhttp3.Call实例。call = rawCall = createRawCall();} catch (Throwable t) {throwIfFatal(t);failure = creationFailure = t;}}}if (failure != null) {callback.onFailure(this, failure);return;}// 1 如果取消了,执行Call的cancel方法。if (canceled) {call.cancel();}// 2 okhttp3.Call实例的enqueue方法。这里就是OkHttp库的核心代码了。call.enqueue(new okhttp3.Callback() {@Overridepublic void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {Response<T> response;try {response = parseResponse(rawResponse);} catch (Throwable e) {throwIfFatal(e);callFailure(e);return;}try {// 3 会回调到自定义ApiResultCall的enqueue方法的delegate的Call的回调。见2.3.1分析callback.onResponse(OkHttpCall.this, response);} catch (Throwable t) {throwIfFatal(t);t.printStackTrace(); // TODO this is not great}}@Overridepublic void onFailure(okhttp3.Call call, IOException e) {callFailure(e);}private void callFailure(Throwable e) {try {// 4 会回调到自定义ApiResultCall的enqueue方法的delegate的Call的回调。见2.3.1分析callback.onFailure(OkHttpCall.this, e);} catch (Throwable t) {throwIfFatal(t);t.printStackTrace(); // TODO this is not great}}});}private okhttp3.Call createRawCall() throws IOException {okhttp3.Call call = callFactory.newCall(requestFactory.create(args));if (call == null) {throw new NullPointerException("Call.Factory returned null.");}return call;}
public class OkHttpClient implements Cloneable, Call.Factory, WebSocket.Factory {...@Override public Call newCall(Request request) {// 1 构建RealCall对象。return RealCall.newRealCall(this, request, false /* for web socket */);}...
}

被委托的OkHttpCall在执行异步网络请求过程中,先获取RealCall对象,然后调用enqueue函数执行网络请求。

3.9 小节

Retrofit加载ApiInterface定义的suspend的函数并调用的流程如下:

(1) Retrofit通过create方法构建接口实例。过程中会构建代理对象得到InvocationHandler,当通过接口实例调用对应的suspend函数时候会触发invoke方法。

(2) 加载方法得到ServiceMethod的实例。如果缓存中有已加载好的ServiceMethod实例则直接返回。否则先解析对应的方法,得到ServiceMethod实例后,缓存起来以便以后重复利用。

(3) 根据方法得到返回值类型。由Retrofit配置的CallAdapter.Factory构建CallAdapter实例。构建SuspendForBody的实例

(4) 调用CallAdapter#adapt方法将自定义的Call和OkHttpCall关联起来。

(5) 由自定义的ApiResultCall调用enqueue函数,将请求交给OkHttpCall执行。执行完的结果会通过Callback的回调,并得到返回值

ApiResult或抛出异常。本例而言不会抛出异常。

(6) 被委托的OkHttpCall在执行异步网络请求过程中,先获取RealCall对象,然后调用enqueue函数执行网络请求。

这样通过自定义CallAdapter.Factory、CallAdapter、Call,并给Retrofit配置自定义的CallAdapter.Factory,可实现调用apiInterface的suspend方法得到自定义的结果,避免了使用回调来处理异步。

4 使用

基于Retrofit网络库使用Kotlin协程进一步封装后,就可以使用了,使用起来也比较方便,比如可以在ViewModel中通过viewModelScope启动一个协程,在协程体中获取apiInterface的实例并调用对应的suspend方法,根据方法的返回值来更新ViewModel中的LiveData字段,然后通知View。

class LoginViewModel() : ViewModel() {...fun login2(account: String, pwd: String) {viewModelScope.launch {when (val userApiResult = HttpManager.service.login2(account, pwd)) {is ApiResult.Success -> {val user = userApiResult.data.dataLog.d(TAG, "login2: name: ${user.username}")}is ApiResult.Failure -> {Log.d(TAG,"login2: errorCode:${userApiResult.errorCode}, errorMsg:${userApiResult.errorMsg}")}}}}...
}

文中的代码详细见 https://github.com/aSmartTortoise/kotlinLearningPlan/tree/master/kotlinTutorials/CoroutineAndroid

这篇关于Coroutine + Retrofit 封装网络请求的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/325411

相关文章

Java实现任务管理器性能网络监控数据的方法详解

《Java实现任务管理器性能网络监控数据的方法详解》在现代操作系统中,任务管理器是一个非常重要的工具,用于监控和管理计算机的运行状态,包括CPU使用率、内存占用等,对于开发者和系统管理员来说,了解这些... 目录引言一、背景知识二、准备工作1. Maven依赖2. Gradle依赖三、代码实现四、代码详解五

SpringBoot中Get请求和POST请求接收参数示例详解

《SpringBoot中Get请求和POST请求接收参数示例详解》文章详细介绍了SpringBoot中Get请求和POST请求的参数接收方式,包括方法形参接收参数、实体类接收参数、HttpServle... 目录1、Get请求1.1 方法形参接收参数 这种方式一般适用参数比较少的情况,并且前后端参数名称必须

Linux 网络编程 --- 应用层

一、自定义协议和序列化反序列化 代码: 序列化反序列化实现网络版本计算器 二、HTTP协议 1、谈两个简单的预备知识 https://www.baidu.com/ --- 域名 --- 域名解析 --- IP地址 http的端口号为80端口,https的端口号为443 url为统一资源定位符。CSDNhttps://mp.csdn.net/mp_blog/creation/editor

ASIO网络调试助手之一:简介

多年前,写过几篇《Boost.Asio C++网络编程》的学习文章,一直没机会实践。最近项目中用到了Asio,于是抽空写了个网络调试助手。 开发环境: Win10 Qt5.12.6 + Asio(standalone) + spdlog 支持协议: UDP + TCP Client + TCP Server 独立的Asio(http://www.think-async.com)只包含了头文件,不依

poj 3181 网络流,建图。

题意: 农夫约翰为他的牛准备了F种食物和D种饮料。 每头牛都有各自喜欢的食物和饮料,而每种食物和饮料都只能分配给一头牛。 问最多能有多少头牛可以同时得到喜欢的食物和饮料。 解析: 由于要同时得到喜欢的食物和饮料,所以网络流建图的时候要把牛拆点了。 如下建图: s -> 食物 -> 牛1 -> 牛2 -> 饮料 -> t 所以分配一下点: s  =  0, 牛1= 1~

poj 3068 有流量限制的最小费用网络流

题意: m条有向边连接了n个仓库,每条边都有一定费用。 将两种危险品从0运到n-1,除了起点和终点外,危险品不能放在一起,也不能走相同的路径。 求最小的费用是多少。 解析: 抽象出一个源点s一个汇点t,源点与0相连,费用为0,容量为2。 汇点与n - 1相连,费用为0,容量为2。 每条边之间也相连,费用为每条边的费用,容量为1。 建图完毕之后,求一条流量为2的最小费用流就行了

poj 2112 网络流+二分

题意: k台挤奶机,c头牛,每台挤奶机可以挤m头牛。 现在给出每只牛到挤奶机的距离矩阵,求最小化牛的最大路程。 解析: 最大值最小化,最小值最大化,用二分来做。 先求出两点之间的最短距离。 然后二分匹配牛到挤奶机的最大路程,匹配中的判断是在这个最大路程下,是否牛的数量达到c只。 如何求牛的数量呢,用网络流来做。 从源点到牛引一条容量为1的边,然后挤奶机到汇点引一条容量为m的边

JavaSE——封装、继承和多态

1. 封装 1.1 概念      面向对象程序三大特性:封装、继承、多态 。而类和对象阶段,主要研究的就是封装特性。何为封装呢?简单来说就是套壳屏蔽细节 。     比如:对于电脑这样一个复杂的设备,提供给用户的就只是:开关机、通过键盘输入,显示器, USB 插孔等,让用户来和计算机进行交互,完成日常事务。但实际上:电脑真正工作的却是CPU 、显卡、内存等一些硬件元件。

配置InfiniBand (IB) 和 RDMA over Converged Ethernet (RoCE) 网络

配置InfiniBand (IB) 和 RDMA over Converged Ethernet (RoCE) 网络 服务器端配置 在服务器端,你需要确保安装了必要的驱动程序和软件包,并且正确配置了网络接口。 安装 OFED 首先,安装 Open Fabrics Enterprise Distribution (OFED),它包含了 InfiniBand 所需的驱动程序和库。 sudo

【机器学习】高斯网络的基本概念和应用领域

引言 高斯网络(Gaussian Network)通常指的是一个概率图模型,其中所有的随机变量(或节点)都遵循高斯分布 文章目录 引言一、高斯网络(Gaussian Network)1.1 高斯过程(Gaussian Process)1.2 高斯混合模型(Gaussian Mixture Model)1.3 应用1.4 总结 二、高斯网络的应用2.1 机器学习2.2 统计学2.3