Retrofit 创建网络请求接口实例过程

作者:阿健叔 时间:2023-11-23 22:26:43 

Retrofit 基本使用

implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
interface NetApi {
   @GET("/hotkey/json")
   fun getHotKey(): Call<Response>
   companion object {
       private const val BASE_URL = "https://www.wanandroid.com/"
       fun createApi(): NetApi =
           Retrofit.Builder().baseUrl(BASE_URL).addConverterFactory(GsonConverterFactory.create())
               .build().create(NetApi::class.java)
   }
}
data class HotWords(
   val id: String,
   val name: String,
)
data class Response(
   val errorCode: Int,
   val errorMsg: String,
   val data: List<HotWords>
)
NetApi.createApi().getHotKey().enqueue(object : Callback<Response> {
   override fun onResponse(call: Call<Response>, response: retrofit2.Response<Response>) {
       Log.i(tag, "onResponse: ${response.body()?.data}")
   }
   override fun onFailure(call: Call<Response>, t: Throwable) {
       Log.i(tag, "onFailure: ${t.message}")
   }
})

这样一个基本的网络请求就搞定了,使用很简洁,正是因为其内部使用了大量的设计模式和优秀的架构设计,才得以使其如此方便地进行网络请求,下面来一起瞧瞧 Retrofit 的源码吧~

Retrofit构建过程

使用了建造者模式通过内部静态类 Builder 构建一个 Retrofit 实例,这里列出了部分方法,其他类似。

public static final class Builder {
   private final Platform platform;
   // 网络请求工厂,工厂方法模式
   private @Nullable okhttp3.Call.Factory callFactory;
   // 网络请求地址
   private @Nullable HttpUrl baseUrl;
   // 数据转换器工厂的集合
   private final List<Converter.Factory> converterFactories = new ArrayList<>();
   // 网络请求适配器工厂的集合,默认是 ExecutorCallAdapterFactory
   private final List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>();
   // 回调方法执行器,用于切换线程
   private @Nullable Executor callbackExecutor;
   // 一个开关,为 true 则会缓存创建的 ServiceMethod
   private boolean validateEagerly;
   ...
   public Builder baseUrl(String baseUrl) {
     Objects.requireNonNull(baseUrl, "baseUrl == null");
     return baseUrl(HttpUrl.get(baseUrl));
   }
   public Builder baseUrl(HttpUrl baseUrl) {
     Objects.requireNonNull(baseUrl, "baseUrl == null");
     List<String> pathSegments = baseUrl.pathSegments();
     if (!"".equals(pathSegments.get(pathSegments.size() - 1))) {
       throw new IllegalArgumentException("baseUrl must end in /: " + baseUrl);
     }
     this.baseUrl = baseUrl;
     return this;
   }
   // 将一个含有 Gson 对象实例的 GsonConverterFactory 放入数据转换器工厂
   public Builder addConverterFactory(Converter.Factory factory) {
     converterFactories.add(Objects.requireNonNull(factory, "factory == null"));
     return this;
   }
   ...
}

通过 build,我们上面 Builder 类中的参数对象都配置到了 Retrofit 对象中。

public Retrofit build() {
     if (baseUrl == null) {
       throw new IllegalStateException("Base URL required.");
     }
     okhttp3.Call.Factory callFactory = this.callFactory;
     if (callFactory == null) {
       callFactory = new OkHttpClient();
     }
     Executor callbackExecutor = this.callbackExecutor;
     if (callbackExecutor == null) {
       callbackExecutor = platform.defaultCallbackExecutor();
     }
     // Make a defensive copy of the adapters and add the default Call adapter.
     List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
     callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));
     // Make a defensive copy of the converters.
     List<Converter.Factory> converterFactories =
         new ArrayList<>(
             1 + this.converterFactories.size() + platform.defaultConverterFactoriesSize());
     // Add the built-in converter factory first. This prevents overriding its behavior but also
     // ensures correct behavior when using converters that consume all types.
     converterFactories.add(new BuiltInConverters());
     converterFactories.addAll(this.converterFactories);
     converterFactories.addAll(platform.defaultConverterFactories());
     return new Retrofit(
         callFactory,
         baseUrl,
         unmodifiableList(converterFactories),
         unmodifiableList(callAdapterFactories),
         callbackExecutor,
         validateEagerly);
   }

