Retrofit2.0源码解析

最近看了一下Retrofit2的源码,感觉并不是很难,但是对于其中的设计方式大为赞叹,怪不得会成为现在最流行的网络请求框架,Retrofit感觉就像是一个包装壳,我不干请求这种脏话,还是你okhttp干吧,毕竟你做得好,在内部来说我就是专门给你检查检查一些url接口的规范,或是是一些Request,你就专心干你的请求就行,对于外部使用者来说,我什么都给你封装好了,你要啥我就给你啥,你想要什么类型的结果数据我就给你什么样的数据,你不要管我干了什么,这种不管不问的方式,我还是挺喜欢的。


下面贴出Retrofit的基本使用,然后跟着这个基本结构去分析源码,这样,有利于去探索。

public interface APIServer {
    @GET("/")
    Call<ResponseBody> getText();
 }
      Retrofit rv = new Retrofit.Builder()
                .baseUrl("http://www.lontano.wang/")
                .build();

        APIServer apiServer = rv.create(APIServer.class);
        Call<ResponseBody> call = apiServer.getText();
        call.enqueue(new Callback<ResponseBody>() {
            @Override
            public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
                response.toString()
            }

            @Override
            public void onFailure(Call<ResponseBody> call, Throwable t) {

            }
        });

这是最基本的请求步骤了,接下来一步步的分析。

初始化

我们来看看初始化

Retrofit rv = new Retrofit.Builder()
                .baseUrl("http://www.lontano.wang/")
                .build();

先看看内部建造者Builder类的初始化,下面有一个Platform类,这个类是用来获取当前的使用平台,有默认值,java8平台,还有Android平台。最下面默认还添加了一个BuiltInConverters,这个是默认的覆盖结果的类,我们常常在请求返回结果类型的时候希望返回一个处理好的bean,所以,我们使用Retrofit的时候喜欢用GsonConverterFactory.create(),如果使用者没有添加(就像我上面的例子)的话就是用默认值,这个类覆盖结果的是一个String类型的结果,所有覆盖结果类都继承了Retrofit的Converter.Factory类,并对结果进行转换。

Builder(Platform platform) {
      this.platform = platform;
      //这个注释说的好,首先添加内置的转换器工厂。这可以防止覆盖其行为,但也可以确保使用所有类型的转换器时的正确行为
      // 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());
    }

    public Builder() {
      this(Platform.get());
    }

接下来看看baseUrl方法,这个方法主要用来检查这个链接是否正确,然后是区分是http还是https,然后并对这个链接进行再一次拼接。主要操作都在HttpUrl这个类里面

   public Builder baseUrl(String baseUrl) {
      checkNotNull(baseUrl, "baseUrl == null");
      HttpUrl httpUrl = HttpUrl.parse(baseUrl);
      if (httpUrl == null) {
        throw new IllegalArgumentException("Illegal URL: " + baseUrl);
      }
      return baseUrl(httpUrl);
    }

然后看build方法,这个方法主要就是对刚刚设置build参数做最后的总结,我们来看看这个callFactory,因为Retrofit把okHttp都封装好了,我们根本无法对他进行设置,比如,我要用session去请求,而且我还要设置请求头或是请求时间,但是我接触不到okHttp啊,所以,这个时候,我们可以通过Build的 public Builder client(OkHttpClient client) 将需要设置okHttp的设置带进来,下面的这个callFactory如果为null的话,就默认初始化一个,

 public Retrofit build() {
      if (baseUrl == null) {
        throw new IllegalStateException("Base URL required.");
      }
      //-------callFactory
      okhttp3.Call.Factory callFactory = this.callFactory;
      if (callFactory == null) {
        callFactory = new OkHttpClient();
      }
//is not used for addCallAdapterFactory custom method return types
      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> adapterFactories = new ArrayList<>(this.adapterFactories);
      adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));

    /*返回类型添加到集合中,这个地方比较常见的就是RxJava,可以通过build方法的  
        addCallAdapterFactory(RxJava2CallAdapterFactory.create())将返回类型进行转换
    */
      // Make a defensive copy of the converters.
      List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);

//返回给Retrofit类
      return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
          callbackExecutor, validateEagerly);
    }
  }

使用

好了,Build的过程结束了,这个返回的new Retrofit也无处看了,也都是刚才的值,设置到Retrofit的全局去了,恩,我们接下来直接看使用方法了,来看看

  APIServer apiServer = rv.create(APIServer.class);

这个类才是最核心的地方,我们点进去看看,这个类使用了动态代理,在原来调用的方法中,添加一些自己的处理,这个动态代理主要干了些什么呢?

