Android网络知识点全总结(4-Retrofit)

4 篇文章 0 订阅
4 篇文章 0 订阅
Retrofit是Square公司的网络请求库,它通过注解将网络请求接口与实现解耦,简化了Android应用的网络访问。本文详细介绍了Retrofit的工作原理、使用步骤、注解类型,以及其实现动态代理、建造者模式和适配器模式的设计思想,帮助开发者深入理解并高效使用Retrofit。
摘要由CSDN通过智能技术生成

Retrofit是squareup公司的开源力作,和同属squareup公司开源的OkHttp,一个负责网络调度,一个负责网络执行,为Android开发者提供了即方便又高效的网络访问框架。

不过,对于Retrofit这样设计精妙、代码简洁、使用方便的优秀开源项目,不能仅知道如何扩展和使用,或者仅研究它采用的技术或模式,“技”当然重要,但不能忽视了背后的“道”。

对于Retrofit,我们还应该看到的,是她在优化App架构方面的努力,以及她在提升开发效率方面的借鉴和启示。

Retrofit 是一个 RESTful 的 HTTP 网络请求框架的封装,网络请求的工作本质上是 OkHttp 完成,而 Retrofit 仅负责 网络请求接口的封装

注意哦,Retrofit并不做网络请求,只是生成一个能做网络请求的对象。
Retrofit的作用是按照接口去定制Call网络工作对象

什么意思?就是说:
Retrofit不直接做网络请求
Retrofit不直接做网络请求
Retrofit不直接做网络请求

重要的事情说三遍。

使用步骤

1.添加Retrofit库的依赖:

implementation 'com.squareup.retrofit2:retrofit:2.0.2'
 implementation 'com.squareup.retrofit2:converter-gson:2.0.2'
 implementation 'com.google.code.gson:gson:2.8.5'
implementation 'com.squareup.retrofit2:adapter-rxjava:2.0.2'

后面三个是可选的,分别是数据解析器和gson,以及rxjava支持的依赖

2.创建 用于描述网络请求 的接口
Retrofit将 Http请求 抽象成 Java接口:采用 注解 描述网络请求参数 和配置网络请求参数

public interface GetRequest_Interface {

    @GET("openapi.do?keyfrom=abc&key=2032414398&type=data&doctype=json&version=1.1&q=car")
    Call<Reception> getCall(@Field("name") String name);
    // @GET注解的作用:采用Get方法发送网络请求
 
    // getCall() = 接收网络请求数据的方法
    // 其中返回类型为Call<*>,*是接收数据的类(即上面定义的Translation类)
    // 如果想直接获得Responsebody中的内容,可以定义网络请求返回值为Call<ResponseBody>
}

3.创建Retrofit实例

Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("http://fanyi.youdao.com/") //设置网络请求的Url地址
                .addConverterFactory(GsonConverterFactory.create()) //设置数据解析器
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .build();

4.发送请求
请求分为同步请求和异步请求

        // 创建 网络请求接口 的实例
        GetRequest_Interface request = retrofit.create(GetRequest_Interface.class);
        //对 发送请求 进行封装
        Call<Reception> call = request.getCall("");
        call.enqueue(new Callback<Reception>() {
            //请求成功时回调
            @Override
            public void onResponse(Call<Reception> call, Response<Reception> response) {
                //请求处理,输出结果
                response.body().show();
            }
            //请求失败时候的回调
            @Override
            public void onFailure(Call<Reception> call, Throwable throwable) {
                System.out.println("连接失败");
            }
        });

          //同步请求
        try {
            Response<Reception> response = call.execute();
            response.body().show();
        } catch (IOException e) {
            e.printStackTrace();
        }

response.body()就是Reception对象,网络请求的完整 Url =在创建Retrofit实例时通过.baseUrl()设置 +网络请求接口的注解设置(下面称 “path“ )
整合的规则如下:

注解

上面我们用了@GET注解来发送Get请求,Retrofit还提供了很多其他的注解类型

第一类:网络请求方法

1.@GET、@POST、@PUT、@DELETE、@HEAD分别对应 HTTP中的网络请求方式
2.@HTTP替换@GET、@POST、@PUT、@DELETE、@HEAD注解的作用 及 更多功能拓展
具体使用:通过属性method、path、hasBody进行设置

public interface ApiService{
    /**
     * method:网络请求的方法(区分大小写)
     * path:网络请求地址路径
     * hasBody:是否有请求体
     */
    @HTTP(method = "GET", path = "blog/{id}", hasBody = false)
    Call<ResponseBody> getCall(@Path("id") int id);
    // {id} 表示是一个变量
    // method 的值 retrofit 不会做处理,所以要自行保证准确
}

