美文网首页
Retrofit框架源码学习

Retrofit框架源码学习

作者: 浪里_个郎 | 来源:发表于2020-04-09 21:16 被阅读0次

    引用和参考:
    从架构角度看Retrofit的作用、原理和启示
    Retrofit分析-漂亮的解耦套路

    本文有以下内容:
    1,Retrofit用于解决什么问题
    2,Retrofit怎么实现Request的封装
    3,Retrofit中有哪些功能扩展,或者说有哪些可供用户自定义的部分
    4,Retrofit有哪些值得学习的设计思想

    1,Retrofit用于解决什么问题

    Retrofit内部使用了自家的OKHttp来实现网络请求,如果简单的使用,OKHttp已经很方便了,但如果需要定义大批量的Request,就要写很多重复代码。并且网络请求通常使用Rxjava处理并发,不同库之间组合使用时也相对麻烦。
    Retrofit简化了这些重复性的代码。我们只需要定义一个网络请求接口,加上注解,Retrofit就能帮我们生成Request并交给OKHttp处理,并且能根据我们的需求,返回生成OKHttp的Call对象或Rxjava的Observable对象,将返回内容解析成我们需要的类型。
    Retrofit基本使用流程:

            //1,创建OkHttpClient
            sOkHttpClient = new OkHttpClient.Builder()
                    ...
                    .addInterceptor(InterceptorUtil.getCacheInterceptor())
                    .build();
            //2,使用Build模式创建Retrofit
            sRetrofit = new Retrofit.Builder()
                    .baseUrl(sBaseUrl) //主页
                    .client(sOkHttpClient)
                    .addConverterFactory(GsonConverterFactory.create())
                    .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                    .build();
            //3,定义网络请求API
            public interface LoginApi {
              public static final String HOST = "http://www.wanandroid.com/tools/mockapi/2164/";
              //通过注解封装url请求,接在主页后面
              @GET("hotchpotch_login")
              Observable<BaseResponse<Login>> login();
            }
            //4,通过Retrofit将网络请求API转换为实际的网络请求,并获取返回值
            return sRetrofit.create(LoginApi.HOST,LoginApi.class);
    

    2,Retrofit怎么实现Request的封装

    Retrofit封装

    自定义interface里有注解的方法:

    @GET("hotchpotch_login")
    Observable<BaseResponse<Login>> login();
    

    注解中可以描述url和请求方式,Retrofit解析注解并封装进ServiceMethod。通过ServiceMethod可以获取Request:

    Request request = serviceMethod.toRequest(args);
    

    其中的解析和转换,是在Retrofit.create(XXX.class)方法中通过动态代理完成的。XXX.class代表自定义接口,里面定义了带注解的方法,代表Request需求。

    关于动态代理

    我们把定义好的网络访问接口传给Retrofit,就能得到一个可以调用的实现类,这就使用了JDK中Proxy和InvocationHandler组合的动态代理。
    proxy的方法:

    Object newProxyInstance(ClassLoader var0, Class<?>[] var1, InvocationHandler var2)
    

    会返回代理类,每当通过代理调用var1(目标类)中方法时,都会变为调用var2中的

    Object invoke(Object var1, Method var2, Object[] var3)
    

    invoke函数实现的返回值需要和自定义接口中方法的返回值一致。invoke函数中会帮我们自动实现接口中方法的功能,生成代理类:

      @SuppressWarnings("unchecked") // Single-interface proxy creation guarded by parameter safety.
      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];
    
              @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,返回自定义接口的代理类
                return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
              }
            });
      }
    

    3,Retrofit中有哪些功能扩展,或者说有哪些可供用户自定义的部分

    这个问题,其实涉及到Retrofit框架的开发原因。
    对于一个网络请求,有哪些变量?
    url、请求方式(get/post等)、Http请求的Header设置与安全设置等,以及返回的数据类型。
    针对易变的url和请求方式,Retrofit使用了方法注解的方式,可读性良好,扩展性优异,但这需要实现对接口函数中注解的解析,这样就有了ServiceMethod。
    针对Http请求的各种设置,其实Retrofit没做什么,因为Retrofit使用的OkHttp有拦截器机制,可以应付这种变化。
    针对返回的数据类型,默认情况下,Retrofit会把HTTP响应体反序列化到OkHttp的ResponseBody中,加入Converter可以将返回的数据直接格式化成你需要的样子。由于目标数据类型与业务有关,是不确定的,Retrofit无法提供一个万能的转换类,所以Retrofit提供了扩展接口,允许开发者自己定义ConverterFactory和Converter,去实现潜在的数据类型转换。
    针对返回的网络工作对象,对于OKHttp来说,一个请求返回的是okhttp3.call这个Call接口,但如果我们要返回Rxjava的ObServable或Flowable接口的网络工作对象,可以使用addCallAdapterFactory添加CallAdapterFactory。
    GsonConverterFactory和RxJava2CallAdapterFactory都是Retrofit帮我们写好的,如果有需要,我们可以自己扩展。添加扩展的示例代码如下:

    sRetrofit = new Retrofit.Builder()
        .baseUrl(baseUrl)
        .client(sOkHttpClient)
        .addConverterFactory(GsonConverterFactory.create()) //可以把Http访问得到的json字符串转换为Java数据对象BizEntity
        .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) //Rxjava网络工作对象
        .build();
    return sRetrofit.create(tClass);
    

    下面定义的请求,返回的是Observable网络工作对象,返回值为Gson转成的BaseResponse类。

    public interface LoginApi {
        public static final String HOST = "http://www.wanandroid.com/tools/mockapi/2164/";
        //自定义接口与返回数据模拟返回统一格式数据封装(http://www.wanandroid.com/tools/mockapi)
        @GET("hotchpotch_login")
        Observable<BaseResponse<Login>> login();
    }
    

    4,Retrofit有哪些值得学习的设计思想

    stay4it大神的流程图

    在设计模式的使用上,首先通过外观模式,用Retrofit类封装了各个功能模块的接口,在使用方便的同时,将用户代码和实际的功能模块解耦。
    然后,通过动态代理模式,将用户通过注解定义的接口中的方法生成网络请求。
    再往后,就是通过适配器模式,将OKHttp的返回适配成用户需要的类型。
    另外,就是框架对于面向对象原则的坚持。哪怕是使用自家的OkHttp,哪怕底层使用了OkHttpClient去处理网络请求,但她并没有使用okhttp3.call这个Call接口,而是自己又建了一个retrofit2.Call接口,OkHttpCall继承的是retrofit2.Call,与okhttp3.call只是引用关系。这样的设计符合依赖倒置原则,可以尽可能的与OkHttpClient解耦。

    其实,对于设计的体会,是随着自己的经验不断增加的,过一段时间再来看源码,可能又是一堆新的体验。

    相关文章

      网友评论

          本文标题:Retrofit框架源码学习

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