四.Retrofit

前言

核心思想就是AOP思想,面向切面编程。
AOP使用场景比如:LeakCanary、BlockCanary、Matrix、LifeCycle、OkHttp(拦截器)等

1.如何封装OkHttp

OkHttp是一个网络请求框架,不过这个网络请求框架存在一些不足:

  • 用户网络请求的接口配置繁琐,尤其是需要配置复杂的请求body、请求头和请求参数的时候;
  • 数据的解析过程需要用户手动拿到ResponseBody进行解析,不能复用;
  • 无法适配自动进行线程的切换;
  • 嵌套网络请求数据回调存在问题。

我们可以通过Retrofit来对OkHttp网络请求框架进行改造,具体的改造如下:

  • 请求前
    • 统一配置网络请求头
    • 一致适配请求Request
  • 返回结果
    • 数据适配
    • 线程切换

2.Retrofit的设计思想

Retrofit的使用方式:

//构建出Retrofit对象
var retrofit = Retrofit.Builder()
        .baseUrl("https://www.wanandroid.com/")//请求地址域名
        .addConverterFactory(GsonConverterFactory.create(Gson()))//返回数据进行Gson解析
        .addCallAdapterFactory(RxJava2CallAdapterFactory.create())//通过RxJava实现线程间切换
        .build()
//基于访问接口创建一个接口类型对象,通过动态代理生成了一个实现类
val retrofitService = retrofit.create<IRetrofitService>(IRetrofitService::class)
//基于接口类型对象构建以及参数,构建一个OkHttp的Call接口实现类ExecutorCallbackCall
var call = retrofitService.banner()
//将请求Call添加到OkHttp请求队列,最终执行的是Call接口的实现类ExecutorCallbackCall.enqueue方法
//当在调用ExecutorCallbackCall.enqueue方法时,会调用delegate.enqueue方法,
//而这个delegate是一个OkHttpCall对象,所以最终执行的是OkHttpCall.enqueue方法
call.enqueue(object:retrofit2.Callback<BannerBean>{
    
    
    override fun onFailure(call: retrofit2.Call<BannerBean>, exception: Throwable) {
    
    
    }

    override fun onResponse(call: retrofit2.Call<BannerBean>, response: retrofit2.Response<BannerBean>) {
    
    
        Log.e("TangKunLog",response.body().toString())
    }

})

3.ServiceMethod存在的价值

Retrofit的create()方法中,InvocationHandler接口回调的invoke方法,每回调一次invoke方法,就表示执行了接口中的一个方法,invoke方法的返回值是通过如下代码:
retrun loadServiceMethod(method).invoke(args != null ? args : emptyArgs);

这个方法包含的业务逻辑,先总结在这里,有一个整体的认识,然后通过后面的知识点去论证这个观点。

  • loadServiceMethod(method)
    • 该方法最终会返回ServiceMethod接口的实现类HttpServiceMethod
    • HttpServiceMethod类在解析注解的时候会调用createCallAdapter()创建CallAdapter接口,并重写适配方法adapt返回接口Call的实现类ExecutorCallbackCall
      那么ExecutorCallbackCall是怎么来的呢?
      在通过Builder建造者模式创建Retrofit对象时,调用build()方法中,会通过如下代码创建默认的请求适配器工厂类DefaultCallAdapterFactory
      代码如下:
      callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));
      ->new DefaultCallAdapterFactory(callbackExecutor);
      上面的ExecutorCallbackCall就是通过DefaultCallAdapterFactory类中的get()方法创建得来,这里流程相对比较复杂,知道结果即可。
    • 参数method就是我们接口类(如:IRetrofitService.java)中的一个方法(如:banner)
  • invoke(args != null ? args : emptyArgs)
    • 通过HttpServiceMethod.invoke()最终创建Call接口的实现类OkHttpCall
    • 参数args就是接口中的每一个方法包含的参数数组

loadServiceMethod(method)这个方法会返回ServiceMethod抽象类的实现类HttpServiceMethod,然后就会通过如下代码去解析接口请求方法所包含的注解信息:
ServiceMethod.parseAnnotations(this, method);
注解解析完成后,然后将注解信息添加到接口请求工厂中去RequestFactoryRequestFactory.java代码如下:

RequestFactory(Builder builder) {
    
    
   method = builder.method;//方法
   baseUrl = builder.retrofit.baseUrl;//域名
   httpMethod = builder.httpMethod;//get/post请求方式
   relativeUrl = builder.relativeUrl;//接口地址
   headers = builder.headers;//请求头
   contentType = builder.contentType;//数据类型
   hasBody = builder.hasBody;//是否包含body
   isFormEncoded = builder.isFormEncoded;
   isMultipart = builder.isMultipart;//是否是多个参数
   parameterHandlers = builder.parameterHandlers;
   isKotlinSuspendFunction = builder.isKotlinSuspendFunction;
 }