public <T> T create(final Class<T> service) {
    Utils.validateServiceInterface(service);
    if (validateEagerly) {
      eagerlyValidateMethods(service);
    }
    return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
        new InvocationHandler() {
          private final Platform platform = Platform.get();

          @Override public 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);
            }
            //这个处理也不看,只有平台是java8的时候才有用
            if (platform.isDefaultMethod(method)) {
              return platform.invokeDefaultMethod(method, service, proxy, args);
            }
            //这里是重点
            ServiceMethod<Object, Object> serviceMethod =
                (ServiceMethod<Object, Object>) loadServiceMethod(method);
            OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
            return serviceMethod.callAdapter.adapt(okHttpCall);
          }
        });
  }

我们来探探上面注释“重点”的地方,我们一个一个单独拿出来说首先看ServiceMethod方法,这个方法主要功能就是通过注解解析调用方法一些请求信息,将这些信息缓存起来,下次调用的时候直接拿,这个方法调用了loadServiceMethod来返回一个ServiceMethod,我们来看看这个方法。

ServiceMethod<Object, Object> serviceMethod =
                (ServiceMethod<Object, Object>) loadServiceMethod(method);

首先是一个判断当前缓存是否有该方法,没有的话就去初始化,初始化类用的是ServiceMethod.Builder去构建的,并将Retrofit引用和方法信息传递过去。

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

    synchronized (serviceMethodCache) {
      result = serviceMethodCache.get(method);
      if (result == null) {
        result = new ServiceMethod.Builder<>(this, method).build();
        serviceMethodCache.put(method, result);
      }
    }
    return result;
  }

我们来看看ServiceMethod.Builder的build方法干了些什么,因为代码过长,我把主要的代码拎出来。

 /*首先创建一个CallAdapter,这个类主要检查一些注解的规范,比如方法的修饰不能为void,必须要有返回值,然后获取该方法的注解信息,
Retrofit继续调用 return nextCallAdapter(null, returnType, annotations)方法,
该方法通过遍历类型CallAdapter.Factory的集合获取对应的CallAdapter,对于这个CallAdapter我们有一个非常常见的使用,那就是
addCallAdapterFactory(RxJava2CallAdapterFactory.create()),
我们在使用RxJava来作为返回类型的时候,就是这样为Retrofit来添加一个返回类型的,所以,
此处如果开发者使用了RxJava类返回类型的话,那么这个地方所创建的返回类型就是RxJava返回的类型,
*/

     callAdapter = createCallAdapter();

    //此处去获取CallAdapter的响应类型,这个responseType还使用了检测,不能使用Response.class ,

    responseType = callAdapter.responseType();

    //获取方法的注解和返回类型,返回给Retrofit的  return retrofit.responseBodyConverter(responseType, annotations);
//然后返回return (Converter<ResponseBody, T>) converter;
    //这个地方比较常见的就是addConverterFactory(GsonConverterFactory.create())添加覆盖的返回类型为Gson

   responseConverter = createResponseConverter();

  //这个parseMethodAnnotation方法应该是大家最常用的了,这个方法是获取请求方法的注解,
  //Retrofit支持的请求有DELETE、GET、HEAD、PATCH、POST、PUT、OPTIONS等等,通过instanceof来判断当前的类型,
  //然后调用parseHttpMethodAndPath来检查注解的参数,并赋给全局,这个地方有个检查,当使用Multipart注解的时候,必须是PATCH、POST、PUT、OPTIONS来修饰,
  //如果使用GET请求的话,不能用他来修饰,和他一样的还是FormUrlEncoded,这两个在平时使用的时候,都是和POST一块使用
      for (Annotation annotation : methodAnnotations) {
        parseMethodAnnotation(annotation);
      }

     //
      for (int p = 0; p < parameterCount; p++) {
      //获取参数类型
        Type parameterType = parameterTypes[p];

    //获取参数的注解
     Annotation[] parameterAnnotations = parameterAnnotationsArray[p];
     //创建ParameterHandler对象来存放检查过后的参数和注解,这个方法中会对
     //Path、Query、QueryName、QueryMap、Header、HeaderMap、Field、FieldMap、Part、PartMap、Body来进行一些规范的检查
   parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
         }  


最后将当前的引用信息返回ServiceMethod类return new ServiceMethod<>(this);

好了,最关键的ServiceMethod终于结束了,果然是重中之中,然后我们返回到Retrofit的代理中继续接下来的操作。

