【Android】【框架】【网络】【Retrofit】

2019-08-04  本文已影响0人  徐乙_

使用

1、接入

    compile 'com.squareup.retrofit2:retrofit:2.3.0'

    compile 'com.squareup.retrofit2:retrofit-converters:2.3.0'
    compile 'com.squareup.retrofit2:retrofit-adapters:2.3.0'

    compile 'com.squareup.retrofit2:converter-gson:2.3.0'
    compile 'com.squareup.retrofit2:adapter-rxjava2:2.3.0'

2、由此分析的架构

image.png

3、使用

// Retrofit的构建,一般全局仅构建一次
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://www.zhihu.com")
.addConverterFactory(GsonConverterFactory.create())
.build();

// 接口定义
public interface INetApiService {
    @GET("/demobiz/api.php")
    Call<BizEntity> getBizInfo(@Query("id") String id);
}

// 发起请求
retrofit.create(INetApiService.class)
  .getBizInfo("fff")
  .enqueue(new Callback<BizEntity>() {
            @Override
            public void onResponse(Call<BizEntity> call, Response<BizEntity> response) {}

            @Override
            public void onFailure(Call<BizEntity> call, Throwable t) {}
});

对比传统封装

// 接口定义
public class GetBizInfoRequest extends BaseRequest<BizEntity> {
     public String id;

     public GetBizInfoRequest(String id) {
         super("/demobiz/api.php");
         this.id = id;
     }
}

// 发起请求
AppNetWork.execute(new GetBizInfoRequest("fff"), new Callback<BizEntity>() {
            @Override
            public void onResponse(Call<BizEntity> call, Response<BizEntity> response) {}

            @Override
            public void onFailure(Call<BizEntity> call, Throwable t) {}
});
  1. 可以看到发起请求这一块都很简洁,区别在于接口的定义
  2. Retrofit通过编译时注解,把接口定义从类降低到了一个方法,代码更简洁,开销更小
  3. 面对大量接口的定义,比如Login相关的API,传统请求需要建立一个文件夹,里面存放若干Request类,而Retrofit请求,只需要一个LoginAPI的类,内部存放不同方法即可
  4. 合理使用编译时注解,可以大大简化项目,这个技巧值得我们学习与实践

架构

1、数据角度

image.png

对于开发者来说,发起网络请求是非常简单的
框架会给你制定一套模板,你按照模板,把数据填充进去,即可发起请求
而Retrofit提供给你的模板显然更简单,甚至于到了完美的地步

2、功能角度

image.png
  1. 通过编译时注解,搜集请求数据
  2. converter数据解析:比如默认的converter-gson,把json转换成了JavaBean。如果服务器返回的是xml,可以替换为对应的converter
  3. adapter响应类型转换:Retrofit自己定制了Call对象,区别于OkHttp的Call对象,内部会进行retrofit.call => okhttp.call的转换,高度解耦。而通过adapter,可以定义这种转换,并把这种能力开放出来,很典型的就是adapter-rxjava,定义了Observer<retrofit.Call<T>>到retrofit.Call<T>的转换
  4. OkHttp发起请求
    一言以蔽之,Retrofit只是数据的收集者,处理者,并不是网络请求的执行者

原理

对于原理的分析,只分析请求数据的搜集这一块
而对于converter、adapter等其他实现,由于过于简单,所以不再赘述

1、回顾Retrofit的使用

retrofit.create(INetApiService.class)
  .getBizInfo("fff")
  .enqueue(new Callback<BizEntity>() {
            @Override
            public void onResponse(Call<BizEntity> call, Response<BizEntity> response) {}

            @Override
            public void onFailure(Call<BizEntity> call, Throwable t) {}
});

看到getBizInfo方法的调用,可以明白INetApiService这个接口必然被实例化了,否则其方法无法被调用
可是我们并没有定义其实现类,所以这个类是框架为我们创建的,所以其实现手段就是动态代理

2、理解动态代理

看下create方法

public <T> T create(final Class<T> service) {
    return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
        new InvocationHandler() {
          @Override 
          public Object invoke(Object proxy, Method method, @Nullable Object[] args) throws Throwable {
            ServiceMethod<Object, Object> serviceMethod = (ServiceMethod<Object, Object>) loadServiceMethod(method);
            OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
            return serviceMethod.adapt(okHttpCall);
          }
        });
  }

Proxy.newProxyInstance就是Java动态代理的实现,可以根据一个类的类型,生产其实例化对象,并在某方法执行前后,织入一段逻辑
这也是Java常用的Hook手段,先通过动态代理生成“狸猫”,再通过反射替换“太子”,以达到不可告人的目的
这一切是通过JVM修改字节码来实现的

3、具体的实现

明白了动态代理的原理,自然知道,我们通过动态代理实现的接口,自然是空逻辑,所有的具体实现逻辑,就是我们传入的

public <T> T create(final Class<T> service) {
    return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
        new InvocationHandler() {
          @Override
          public Object invoke(Object proxy, Method method, @Nullable Object[] args) throws Throwable {
            ServiceMethod<Object, Object> serviceMethod = (ServiceMethod<Object, Object>) loadServiceMethod(method);
            OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
            return serviceMethod.adapt(okHttpCall);
          }
        });
  }

核心在于loadServiceMethod方法,不难猜到,这一步会对注解中的信息进行读取和保存,用于后续的网络请求
而且这里使用的是编译时注解,比如@Get中指定的url,还有@Query中指定的param key name,都会在编译时被保存
而param value,就是我们运行时动态传入的args

可优化的点

  1. 目前仅支持OkHttp通道,不支持灵活切换网络库,可以对此进行改造。因为很多公司设计的网络库比OkHttp更好或者更适合公司业务,比如美团更好地支持长连接,携程抛弃HTTP协议直接面向TCP协议做请求等。
  2. 不仅要支持网络库切换,还应该让网络库把线程控制权交出来,实现外部管理网络请求线程池,可以对网络请求性能做优化

结合RxJava使用

非常推荐Retrofit结合RxJava使用。在寻常请求中,是否使用RxJava没有任何影响。但是后续具有的复杂异步场景情况下,RxJava就能发挥很大的威力。比如一个请求执行完后,拿着返回数据,再次做请求,可以用flatMap解决回调的多重嵌套。比如后续要做磁盘缓存等等。

收获

  1. 编译时注解:可以用它来开发一些自己的库,大大简化自己的项目
  2. 动态代理:这里的动态代理的使用方式比较奇葩,目的不是为了在方法前后织入逻辑,而是给了接口统一的默认实现
  3. 解耦OkHttp,不直接使用okhttp.callback,而是使用adapter实现了retrofit.callback到okhttp.callback的转换,我们以后在封装另一个库的时候,完全可以如法炮制
  4. converter、adapter的配置。我们在开发一个库的时候,如果希望某个功能是可配置的,完全可以如法炮制

后记

有什么写得错误、让人费解或遗漏的地方,希望可以不吝赐教,我会马上更改

学习自

https://www.jianshu.com/p/f57b7cdb1c99

上一篇 下一篇

猜你喜欢

热点阅读