第二类:标记

1.@FormUrlEncoded
表示发送form-encoded的数据,每个键值对需要用@Filed来注解键名,随后的对象需要提供值。
2.@Multipart
表示发送form-encoded的数据(适用于 有文件 上传的场景),每个键值对需要用@Part来注解键名,随后的对象需要提供值。

public interface ApiService{
        /**
         *表明是一个表单格式的请求(Content-Type:application/x-www-form-urlencoded)
         * <code>Field("username")</code> 表示将后面的 <code>String name</code> 中name的取值作为 username 的值
         */
        @POST("/form")
        @FormUrlEncoded
        Call<ResponseBody> testFormUrlEncoded1(@Field("username") String name, @Field("age") int age);
         
        /**
         * {@link Part} 后面支持三种类型,{@link RequestBody}、{@link okhttp3.MultipartBody.Part} 、任意类型
         * 除 {@link okhttp3.MultipartBody.Part} 以外,其它类型都必须带上表单字段({@link okhttp3.MultipartBody.Part} 中已经包含了表单字段的信息),
         */
        @POST("/form")
        @Multipart
        Call<ResponseBody> testFileUpload1(@Part("name") RequestBody name, @Part("age") RequestBody age, @Part MultipartBody.Part file);

}

 第三类:网络请求参数

1.@Header & @Headers
添加请求头 &添加不固定的请求头

 

// @Header
@GET("user")
Call<User> getUser(@Header("Authorization") String authorization)

// @Headers
@Headers("Authorization: authorization")
@GET("user")
Call<User> getUser()

// 以上的效果是一致的。
// 区别在于使用场景和使用方式
// 1. 使用场景:@Header用于添加不固定的请求头,@Headers用于添加固定的请求头
// 2. 使用方式:@Header作用于方法的参数;@Headers作用于方法

 

2.@Body
以 Post方式 传递 自定义数据类型 给服务器,如果提交的是一个Map,那么作用相当于 @Field,不过Map要经过 FormBody.Builder 类处理成为符合 Okhttp 格式的表单,如:

FormBody.Builder builder = new FormBody.Builder();
builder.add("key","value");

3.@Field & @FieldMap
发送 Post请求 时提交请求的表单字段,与 @FormUrlEncoded 注解配合使用

public interface ApiService{
        /**
         *表明是一个表单格式的请求(Content-Type:application/x-www-form-urlencoded)
         * <code>Field("username")</code> 表示将后面的 <code>String name</code> 中name的取值作为 username 的值
         */
        @POST("/form")
        @FormUrlEncoded
        Call<ResponseBody> testFormUrlEncoded1(@Field("username") String name, @Field("age") int age);

/**
         * Map的key作为表单的键
         */
        @POST("/form")
        @FormUrlEncoded
        Call<ResponseBody> testFormUrlEncoded2(@FieldMap Map<String, Object> map);

}

 

4.@Part & @PartMap
发送 Post请求 时提交请求的表单字段,与@Field的区别:功能相同,但携带的参数类型更加丰富,包括数据流,所以适用于 有文件上传 的场景,与 @Multipart 注解配合使用

public interface ApiService{

          /**
         * {@link Part} 后面支持三种类型,{@link RequestBody}、{@link okhttp3.MultipartBody.Part} 、任意类型
         * 除 {@link okhttp3.MultipartBody.Part} 以外,其它类型都必须带上表单字段({@link okhttp3.MultipartBody.Part} 中已经包含了表单字段的信息),
         */
        @POST("/form")
        @Multipart
        Call<ResponseBody> testFileUpload1(@Part("name") RequestBody name, @Part("age") RequestBody age, @Part MultipartBody.Part file);

        /**
         * PartMap 注解支持一个Map作为参数,支持 {@link RequestBody } 类型,
         * 如果有其它的类型,会被{@link retrofit2.Converter}转换,如后面会介绍的 使用{@link com.google.gson.Gson} 的 {@link retrofit2.converter.gson.GsonRequestBodyConverter}
         * 所以{@link MultipartBody.Part} 就不适用了,所以文件只能用<b> @Part MultipartBody.Part </b>
         */
        @POST("/form")
        @Multipart
        Call<ResponseBody> testFileUpload2(@PartMap Map<String, RequestBody> args, @Part MultipartBody.Part file);