//这个类封装了OkHttp的enqueue和execute,执行enqueue方法会调用okhttpenqueue方法,
//通过回调的方式,将okHttp请求的结果回调给Retrofit,然后回调给用户
            OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);

//返回callAdapter的类型,如果添加了RxJava2CallAdapterFactory,
//那么就返回RxJava类型,然后就可以通过subscribe订阅的方式实现链式调用
            return serviceMethod.callAdapter.adapt(okHttpCall);

至此,Retrofit的源码大致分析如此,最后来个整体的总结。

总结

1.通过Retrofit.Builder()构建baseUrl,类型的覆盖(addConverterFactory),请求结果转换(addCallAdapterFactory)
2. 通过create(APIServer.class)实现动态代理APIServer类的处理
3. apiServer.getText()动态代理方法的执行
4. 处理ServiceMethod,并对一些注解进行规范化检查,将结果缓存起来
5. 创建OkHttpCall通过okHttp去处理请求结果
6. serviceMethod.callAdapter.adapt返回结果类型

附录

最后附上compile库,以后添加的时候好用一些,RxAndroid、OkHttp,RxJava,Gson,RxBinding,Rxpermissions全家桶

    compile 'io.reactivex.rxjava2:rxjava:2.0.0'
    compile 'io.reactivex.rxjava2:rxandroid:2.0.0'
    compile 'org.reactivestreams:reactive-streams:1.0.0'
    compile 'com.squareup.okhttp3:okhttp:3.4.1'
    compile 'com.squareup.okio:okio:1.10.0'
    compile 'com.squareup.retrofit2:retrofit:2.1.0'
    compile 'com.jakewharton.retrofit:retrofit2-rxjava2-adapter:1.0.0'
    compile 'com.squareup.retrofit2:converter-gson:latest.release'

    compile 'com.jakewharton.rxbinding2:rxbinding:2.0.0'
    compile 'com.jakewharton.rxbinding2:rxbinding-support-v4:2.0.0'
    compile 'com.jakewharton.rxbinding2:rxbinding-appcompat-v7:2.0.0'
    compile 'com.jakewharton.rxbinding2:rxbinding-design:2.0.0'
    compile 'com.jakewharton.rxbinding2:rxbinding-recyclerview-v7:2.0.0'
    compile 'com.jakewharton.rxbinding2:rxbinding-leanback-v17:2.0.0'

    compile 'com.tbruyelle.rxpermissions2:rxpermissions:0.9.3@aar'
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在 Android 中使用 Retrofit 上传图片可以通过 `@Multipart` 和 `@Part` 注解实现。以下是 Retrofit 2.0 实现图文上传的方法总结: 1. 添加依赖库 在项目的 `build.gradle` 文件中添加以下依赖库: ```groovy implementation 'com.squareup.retrofit2:retrofit:2.9.0' implementation 'com.squareup.retrofit2:converter-gson:2.9.0' implementation 'com.squareup.retrofit2:converter-scalars:2.9.0' ``` 2. 创建 Retrofit 实例 ```java Retrofit retrofit = new Retrofit.Builder() .baseUrl(BASE_URL) .addConverterFactory(GsonConverterFactory.create()) .build(); ``` 3. 创建 API 接口 ```java public interface ApiService { @Multipart @POST("upload") Call<ResponseBody> upload( @Part("description") RequestBody description, @Part MultipartBody.Part file); } ``` 其中,`@Multipart` 注解表示这是一个包含文本和文件的表单;`@POST("upload")` 表示上传的 URL 地址;`@Part("description") RequestBody description` 表示上传的文本参数,`description` 是参数名,可以自己定义;`@Part MultipartBody.Part file` 表示上传的文件参数。 4. 创建请求参数 ```java File file = new File(filePath); RequestBody requestFile = RequestBody.create(MediaType.parse("image/*"), file); MultipartBody.Part body = MultipartBody.Part.createFormData("file", file.getName(), requestFile); RequestBody description = RequestBody.create(MediaType.parse("multipart/form-data"), "description"); ``` 5. 发送网络请求 ```java ApiService apiService = retrofit.create(ApiService.class); Call<ResponseBody> call = apiService.upload(description, body); call.enqueue(new Callback<ResponseBody>() { @Override public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) { // 成功上传后的处理 } @Override public void onFailure(Call<ResponseBody> call, Throwable t) { // 上传失败后的处理 } }); ``` 以上就是 Retrofit 2.0 实现图文上传的方法总结。注意,在 `AndroidManifest.xml` 文件中需要添加网络权限: ```xml <uses-permission android:name="android.permission.INTERNET" /> ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值