Retrofit实现流程分析

本文分析了Retrofit的创建过程,包括Builder模式创建Retrofit对象、动态代理实现网络接口对象,以及对OkHttp的二次封装。讲解了Retrofit如何通过CallAdapter适配返回值类型,并介绍了对接口注解的处理。通过对Retrofit实现原理的理解,有助于提升代码设计和扩展能力。
摘要由CSDN通过智能技术生成

一、前言

Retrofi+OkHttp作为现在常用的网络请求框架,更多的时候我们只知道如何用以及如何扩展,但并没有真正了解其中底层的实现原理以及为什么要做这个开源的产品。接下来我们手动写一个简要的Retrofit框架,通过一步一步的代码实现来彻底明白其中的实现原理以及用到的技术点,这样对我们以后开发高品质的代码有更好的帮助。

二、基本使用流程

  1. 引入相关配置
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
//目前大部分服务端的接口设计都是json格式,因此可以引入json相关的Converter
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation 'com.squareup.okhttp3:okhttp:3.14.9'

  1. 定义请求接口
public interface GitHubService {
  @GET("users/{user}/repos")
  Call<List<Repo>> listRepos(@Path("user") String user);
}
  1. 获得GitHubService对象
Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com/")
    .build();

GitHubService service = retrofit.create(GitHubService.class);
  1. 获得Call对象,在不配置其他CallAdapter时候,Retrofit默认返回对象
Call<List<Repo>> repos = service.listRepos("octocat");
  1. 执行真正的网络请求,获得数据
//同步
try {
    Response<List<Repo>> rsp = repos.execute();
} catch (IOException e) {
    e.printStackTrace();
}

//异步
repos.enqueue(new Callback<List<Repo>>() {
    @Override
    public void onResponse(Call<List<Repo>> call, Response<List<Repo>> response) {
    }

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

6.扩展CallAdapter,接口中即可指定返回对象

public interface GitHubService {
    
  @GET("users/{user}/repos")
  Call<List<Repo>> listRepos(@Path("user") String user);

  @GET("users/{user}/repos")
  Observable<List<Repo>> listReposToObservable(@Path("user") String user);

  @GET("users/{user}/repos")
  LiveData<List<Repo>> listReposToLiveData(@Path("user") String user);
}

上面描述了基本的使用流程,使用也比较简单,对开发而言,更多是关注接口中定义方法的时候相关注解参数的配置,得到数据后再业务代码中处理相关的业务流程。下面我们手写一个简要版本的Retrofit,通过基础流程的实现来了解使用过程中使用的技术。

三、创建Retrofit对象以及网络接口对象

3.1 Builder(建造者)模式创建Retrofit

Retrofit对象的创建过程

Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com/")
    .build();

从上面的代码中可以看出Retrofit使用了Builder模式。Builder模式是将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示,同时还可以对对象的创建进行相关的约束处理。下面看一下Builder模式如何做了约束和默认处理。

    public Retrofit build() {
      //baseUrl默认不能为空
      if (baseUrl == null) {
        throw new IllegalStateException("Base URL required.");
      }
      //以下添加默认配置信息
      okhttp3.Call.Factory callFactory = this.callFactory;
      if (callFactory == null) {
        callFactory = new OkHttpClient();
      }

      Executor callbackExecutor = this.callbackExecutor;
      if (callbackExecutor == null) {
        callbackExecutor = platform.defaultCallbackExecutor();
      }
      List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
      callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));
      List<Converter.Factory> converterFactories =
      converterFactories.add(new BuiltInConverters());
      converterFactories.addAll(this.converterFactories);
      converterFactories.addAll(platform.defaultConverterFactories());
    
      .....        
    }

分析了Retrofit的创建过程后,我们实现自己简要版的Retrofit

public final class Retrofit {

    private HttpUrl baseUrl;
    Call.Factory callFactory;

    private Retrofit(Builder builder) {
        this.baseUrl = builder.baseUrl;
        this.callFactory = builder.callFactory;
    }

    public static class Builder {
        private HttpUrl baseUrl;
        private Call.Factory callFactory;
        public Builder baseUrl(String baseUrl) {
            return baseUrl(HttpUrl.get(baseUrl));
        }
        public Builder baseUrl(HttpUrl baseUrl) {
            Objects.requireNonNull(baseUrl, "baseUrl == null");
            List<String> pathSegments = baseUrl.pathSegments();
            if (!"".equals(pathSegments.get(pathSegments.size() - 1))) {
                throw new IllegalArgumentException("baseUrl must end in /: " + baseUrl);
            }
            this.baseUrl = baseUrl;
            return this;
        }
        //OkHttpClient已经实现了 okhttp3.Call.Factory
        //OkHttpClient默认配置交由外部设置
        public Builder client(OkHttpClient client) {
            return callFactory(Objects.requireNonNull(client, "client == null"));
        }
        //
        public Builder callFactory(Call.Factory factory) {
            this.callFactory = Objects.requireNonNull(factory, "factory == null");
            return this;
        }

        public Retrofit build() {
            if (baseUrl == null) {
                throw new IllegalStateException("Base URL required.");
            }
            //配置默认值
            if (callFactory == null) {
                callFactory = new OkHttpClient();
            }
            return new Retrofit(this);
        }


    }
}

3.2 代理模式

在使用Retrofi的时候,需要使用接口,用于设置网络请求信息

/*
*
*以下返回对象仅限demo使用,实际使用Retrofit的过程不能按照下面方式定义返回对象
*/

public interface TestApi {

    @GET("/api/user")
    UserInfo getUserInfo(@Query("key") String name,@Query("key") int code);

    @GET("/api/user")
    String getName(@Query("key") String id);
}

接着创建TestApi对象,进行请求操作

//retrofit.create()过程为动态代理模式
TestApi service = retrofit.create(TestApi.class);
UserInfo userInfo = service.getUserInfo("octocat",10);

在retrofit.create的过程中使用动态代理模式,下面介绍一些代理相关的内容:

代理模式:代理模式给某一个对象提供一个代理对象,并由代理对象控制原对象的引用。代理模式分为静态代理、动态代理、CGLIB代理。

  • 静态代理:需要手动创建代理类,同时给代理类指定被代理对象,再由代理类执行相关的操作。
  • 动态代理:不要手动创建代理类,我们只需要编写一个动态处理器就可以了,真正的代理对象由JDK在运行时为我们动态的来创建,需编写动态处理器,InvocationHandler,在该类中完成相关的操作。
  • CGLIB代理:CGLib采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。但因为采用的是继承,所以不能对final修饰的类进行代理

这里主要说明一下动态的实现过程,Retrofit创建代理类的方法如下:

public  <T> T create(Class<T> service) {
    return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[]{service}, new InvocationHandler() {
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            return null;
        }
    });
}

通过的Proxy.newProxyInstance的方法自动帮我们实例化了TestApi这个接口,在这里我们就有疑问了,实现一个接口,我们需要重写里面的相关方法,API又是怎么帮我们实现的呢?这里我们不去关心底层的实现细节,只需要了解底层帮我生成了一个什么样的类。通过下面的方法,我们将动态代理在运行时帮我们生成的类的信息保存下来,看一下里面具体的信息:

    private static void saveProxyFile(String filePath, Class<?>[] var1) {
        Fil
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值