        @POST("/form")
        @Multipart
        Call<ResponseBody> testFileUpload3(@PartMap Map<String, RequestBody> args);
}

5.@Query和@QueryMap
用于 @GET 方法的查询参数(Query = Url 中 ‘?’ 后面的 key-value)
如:url = http://www.println.net/?cate=android,其中,Query = cate
配置时只需要在接口方法中增加一个参数即可:

   @GET("/")    
   Call<String> cate(@Query("cate") String cate);
}

// 其使用方式同 @Field与@FieldMap,这里不作过多描述

6.@Path
URL地址的缺省值

public interface ApiService{

        @GET("users/{user}/repos")
        Call<ResponseBody>  getBlog(@Path("user") String user );
        // 访问的API是:https://api.github.com/users/{user}/repos
        // 在发起请求时, {user} 会被替换为方法的第一个参数 user(被@Path注解作用)
    }

7.@Url
直接传入一个请求的 URL变量 用于URL设置

public interface ApiService{

        @GET
        Call<ResponseBody> testUrlAndQuery(@Url String url, @Query("showAll") boolean showAll);
       // 当有URL注解时,@GET传入的URL就可以省略
       // 当GET、POST...HTTP等方法中没有设置Url时,则必须使用 {@link Url}提供

}

网络请求的目标虽然是数据,但是我们需要为这个数据写大量的配套代码,发起请求的对象Call,接收数据的对象CallBack,做数据转换的对象Converter,以及检查和处理异常的对象等。
这对于一个项目的开发、扩展和维护来说,都是成本和风险。

而Retrofit做的事情,就是为开发者节省这部分的工作量,Retrofit一方面从底层统一用OkHttp去做网络处理;另一方面在外层灵活提供能直接融入业务逻辑的Call网络访问对象。

具体来说,Retrofit只负责生产对象,生产能做网络请求的工作对象,他有点像一个工厂,只提供产品,工厂本身不处理网络请求,产品才能处理网络请求。
Retrofit在网络请求中的作用大概可以这样理解:

Retrofit实现原理

Retrofit固然设计精妙,代码简洁,使用方便,但相应的,我们要理解Retrofit的实现原理也不太容易,这么精妙的设计是极佳的研究素材,我们不能仅仅停留在知道怎么使用,怎么扩展的阶段,那实在是对这个优秀开源项目的浪费。
其实,Retrofit使用的,就是动态代理,方法注解、建造者和适配器等成熟的技术或模式,但是由于她的设计紧凑,而且动态代理屏蔽了很多过程上的细节,所以比较难以理解。

Retrofit实现原理——从动态代理开始

从前面的使用场景可知,retrofit会生成一个接口实例。

//用retrofit加工出对应的接口实例对象
ApiService apiService= retrofit.create(ApiService.class);

 到Retrofit源码里看create函数,是一个动态代理。

 public <T> T create(final Class<T> service) {
    ...
    return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
        new InvocationHandler() {
            ...
            ServiceMethod<Object, Object> serviceMethod =
                (ServiceMethod<Object, Object>) loadServiceMethod(method);
            OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
            return serviceMethod.callAdapter.adapt(okHttpCall);
          }
        });
  }

 

   @GET("/")    
   Call<String> cate(@Query("cate") String cate);
}

// 其使用方式同 @Field与@FieldMap,这里不作过多描述

这个Call网络工作对象是在InvocationHandler中实现的,也就是在Retrofit.create函数中,由InvocationHandler实现的。

这样我们就明白了,Retrofit使用动态代理,其实是为了开发者在写代码时方便调用,而真正负责生产Call网络工作对象的,还是Retrofit.create函数中定义的这个InvocationHandler,这个InvocationHandler的代码我们再贴一遍:

  new InvocationHandler() {
            ...
            ServiceMethod<Object, Object> serviceMethod =
                (ServiceMethod<Object, Object>) loadServiceMethod(method);
            OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
            return serviceMethod.callAdapter.adapt(okHttpCall);
          }

ServiceMethod能让我们准确解析到INetApiService中定义的函数,为最后的适配转换提供转换目标,详细分析我们后面再说,先看适配转换的过程。

我们看到,Retrofit内部默认使用OkHttpCall对象去处理网络请求,但是返回的网络工作对象是经过适配器转换的,转换成接口定义的那种Call网络工作对象。

这个适配转换,就是Retrofit能按照接口去定制Call网络工作对象的秘密。

Retrofit实现原理——适配转换Call对象

我们在初始化Retrofit对象时,好像不添加CallAdapterFactory也能实现适配转换。

