美文网首页
【Android】【框架】【网络】【Retrofit】

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

作者: 徐乙_ | 来源:发表于2019-08-04 19:07 被阅读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

    相关文章

      网友评论

          本文标题:【Android】【框架】【网络】【Retrofit】

          本文链接:https://www.haomeiwen.com/subject/pzwydctx.html