创建网络请求接口实例过程

使用 * 的方式拿到所有注解配置后,创建网络请求接口实例。

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

ServiceMethod<?> loadServiceMethod(Method method) {
   ServiceMethod<?> result = serviceMethodCache.get(method);
   if (result != null) return result;
   synchronized (serviceMethodCache) {
     result = serviceMethodCache.get(method);
     if (result == null) {
       result = ServiceMethod.parseAnnotations(this, method);
       serviceMethodCache.put(method, result);
     }
   }
   return result;
 }

parseAnnotations 解析注解配置得到 ServiceMethod,然后加入到 serviceMethodCache 缓存中,是一个 ConcurrentHashMap 。

abstract class ServiceMethod<T> {
 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);
 }
 abstract @Nullable T invoke(Object[] args);
}

通过 RequestFactory 的 parseAnnotations 方法,解析接口方法上的注解,然后封装在 RequestFactory 对象中,将其返回,这个 RequestFactory,主要用于后续创建 OkHttp 请求所需要的 Request 对象。那后面的 HttpServiceMethod.parseAnnotations 又是干什么的呢?往下看。

static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
   Retrofit retrofit, Method method, RequestFactory requestFactory) {
 ...
 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.
   return (HttpServiceMethod<ResponseT, ReturnT>)
       new SuspendForBody<>(
           requestFactory,
           callFactory,
           responseConverter,
           (CallAdapter<ResponseT, Call<ResponseT>>) callAdapter,
           continuationBodyNullable);
 }
}

如果不是 kotlin suspend 函数,使用 CallAdapted 类,如果是 kotlin suspend 函数返回类型是 Response,则使用 SuspendForResponse 类,其余情况使用 SuspendForBody,如 suspend 函数返回类型不是 Response 。一般情况下,我们使用的基本上是属于其余情况,我们来看下 SuspendForBody 类

static final class SuspendForBody<ResponseT> extends HttpServiceMethod<ResponseT, Object> {
 private final CallAdapter<ResponseT, Call<ResponseT>> callAdapter;
 private final boolean isNullable;
 ...
 @Override
 protected Object adapt(Call<ResponseT> call, Object[] args) {
   call = callAdapter.adapt(call);
   Continuation<ResponseT> continuation = (Continuation<ResponseT>) args[args.length - 1];
   try {
     return isNullable
         ? KotlinExtensions.awaitNullable(call, continuation)
         : KotlinExtensions.await(call, continuation);
   } catch (Exception e) {
     return KotlinExtensions.suspendAndThrow(e, continuation);
   }
 }
}

跟进 KotlinExtensions.awaitNullable,我们可以看到 SuspendForBody 会将 Response.body 作为协程挂起点的返回值。

suspend fun <T : Any> Call<T?>.await(): T? {
 return suspendCancellableCoroutine { continuation ->
   //协程取消是调用 cancel
   continuation.invokeOnCancellation {
     cancel()
   }
   enqueue(object : Callback<T?> {
     override fun onResponse(call: Call<T?>, response: Response<T?>) {
       if (response.isSuccessful) {
         //继续执行相应的协程,将 response.body 作为最后一个挂起点的返回值。
         continuation.resume(response.body())
       } else {
         continuation.resumeWithException(HttpException(response))
       }
     }
     override fun onFailure(call: Call<T?>, t: Throwable) {
       continuation.resumeWithException(t)
     }
   })
 }
}

执行请求过程

