记一次简洁而优雅的网络请求分析--Retrofit

本文深入探讨Retrofit框架,基于OkHttp实现网络请求封装,简化代码,增强解耦。通过动态代理创建接口,解析注解生成请求,利用CallAdapter与Converter处理响应。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前面我们已经分析过了OkHttp网络请求框架的源码,今天继续聊聊一个新的框架,Retrofit。如果还没看过OkHttp框架的源码分析,点击这里(OkHttp源码解析)查看,有助于对今天内容的了解。

国际惯例,先了解下框架的功能和特点:Retrofit是Square公司基于OkHttp开发的对网络请求接口进行封装的开源框架。框架中采用大量的注解,很大程度上简化了网络请求的代码,而且解耦性很强,现广泛使用于移动端和Java后端。

那我们就从简单的使用开始把:

首先,就是添加依赖和添加网络权限:

implementation 'com.squareup.retrofit2:retrofit:2.7.2'


<uses-permission android:name="android.permission.INTERNET"></uses-permission>

接下来需要定义一个接口类:

public interface PixabayAPI {
    public static final String BASE_URL = "https://pixabay.com/api/";  //服务器的地址,必须以"/"结束

    @GET("?key=16902026-685*************01c&image_type=photo") //请求数据的接口,采用的是get的请求方式,参数直接拼接
    public Call<Pixabay> getPhotoItems(@Query("q") String param);  //因为参数是q=***直接拼接在请求url中,所以使用@Query注解
}

然后就开始使用retrofit请求了:

Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(PixabayAPI.BASE_URL)
                .addConverterFactory(GsonConverterFactory.create())
                .build();

 PixabayAPI api = retrofit.create(PixabayAPI.class);
 final Call<Pixabay> photos= api.getPhotoItems("cat");//创建一个Retrofit的网络请求,网络请求返回的值会封装成泛型里面的数据类型

        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Response<Pixabay> response = photos.execute();//同步请求,内部是OkHttpCall调用okhttp对象执行的execute();
                    Log.e("retrofit_response", response.body().toString());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }).start();

builder模式我们看过很多了吧,前两天刚介绍的OkHttp采用的也是builder模式。创建实例的方法也一样有两种,可以直接通过构造函数new出来,或者通过build方法构建出来,这里使用第二种方式,我们看下build方法里面有那些好料。

public Retrofit build() {
      if (baseUrl == null) {
        throw new IllegalStateException("Base URL required."); //强制要求baseurl不能为空
      }

      okhttp3.Call.Factory callFactory = this.callFactory;
      if (callFactory == null) {
        callFactory = new OkHttpClient(); //如果没有自定义设置callFactory,就使用默认的OkHttpClient
      }

      Executor callbackExecutor = this.callbackExecutor;
      if (callbackExecutor == null) {
        callbackExecutor = platform.defaultCallbackExecutor();//如果没有手动设置callbackExecutor,使用默认的平台返回的callbackExecutor,在Android 里面就是一个MainThreadExecutor,线程切换最终还是得靠handler来处理
      }

      ……

      return new Retrofit(callFactory, baseUrl, unmodifiableList(converterFactories),
          unmodifiableList(callAdapterFactories), callbackExecutor, validateEagerly);//最后还是通过new一个客户端进行返回
    }

所以,从代码中可以看到,build方法中做了一些初始化操作,最后还是通过new的方式创建一个实例返回;

然后看看创建接口实例的代码:

PixabayAPI api = retrofit.create(PixabayAPI.class);  //通过retrofit的动态代理,创建请求接口对象

这里开始要进入源码研究了,看着好像就一句代码,创建一个实例,轻轻松松完成,那么内部为我们实现了哪些操作呢?

@SuppressWarnings("unchecked") // Single-interface proxy creation guarded by parameter safety.
  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();//在Android设备中最终 return new Android();   MainThreadExecutor中用handler切换线程
          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) {//如果是Object类里的方法,直接使用
              return method.invoke(this, args);
            }
            if (platform.isDefaultMethod(method)) { //如果是平台封装好的方法,直接返回平台方法
              return platform.invokeDefaultMethod(method, service, proxy, args);
            }
            return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);//一般我们定义的接口类,都是走的这里,所以需要分析下loadServiceMethod方法
          }
        });
  }

ServiceMethod<?> loadServiceMethod(Method method) {
    ServiceMethod<?> result = serviceMethodCache.get(method);//ServiceMethod作用是解析method上的各种注解、参数类型、返回类型,并能根据这些生成相应的CallAdapter、Converter等
    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;
  }


# HttpServiceMethod.class

@Override final @Nullable ReturnT invoke(Object[] args) {
    Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
    return adapt(call, args);
  }