由于前面loadServiceMethod(method)方法会返回ServiceMethod抽象类的实现类HttpServiceMethod,接着调用invoke方法的时候,其实就是调用的HttpServiceMethod.invoke()方法,而这个方法会创建OkHttpCall对象,作为方法的返回值中的参数。
HttpServiceMethod.java中的源码如下:

@Override
final @Nullable ReturnT invoke(Object[] args) {
    
    
  //创建Call接口的实现类OkHttpCall对象,作为抽象方法adapt的参数返回
  Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
  return adapt(call, args);
}

//抽象方法,由于call参数是上面创建的OkHttpCall<ResponseT>对象。
//因此,OkHttpCall在调用enqueue方法发起异步请求后,回调的方法中的泛型对象(比如:BannerBean)就是上面invoke方法的返回值,同样也是我们接口请求的返回值
protected abstract @Nullable ReturnT adapt(Call<ResponseT> call, Object[] args);

综上所述,我们通过Retrofit.create()方法构建的接口,调用接口中每一个方法发起网络请求,最终会返回的Call接口的实现类OkHttpCall,所以调用的其实是OkHttpCall的同步或异步请求,然后在CallBack中回调onResponse和onFailure方法,返回响应的结果。

4.Retrofit的整理流程

  • 通过Builder构建了一个Retrofit对象,可以设置如下功能:baseUrl、对响应数据进行Gson解析、通过RxJava实现线程切换使得网络请求的回调在主线程中,等等。
  • 通过Retrofit.create(IRetrofitService.class)方法创建接口的动态代理类对象
  • 调用接口中的方法,就会执行动态代理中的invoke方法,创建ServiceMethod对象,用于封装请求的参数;同时构建一个ExecutorCallbackCall对象,作为接口调用方法返回Call接口的实现类
  • 调用Call.enquque方法发起网络请求,就会调用到实现类ExecutorCallbackCall的同步或者是异步请求方法,此时就会通过适配器模式将ExecutorCallbackCall适配出一个OkHttpCall对象,同时会将前面封装的ServiceMethod中的参数封装到OkHttp的Request对象中去,然后通过工厂.newCall(Request)方法构造出OkHttp3.Call对象,最终调用OkHttpCall对象的同步或异步请求方法,完成整个网络请求。
  • 总结:
    也就是说Retrofit只是对数据进行了封装,将一些请求参数封装到ServiceMethod对象,封装出Call接口的实现类ExecutorCallbackCall对象;最终还是会把这些数据封装到OkHttp层,将ServiceMethod封装到Request对象中去,将ExecutorCallbackCall封装到OkHttp3.Call对象中去,让OkHttp去实现网络请求。