public void enqueue(final Callback<T> callback) {
   Objects.requireNonNull(callback, "callback == null");
   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 {
         // 创建 OkHttp 的 Call 对象
         call = rawCall = createRawCall();
       } catch (Throwable t) {
         throwIfFatal(t);
         failure = creationFailure = t;
       }
     }
   }
   if (failure != null) {
     callback.onFailure(this, failure);
     return;
   }
   if (canceled) {
     call.cancel();
   }
   call.enqueue(
       new okhttp3.Callback() {
         @Override
         public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
           Response<T> response;
           try {
             // 解析网络请求返回的数据
             response = parseResponse(rawResponse);
           } catch (Throwable e) {
             throwIfFatal(e);
             callFailure(e);
             return;
           }
           try {
             callback.onResponse(OkHttpCall.this, response);
           } catch (Throwable t) {
             throwIfFatal(t);
             t.printStackTrace(); // TODO this is not great
           }
         }
         @Override
         public void onFailure(okhttp3.Call call, IOException e) {
           callFailure(e);
         }
         private void callFailure(Throwable e) {
           try {
             callback.onFailure(OkHttpCall.this, e);
           } catch (Throwable t) {
             throwIfFatal(t);
             t.printStackTrace(); // TODO this is not great
           }
         }
       });
 }
Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
   ResponseBody rawBody = rawResponse.body();
   // Remove the body's source (the only stateful object) so we can pass the response along.
   rawResponse =
       rawResponse
           .newBuilder()
           .body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength()))
           .build();
   int code = rawResponse.code();
   // 根据响应返回的状态码进行处理
   if (code < 200 || code >= 300) {
     try {
       // Buffer the entire body to avoid future I/O.
       ResponseBody bufferedBody = Utils.buffer(rawBody);
       return Response.error(bufferedBody, rawResponse);
     } finally {
       rawBody.close();
     }
   }
   if (code == 204 || code == 205) {
     rawBody.close();
     return Response.success(null, rawResponse);
   }
   //包装 RequestBody
   ExceptionCatchingResponseBody catchingBody = new ExceptionCatchingResponseBody(rawBody);
   try {
     // 将响应体转为 Java 对象
     T body = responseConverter.convert(catchingBody);
     return Response.success(body, rawResponse);
   } catch (RuntimeException e) {
     // If the underlying source threw an exception, propagate that rather than indicating it was
     // a runtime exception.
     catchingBody.throwIfCaught();
     throw e;
   }
 }

来源:https://juejin.cn/post/7178010480672751653

标签:Retrofit,网络请求,接口
0
投稿

猜你喜欢

  • Flutter模仿实现微信底部导航栏流程详解

    2023-06-21 11:46:12
  • C# Random类随机函数实例详解

    2021-09-12 04:26:42
  • Java实现解析JSON大文件JsonReader工具详解

    2023-08-21 21:13:14
  • 详谈OnTouchListener与OnGestureListener的区别

    2023-11-13 14:21:24
  • Android recyclerview实现拖拽排序和侧滑删除

    2021-10-04 00:33:16
  • springcloud微服务之Eureka配置详解

    2021-06-15 14:35:28
  • Android viewpager 3D画廊的实现方法

    2021-05-28 21:19:27
  • Spring Security登录添加验证码的实现过程

    2021-07-25 23:47:17
  • JavaEE组件commons-fileupload实现文件上传、下载

    2022-11-13 13:54:20
  • JDBC实现学生管理系统

    2023-01-22 13:22:19
  • Android实现屏幕旋转方法总结

    2023-04-01 19:37:31
  • C# 类的声明详解

    2022-10-15 14:08:38
  • Java数组(Array)最全汇总(上篇)

    2022-12-19 23:22:30
  • Android客户端实现图片轮播控件

    2023-08-12 17:01:57
  • maven assembly打包生成Java应用启动脚本bat和sh的方法

    2023-09-16 03:06:54
  • Java如何设置系统参数和运行参数

    2023-12-19 10:47:01
  • Java 入门图形用户界面设计之单选按钮

    2023-08-29 13:40:02
  • Android Studio实现登录界面功能

    2023-06-11 20:27:35
  • C#中的HttpWebRequest类用法详解

    2022-11-07 11:41:17
  • linux环境下java程序打包成简单的hello world输出jar包示例

    2023-11-26 11:11:37
  • asp之家 软件编程 m.aspxhome.com