在create方法中,创建了一个请求接口的动态代理对象,当我们调用该对象中的某个方法时,会代理到invoke方法,然后会通过Method对象创建出ServiceMethod对象(loadServiceMethod),用于解析method上的各种注解、参数类型、返回值类型。然后通过ServiceMethod对象的invoke方法会构建一个OkHttpCall对象,所以真正的网络请求是通过OkHttpCall内部使用OkHttp请求实现,等请求返回结果后,再调用ServiceMethod中,根据方法返回类型生成的CallAdapter的adapt方法,将这个原始的OkHttpCall对象包装成需要的类型返回,看过源码可以知道,异步请求中的线程切换最终也是交给handler来完成,至此完成一次网络请求。

Retrofit的网络请求也一样分为同步和异步两种方式,在这里,都是通过OkHttpCall去安排执行的。

我们前面提到,请求是经过ServiceMethod创建出一个OkHttpCall对象,那具体是怎么实现的呢,我们一起去看看:从loadServiceMethod方法进来 => result = ServiceMethod.parseAnnotations(this, method); =>RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);那么,这里我们就要分析RequestFactory这个类了,

final class RequestFactory {
  static RequestFactory parseAnnotations(Retrofit retrofit, Method method) {
    return new Builder(retrofit, method).build();
  }

  private final Method method;//方法名称
  private final HttpUrl baseUrl;//网络接口地址
  final String httpMethod;//请求方法
  private final @Nullable String relativeUrl;//请求接口的后缀地址
  private final @Nullable Headers headers;//请求头
  private final @Nullable MediaType contentType;
  private final boolean hasBody;//是否有请求体
  private final boolean isFormEncoded;//是否默认转码
  private final boolean isMultipart;//是否Multipart文件上传
  private final ParameterHandler<?>[] parameterHandlers;//请求的参数
  final boolean isKotlinSuspendFunction;//是否kotlin协程中的挂起函数


 RequestFactory build() {
      for (Annotation annotation : methodAnnotations) { //循环解析各个注解
        parseMethodAnnotation(annotation);
      }

      if (httpMethod == null) {
        throw methodError(method, "HTTP method annotation is required (e.g., @GET, @POST, etc.).");
      }
……
    }

private void parseMethodAnnotation(Annotation annotation) {  // 解析注解,同时设置请求request的一些参数
      if (annotation instanceof DELETE) {
        parseHttpMethodAndPath("DELETE", ((DELETE) annotation).value(), false);
      } else if (annotation instanceof GET) {
        parseHttpMethodAndPath("GET", ((GET) annotation).value(), false);
      } else if……
    }

……

private void parseHttpMethodAndPath(String httpMethod, String value, boolean hasBody) {
      ……

      // Get the relative URL path and existing query string, if present.
      int question = value.indexOf('?');
      if (question != -1 && question < value.length() - 1) {
        // Ensure the query string does not have any named parameters.
        String queryParams = value.substring(question + 1);
        Matcher queryParamMatcher = PARAM_URL_REGEX.matcher(queryParams);
        if (queryParamMatcher.find()) {
          throw methodError(method, "URL query string \"%s\" must not have replace block. "
              + "For dynamic query parameters use @Query.", queryParams);
        }
      }

      this.relativeUrl = value;
      this.relativeUrlParamNames = parsePathParameters(value);  //解析请求参数
    }


}

上述步骤执行的内容就是解析各种注解,然后获取到各种请求参数到当前的Factory,然后等网络请求的时候,由工厂生产Request。然后继续看刚才的 result = ServiceMethod.parseAnnotations(this, method);方法执行到最后:

return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);

直接看关键点

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);
    }
……
}

所以后面执行网络请求,还是走的okhttp 中的Call(也就是RealCall),具体的实现,在前面的okhttp解析中有讲解过。(

PixabayAPI api = retrofit.create(PixabayAPI.class);

final Call<Pixabay> allContributors = api.getPhotoItems("cat");   //走到这里,就是创建了一个Call  对应okhttp中的call

然后网络请求成功后,数据回传,就得经过Retrofit的解析了,同样还是OkHttpCall来执行:

  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);
    }

    ExceptionCatchingResponseBody catchingBody = new ExceptionCatchingResponseBody(rawBody);
    try {
      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;
    }
  }

其实retrofit并没有网络请求功能,一系列的网络请求和数据解析都是通过各种转换器去实现的,现在流行的组合是:

RxJava + okhttp + Retrofit  ,有机会分析分析为啥选这个组合,今天的Retrofit源码先这样粗略过一遍,回头结合具体的场景做一些分析吧。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值