//retrofit对象
Retrofit retrofit=new Retrofit.Builder()
.baseUrl(Config.DOMAIN)
.addConverterFactory(GsonConverterFactory.create())
//可以不添加CallAdapterFactory
.build();

这是怎么回事呢,我们知道Retrofit使用了建造者模式,建造者模式的特定就是实现了建造和使用的分离,所以建造者模式的建造函数里,一般会有很复杂的对象创建和初始化过程,所以我们要看一下Retrofit的build函数。

public Retrofit build() {
      ...
      okhttp3.Call.Factory callFactory = this.callFactory;
      if (callFactory == null) {
        callFactory = new OkHttpClient();//使用OkHttpClient处理网络请求
      }
      ...
      //根据当前运行平台,设置默认的callAdapterFactory
      adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));
      ...
      return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
          callbackExecutor, validateEagerly);
    }

这段代码里,我们看到Retrofit使用OkHttpClient处理网络请求,并且会添加默认的callAdapterFactory,这个platform是一个简单工厂,能根据当前系统平台去生成对应的callAdapterFactory

  private static Platform findPlatform() {
    try {
      Class.forName("android.os.Build");
      if (Build.VERSION.SDK_INT != 0) {
        return new Android();//根据当前系统平台返回相应的对象
      }
    ...
  }
  ...
  static class Android extends Platform {
    ...
    @Override CallAdapter.Factory defaultCallAdapterFactory(@Nullable Executor callbackExecutor) {
      if (callbackExecutor == null) throw new AssertionError();
      return new ExecutorCallAdapterFactory(callbackExecutor);
    }
    ...
  }

这个Platform是Retrofit在Builder的构造函数里初始化的。

所以,在Retrofit.build()函数中,我们为Retrofit默认添加的callAdapterFactory,是在Platform中为Android系统设定的ExecutorCallAdapterFactory。
我们看ExecutorCallAdapterFactory的代码,这是一个工厂类,可以返回CallAdapter对象:

  @Override
  public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
    ...
    return new CallAdapter<Object, Call<?>>() {
      ...
      //               转换后              转换前,也就是OkHttpCall
      @Override public Call<Object> adapt(Call<Object> call) {
        return new ExecutorCallbackCall<>(callbackExecutor, call);
      }
    };
  }

在adapt函数中,适配器会把Retrofit中用来访问网络的OkHttpCall,转换为一个ExecutorCallbackCall(继承了INetApiService接口里要求返回的网络工作对象retrofit2.Call),
这个例子里面,由于OkHttpCall和ExecutorCallbackCall都实现了retrofit2.Call接口,结果出现了从Call<Object>转换为Call<Object>的情况,这可能不容易理解,我们换个RxJava2CallAdapterFactory来看看

  //RxJava2CallAdapterFactory中
  @Override
  public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
    ...
    return new RxJava2CallAdapter(responseType, scheduler, isAsync, isResult, isBody, isFlowable,
        isSingle, isMaybe, false);
}
  //RxJava2CallAdapter中
  //               转换后        转换前,也就是OkHttpCall
  @Override public Object adapt(Call<R> call) {
   ...
   Observable<?> observable;
   ...
   return observable;
  }

这个CallAdapter的转换就比较明显了,把retrofit2.Call对象通过适配器转换为了一个实为Observable<?>的Object对象。

至此,我们可以理解Retrofit根据接口定义动态生产Call网络请求工作对象的原理了,其实就是通过适配器把retrofit2.Call对象转换为目标对象。

至于适配器转换过程中,如何实现的对象转换,就可以根据需求来自由实现了,比如利用静态代理等,如有必要,我们可以自行开发扩展,Retrofit框架并不限制我们对于适配器的实现方式。

Retrofit实现原理——函数解析、网络请求和数据转换

在前面分析中,我们知道了Retrofit的整体工作流程,就是Retrofit用动态代理生成Call网络请求对象,在这个过程中,用适配器把Retrofit底层的retrofit2.Call对象转换为ApiService中定义的Call网络请求对象(如Flowable)。

问题是,Retrofit具体是如何知道了ApiService中定义的Call网络请求对象,如何实现网络请求,以及如何执行的数据转换呢?

具体过程如下;
首先,根据ApiService中定义的函数,解析函数,得到函数的具体定义,并生成对应的ServiceMethod。
然后,根据这个ServiceMethod,实现一个OkHttpCall的Call对象,负责在Retrofit底层实现网络访问。
其中,在网络访问返回了网络数据时,根据ServiceMethod实现数据转换。
最后,利用上一小节中匹配的适配器,把OkHttpCall对象转换为ApiService要求的Call网络请求对象。