5.Retrofit使用到了哪些设计模式

  • 建造者设计模式Builder

    • 通过Builder模式创建出我们的Retrofit对象,可以对配置的参数进行自由选择。
  • 外观/门面模式

    • Retrofit通过对内部参数的封装,可以直接通过Retrofit类与外界进行交互,我们可以不用关心内部实现的复杂细节。
    • 举例:
      我们需要通过Glide展示图片,可以将Glide封装到一个工具类ImageUtils类中;我们的想用在展示图片的时候直接调用ImageUtils中的方法即可,当有一个更好的网络框架出现的时候,我们只需要对ImageUtils中的加载图片的代码进行替换即可,而不需要在每一个使用到加载图片的地方都去做更改,大大提高了我们的开发效率。
  • 动态代理

    • 利用了AOP思想,在调用Retrofit.create()方法时会创建出网络接口实现类,实现类在调用实现的接口中的每一个方法时,就会调用invocationHandler.invoke()方法,这些方法最终都会统一调用到Retrofit.create()方法的源码中的InvocaionHandler接口实现的invoke方法。
      Retrofit.java中源码如下:
      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];
      
              //参数1,接口类的代理类对象,(代理类可以存在多个,是通过上面数组中service传递进来的,代码:new Class<?>[] { service })
              //参数2,代理类中实现的网络请求接口中的方法
              //参数3,代理类中每一个方法中的参数
              @Override public @Nullable Object invoke(Object proxy, Method method,
                  @Nullable Object[] args) throws Throwable {
              
              
                //如果该方法是Object中的方法,则遵循正常调用
                if (method.getDeclaringClass() == Object.class) {
              
              
                  //代理类对象调用方法
                  return method.invoke(this, args);
                }
                if (platform.isDefaultMethod(method)) {
              
              
                  return platform.invokeDefaultMethod(method, service, proxy, args);
                }
                //将方法中的注解解析出来,构建出请求网络的参数对象ServiceMethod,然后调用这个方法的参数,最终返回一个Call接口的实现类ExecutorCallbackCall
                return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
              }
            });
      }
      
  • 装饰器模式

    • 使用场景:okhttp3.Call->ExecutorCallbackCall->OkHttpCall
      • okhttp3.Call 作为一个接口
      • OkHttpCall 对请求的参数做了一系列的封装
      • ExecutorCallbackCall 是对OkHttpCall的封装,将我们的请求的回调CallBack通过Handler放在主线程中执行,所以我们使用call.enqueue(object:Callback<BannerBean>{}onResponseonFailure都是执行在主线程中。
    • 装饰器在Android中其他使用场景:
      • Context

        • ContextImpl
        • ContextWrapper
          • ContextThemeWrapper
            • Activity
          • Service
          • Application
      • new File(file)

        • new FileOutputStream(new File(file))
          • new BufferedOutputStream(new FileOutputStream(new File(file)))
            • DataOutputStream dataOutputStream = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(new File(file))))
  • 代理模式

    • 我们在使用Retrofit.create(IRetrofitService.class)方法时,这个create方法的源码中就会调用如下代码,采用动态代理的方式,创建出这个接口的代理类对象,代码如下:
      Proxy.newProxyInstance(service.getClassLoader,new Class<?>[]{service},new InvocationHandler(){...invoke()...})
      通过这个代理类对象,就可以调用接口中的方法,从而实现网络请求。
  • 适配器模式

    • 使用场景:OkHttpCall->ExecutorCallbackCall
      • DefaultCallAdapterFactory类的get()方法中,然后在返回的CallAdapter接口重写的adapt(Call<Object> call)方法中,将OkHttpCall对象适配成为ExecutorCallbackCall对象。
        DefaultCallAdapterFactory.java中代码如下:
        @Override 
        public @Nullable CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
                  
                  
          //省略非核心代码
          return new CallAdapter<Object, Call<?>>() {
                  
                  
            @Override public Call<Object> adapt(Call<Object> call) {
                  
                  
              return executor == null
                  ? call
                  //将传入的OkHttpCall对象通过适配成为ExecutorCallbackCall对象
                  : new ExecutorCallbackCall<>(executor, call);
            }
          };
        }
        
      • 使用场景2:
        在接口类中的每一个方法,可以返回Call对象,同时也可以返回Observable对象;从Call对象转换成Obervable对象的过程中,就采用了适配器模式来实现。
        RxJava2CallAdapter.java代码如下:
        public Object adapt(Call<R> call) {
                  
                  
          //根据同步请求还是异步请求,通过适配器将Call转换成Observable返回
          Observable<Response<R>> responseObservable = isAsync
              ? new CallEnqueueObservable<>(call)
              : new CallExecuteObservable<>(call);
        }
        
      • 其他使用场景:
        Gson解析,将String字符串通过适配器转换成JavaBean对象,同理,也可以将JavaBean通过适配器转换成String字符串。

如何使用设计模式:

  1. 什么是设计模式?
  2. 分析框架源码中采用了哪些设计模式?为什么要使用设计模式?有什么好处?
  3. 思考自己的项目,可以使用哪些设计模式?能带来什么好处?

面试题

Retrofit使用过程中的几个疑惑:

  • Interface接口能够直接用?
    • 因为使用了动态代理生成了一个实现类,使用的是实现类的对象。
  • 参数给了谁?
    • 接口请求方法中的参数会通过注解和反射解析出来,然后封装成ServiceMethod对象,最终到OkHttp网络请求的Request对象中去。
  • 具体请求的url是怎么形成的?
    • 通过Retrofit在通过Builder的时候传入的baseUrl,和我们的接口请求方法上通过注解传入的接口请求地址relativeUrl,会被解析到ServiceMethod对象中去,baseUrlrelativeUrl共同构建成OkHttp网络请求Request中的url参数。
  • 为什么所有的请求都是同一个方式?
    • 采用AOP思想,在动态代理创建接口实现类的InvocationHandler的invoke方法中,统一对我们接口中的所有方法进行解析,通过注解和反射的方法解析出来,将参数封装到OkHttpRequest对象,同时封装出OkHttpCall对象,最终通过OkHttp完成同步或异步网络请求。

猜你喜欢

转载自blog.csdn.net/tangkunTKTK/article/details/130890920
今日推荐