所以,我们要了解的就是函数解析、网络请求和数据转换这三个动作,至于最后的适配转换,在上一节中已经分析过了。

1. 函数解析
在接口函数里,用注解描述了输入参数,用Java对象定义了返回值类型,所以对输入参数和返回值,ServiceMethod采取了不同的方式去处理。
输入参数
输入参数是用来描述url的,它的处理相对简单,ServiceMethod会根据反射得到的Method,取得Annotation注解信息,这些注解是Retrofit自己预定义好的(retrofit2.http.*),ServiceMethod根据预先的定义,直接判断注解所属的逻辑分支,在有网络请求时分情况进行处理,就能得到目标url,http请求头等数据。
返回值
返回值是需要用CallAdapter去适配的,所以核心在于生成对应的CallAdapter。
在Retrofit生成Call网络工作对象时,她通过动态代理获取到了接口函数的Method定义,从这个Method中可以获取函数定义的返回对象类型,由于这个转换是需要CallAdapterFactory生产CallAdapter对象去实现,而Retrofit事先并不知道要使用哪个Factory,所以她是遍历所有的CallAdapterFactory,根据目标函数的返回值类型,让每个Factory都去尝试生产一个CallAdapter,哪个成功就用哪个。

2. 网络请求
OkHttpCall继承的retrofit2.Call接口是为了依赖倒置解耦的,真正的网络请求是由OkHttpCall内部引用的okhttp3.call处理的,这个okhttp3.call是
借道ServiceMethod获取的Retrofit中的callFactory,也就是Retrofit中的OkHttpClient。

整个引用链条是这样的:
OkHttpCall--okhttp3.call
-->
ServiceMethod--callFactory
-->
Retrofit.build()--callFactory//(如未扩展赋值)new OkHttpClient();
-->
Retrofit.Builder().client(mClient)//(可能有扩展赋值)扩展过的OkHttpClient

最终的网络请求是由OkHttpCall调用OkHttpClient发出的,调用和回调等过程,也就是在OkHttpCall中处理的。

网络请求的生成过程中,为了使用接口函数中定义的参数,OkHttpCall会调用ServiceMethod来生成Request请求对象,再交给OkHttpCall去处理。

3. 数据转换
因为回调是在OkHttpCall中处理的,所以对回调数据的转换也在OkHttpCall中触发,为了符合接口函数中定义的返回数据类型,OkHttpCall会调用ServiceMethod来转换Response返回数据对象。

OkHttpCall对返回的网络数据,会调用一个serviceMethod.toResponse(ResponseBody body)函数,函数中执行的是:

  R toResponse(ResponseBody body) throws IOException {
    return responseConverter.convert(body);
  }

这个函数可以把原始的okhttp3. ResponseBody数据转换为ApiService接口中要求的数据类型(如BizEntity类型)。
从代码可以看出,实现数据转换的核心对象其实是responseConverter,这个Converter实际上要依次经过Retrofit的建造和ServiceMethod的建造后,才能确定下来的。

Retrofit建造时添加数据转换工厂
Retrofit里有converterFactries列表,这是在我们初始化Retrofit实例时添加的

//retrofit对象
Retrofit retrofit=new Retrofit.Builder()
.baseUrl(Config.DOMAIN)
.addConverterFactory(GsonConverterFactory.create())
.addConverterFactory(YourConverterFactory.create())//添加自定义Converter
.build();

ServiceMethod建造时设定数据转换器
ServiceMethod在建造时,就已经确定了对应的是ApiService中的哪个函数,所以需要明确设定自己的Converter<R,T>转换对象

  public ServiceMethod build() {
      ...
      responseConverter = createResponseConverter();
      ...
  }

这需要调用Retrofit

    private Converter<ResponseBody, T> createResponseConverter() {
      ...
      retrofit.responseBodyConverter(responseType, annotations);
    }

Retrofit会在自己的转换器工厂列表中遍历每个ConverterFactory,尝试根据ServiceMethod所对应的目标数据类型,找到Converter数据转换类

    for (int i = start, count = converterFactories.size(); i < count; i++) {
      Converter<ResponseBody, ?> converter =
          converterFactories.get(i).responseBodyConverter(type, annotations, this);
      if (converter != null) {
        //noinspection unchecked
        return (Converter<ResponseBody, T>) converter;
      }
    }

以Gson转换为例,GsonConverterFactory会通过getAdapter来尝试匹配目标数据类型:

public <T> TypeAdapter<T> getAdapter(TypeToken<T> type) {...}

如果可以匹配,那么前面调用serviceMethod.toResponse(ResponseBody body)函数时,会调用

  R toResponse(ResponseBody body) throws IOException {
    return responseConverter.convert(body);
  }

在调用这段代码时,其实就是调用了Gson中最终执行数据转换的代码:

  @Override public T convert(ResponseBody value) throws IOException {
    JsonReader jsonReader = gson.newJsonReader(value.charStream());
    try {
      return adapter.read(jsonReader);
    } finally {
      value.close();
    }
  }

总结来说,Retrofit在类的单一职责方面分隔的很好,OkHttpCall类只负责网络交互,凡是需要知道函数定义的,都交给ServiceMethod类去处理,而ServiceMethod类对使用者不公开,因为Retrofit是个外观模式,而所有需要扩展的都在Retrofit的建造者中实现,他们的分工大概是这样的:

这三个类分工合作,共同实现了函数解析、网络访问和数据转换,并保留了良好的可扩展性。

Retrofit实现原理——整体结构与分工实现

至此,Retrofit的实现细节就已经基本清楚了,他用动态代理去定制接口定义的Call网络工作对象,用适配器去把底层的Call对象转换为目标Call对象,用函数解析/OkHttpClient/数据转换等实现对Call对象的适配转换,并能处理真正的网络请求。
这里面涉及的整体结构和角色分工,大概可以这样表示:

 

其中,扩展适配器、扩展数据转换和扩展OkHttpClient,虽然都是通过Retrofit实现扩展,但真正的使用者是Retrofit内部的ServiceMethod、OkHttpCall和okhttp3.call等类或对象。

反推Retrofit的设计过程

如果我们不直接正面分析Retrofit的结构设计和技术细节,而是先从Retrofit的功能和作用入手,倒过来推测Retrofit的目标,进而分析其架构和搭建细节,Retrofit为什么会设计成这样就很好理解了。

Retrofit的功能是按照接口定义,自动定制Call网络工作对象,所以Retrofit的目标应该就是避免为网络访问开发大量的配套代码。

为了实现这一目标,Retrofit需要分析哪些是易变的,哪些是不变的,然后分别处理。

由于Retrofit提供网络访问的工作对象,又是服务于具体业务,所以可以分网络访问和具体业务两部分来分析。

网络访问的不变性
对于网络访问来说,不变的是一定有一个实现网络访问的对象,Retrofit选用了自家的OkHttpClient,不过为了把Retrofit和OkHttp两个项目解耦合,Retrofit根据依赖倒置原则,定义了Retrofit自己的Call即retrofit2.call,并定义了操作网络请求的OkHttpCall

网络访问的易变性
对于网络访问来说,易变的是网络访问的url、请求方式(get/post等)、Http请求的Header设置与安全设置等,以及返回的数据类型。

针对易变的url和请求方式,Retrofit使用了方法注解的方式,可读性良好,扩展性优异,但这需要实现对接口函数中注解的解析,这样就有了ServiceMethod
针对Http请求的各种设置,其实Retrofit没做什么,因为Retrofit使用的OkHttp有拦截器机制,可以应付这种变化。
针对返回的数据类型,由于目标数据类型与业务有关,是不确定的,Retrofit无法提供一个万能的转换类,所以Retrofit提供了扩展接口,允许开发者自己定义ConverterFactory和Converter,去实现潜在的数据类型转换。

具体业务的不变性
对于具体业务来说,不变的是一定要有一个Call网络工作对象,所以Retrofit可以有一个生产对象的机制(像工厂一样)

具体业务的易变性
对于具体业务来说,易变的就是这个Call网络工作对象的类型,不仅有CallBacl回调、可能还有Flowable工作流、或者其他潜在的对象类型。

针对这种Call对象的易变性,Retrofit也是无法提供一个万能的实现类,所以也是提供了扩展解耦,允许开发者自己定义CallAdapterFactory和CallAdapter,去实现潜在的Call类型转换。

因为这种Call对象的生产需要有大量的配套代码,为了简化代码,Retrofit使用动态代理来生产这个对象。

最后,因为需要处理的方法和对象太多太复杂,需要使用建造者模式来把建造过程和使用过程分离开。

这样倒着走一遍之后,我们再看Retrofit的设计和实现原理,就会觉得水到渠成,对于Retrofit精妙的设计更会有一种切身体会。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值