源码解析-retrofit

Retrofit的本质是RESTful的HTTP网络请求框架的封装,网络请求的工作本质是OkHttp完成,而Retrofit只是负责网络请求接口的封装。但是现在这么多人用,也看得出这个框架封装的十分受程序员喜爱。

现在先看目录:

一、Retrofit的使用

二、源码解析

 

一、Retrofit的使用

我们先来看步骤:
1)创建描述网络请求的接口

2)创建Retrofit实例

3)创建网络请求接口实例并配置网络请求参数

4)发送网络请求

5)处理服务器返回的数据

 

先看网络请求的接口

public interface retrofit_interface {
    @GET("部分URL")
    Call<String> getCall();
}

这就是第一步,这个接口类,@GET注解方法和Call都是属于 Retrofit的类。注解方法也是Retrofit方便使用的体现。

@GET代表使用GET请求。Call是个泛型,返回的是string。

我们看下后面的4步:

public class RetrofitDemo {
    //创建Retrofit实例
    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl("http://www.baidu.com/")//设置网络请求基础url地址
            .addConverterFactory(GsonConverterFactory.create())//设置数据解析器
            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())//支付rxjava
            .build();

    //创建 网络请求接口的实例
    retrofit_interface request = retrofit.create(retrofit_interface.class);

    //对 发送请求 进行封装
    Call<String> call = request.getCall();

    public void callRequest(){
        //发送网络请求
        call.enqueue(new Callback<String>() {
            @Override
            public void onResponse(Call<String> call, Response<String> response) {

            }

            @Override
            public void onFailure(Call<String> call, Throwable t) {

            }
        });
    }

}

通过builder创建实例并且赋值。这里我们可以看到有个baseurl的设置,这和接口注解@Get(“部分url”)里的部分url是分开的,实际请求则是两个url拼接起来。

而后通过retrofit实例创建网络请求实例。再通过网络请求实例去进行网络请求。

二、源码解析

解析的主要内容:

2.1、如何创建我们的Retrofit实例

2.2、网络请求接口实例创建

2.3、发送网络请求

 

2.1、如何创建我们的Retrofit实例:

我们看到上面创建Retrofit的时候使用的是builder模式:

    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl("http://www.baidu.com/")//设置网络请求基础url地址
            .addConverterFactory(GsonConverterFactory.create())//设置数据解析器
            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())//支付rxjava
            .build();

builder模式简单说就是将复杂对象的创建和表示相分离。就说Retrofit的创建实际上很复杂,但是它将大部分的重要内容都予以默认值,如果你不去改变就不需要去设置。

我们先来看下Retrofit内部的一些变量:

public final class Retrofit {
  private final Map<Method, ServiceMethod<?, ?>> serviceMethodCache = new ConcurrentHashMap<>();

  final okhttp3.Call.Factory callFactory;
  final HttpUrl baseUrl;
  final List<Converter.Factory> converterFactories;
  final List<CallAdapter.Factory> adapterFactories;
  final @Nullable Executor callbackExecutor;
  final boolean validateEagerly;

  Retrofit(okhttp3.Call.Factory callFactory, HttpUrl baseUrl,
      List<Converter.Factory> converterFactories, List<CallAdapter.Factory> adapterFactories,
      @Nullable Executor callbackExecutor, boolean validateEagerly) {
    this.callFactory = callFactory;
    this.baseUrl = baseUrl;
    this.converterFactories = unmodifiableList(converterFactories); // Defensive copy at call site.
    this.adapterFactories = unmodifiableList(adapterFactories); // Defensive copy at call site.
    this.callbackExecutor = callbackExecutor;
    this.validateEagerly = validateEagerly;
  }

我们看到第一个变量就是一个Map,这个map是用来放置我们网络请求的配置对象。这个配置对象里面有网络请求的方法、数据转换器、网络请求适配器、网络请求工厂、url等。

再看第二个变量callFactory,这个Call代表的是我们的网络请求器,同步异步都是通过这个call调用,而且我们可以看到这个类是属于okhttp3的。

第三个变量 baseUrl,网络请求的基础地址。

第四个converterFactories,数据转换器工厂的集合,数据转换器工厂:用于生产我们数据转换器converter。如json,服务返回数据给我们json类型,然后通过这个转换器解析,然后在主线程中显示。

第五个adapterFactories,网络请求适配器的集合,网络请求适配器的工厂:用于生成我们的CallAdapter网络请求适配器。

第六个callbackExecutor,回调方法的执行器,主线程切换到子线程、子线程切换到主线程都是用这个方法执行的。

第七个validateEagerly,用于判断是否提前对我们接口当中的注解进行转换的标志位。

这里我们可以看到,成功建立Retrofit的标准就是这么几个变量。

我们先看下关于CallAdapter.Factory:

我们可以看到Factory是在CallAdapter里的抽象类,而这个抽象类的实现类有三种,Default、Executor、rxjava2。

用个例子说明下:假如我们一开始想用ExecutorCall来替换线程,然后我们发现rxjava2更加方便,不需要用handler来替换线程。那么我们只需要加上RxJava2CallAdapterFactory就可以完成我们okhttpCall到Rxjava间的转换。

现在我们回到前面

    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl("http://www.baidu.com/")//设置网络请求基础url地址
            .addConverterFactory(GsonConverterFactory.create())//设置数据解析器
            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())//支付rxjava
            .build();

我们对Retrofit的内部变量有了些了解后,我们再来下build()方法做了什么:

 public Retrofit build() {
      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();
      }

      // Make a defensive copy of the adapters and add the default Call adapter.
      List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories);
      adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));

      // Make a defensive copy of the converters.
      List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);

      return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
          callbackExecutor, validateEagerly);
    }

第一步就baseUrl的非空判断。

第二步将成员变量callFactory赋给我们的callFactory对象,然后做非空判断。为空就新建个OkhttpClient。这里我们可以看出retrofit本质使用okhttp进行请求。

第三步将成员变量callbackExecutor赋给我们的callbackExecutor对象,然后做非空,为空则新建defaultCallbackExecutor。callbackExecutor实际上就是主线程切换到子线程的执行器。

第四步向集合中添加我们这个的请求适配器adapterFactories。

第五步新建一个数据转换器的集合converterFactories ,在这个存储集合中存储的就是我们默认的转换器工厂,以及我们可以自定我们的转换器工厂,比如前面写的RxJava2CallAdapterFactory。

这里的adapterFactories和converterFactories它们的存储方式都是从首位到末尾遍历的。在集合当做位置越靠前就有越高的使用权限。

第六步创建Retrofit并且将刚刚的对象放入,然后返回。

看到这里我们就已经看完了关于Retrofit的接口实例创建。这个是上面retrofit使用的第二部,下面讲解的是第三步网络请求接口实例创建。

 

2.2、网络请求接口实例创建:

public interface RetrofitService {

    String BASE_URL = "http://IP:port/api/";


    @GET("self/getUserSelfInfo")
    Observable<LoginResponse> login(@Body String request);

}
//创建 网络请求接口的实例
    retrofit_interface request = retrofit.create(retrofit_interface.class);

第一步的网络请求接口就是在第三步这使用retrofit实例create出网络请求的实例。那么现在来看下create方法:

public <T> T create(final Class<T> service) {
    Utils.validateServiceInterface(service);
    if (validateEagerly) {
      eagerlyValidateMethods(service);
    }
    return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
        new InvocationHandler() {
          private final Platform platform = Platform.get();

          @Override public Object invoke(Object proxy, Method method, @Nullable Object[] args)
              throws Throwable {
            // If the method is a method from Object then defer to normal invocation.
            if (method.getDeclaringClass() == Object.class) {
              return method.invoke(this, args);
            }
            if (platform.isDefaultMethod(method)) {
              return platform.invokeDefaultMethod(method, service, proxy, args);
            }
            ServiceMethod<Object, Object> serviceMethod =
                (ServiceMethod<Object, Object>) loadServiceMethod(method);
            OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
            return serviceMethod.callAdapter.adapt(okHttpCall);
          }
        });
  }

首先会根据前面说到validateEagerly标识位来判断是否需要进行验证。我们来看下eagerlyValidateMethods方法

  private void eagerlyValidateMethods(Class<?> service) {
    Platform platform = Platform.get();
    for (Method method : service.getDeclaredMethods()) {
      if (!platform.isDefaultMethod(method)) {
        loadServiceMethod(method);
      }
    }
  }

ServiceMethod<?, ?> loadServiceMethod(Method method) {
    ServiceMethod<?, ?> result = serviceMethodCache.get(method);
    if (result != null) return result;

    synchronized (serviceMethodCache) {
      result = serviceMethodCache.get(method);
      if (result == null) {
        result = new ServiceMethod.Builder<>(this, method).build();
        serviceMethodCache.put(method, result);
      }
    }
    return result;
  }

这个方法显示遍历我们所声明好的方法,然后依次去调用loadServiceMethod方法。loadServiceMethod使用的是一个单例的形式去获取serviceMethodCache,serviceMethodCache就是Retrofit内的第一个变量。然后通过get方法去获取相应的ServiceMethod。如果为空则创建个新的Method。这里可以看到采用的也是builder模式:ServiceMethod.Builder<>(this, method).build()。

那我们就需要来看下builder和build到底做了什么:

  Builder(Retrofit retrofit, Method method) {
      this.retrofit = retrofit;
      this.method = method;
      this.methodAnnotations = method.getAnnotations();
      this.parameterTypes = method.getGenericParameterTypes();
      this.parameterAnnotationsArray = method.getParameterAnnotations();
    }

getAnnotations()方法:获取我们网络请求方法里的注释

getGenericParameterTypes():获取网络请求的参数类型

getParameterAnnotations():获取网络请求的注解内容

public ServiceMethod build() {
      callAdapter = createCallAdapter();
      responseType = callAdapter.responseType();
      if (responseType == Response.class || responseType == okhttp3.Response.class) {
        throw methodError("'"
            + Utils.getRawType(responseType).getName()
            + "' is not a valid response body type. Did you mean ResponseBody?");
      }
      responseConverter = createResponseConverter();

      for (Annotation annotation : methodAnnotations) {
        parseMethodAnnotation(annotation);
      }

      ......
      int parameterCount = parameterAnnotationsArray.length;
      parameterHandlers = new ParameterHandler<?>[parameterCount];
      for (int p = 0; p < parameterCount; p++) {
        Type parameterType = parameterTypes[p];
        if (Utils.hasUnresolvableType(parameterType)) {
          throw parameterError(p, "Parameter type must not include a type variable or wildcard: %s",
              parameterType);
        }

        Annotation[] parameterAnnotations = parameterAnnotationsArray[p];
        if (parameterAnnotations == null) {
          throw parameterError(p, "No Retrofit annotation found.");
        }

        parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
      }
      ......
      return new ServiceMethod<>(this);
    }

第一创建了callAdapter,作用是回调传递到UI线程。这个创建是根据Retrofit创建里设置的callAdapter来判断创建的。

第二callAdapter.responseType()。返回我们想要的类型。如我上面写的是Call<String>,则返回的是String,然后对这个类型进行判断。

第三createResponseConverter()。创建我们的数据类型转换器。即Retrofit创建里设置的GsonConverterFactory.create()。如服务器返回的是json类型的String,我们想要拿到的是User类型,转换器就会将String转成User返回。

第四遍历我们所有方法的注解去调用 parseMethodAnnotation(annotation);方法去解析网络请求的注解。

第五 parameterAnnotationsArray.length获取我们当前方法的参数数量。然后通过parseParameter(p, parameterType, parameterAnnotations);去解析我们的方法参数和注解。

 

我们看回网络请求接口的实例创建下一步,Proxy.newProxyInstance:

return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
        new InvocationHandler()

一看 Proxy就知道是用了动态代理,最终执行方法在InvocationHandler里的invoke(Object proxy, Method method, @Nullable Object[] args)方法中。这个方法调用的其实就是我们定义的接口方法getCall()。

而在这个invoke方法里面有个最核心的代码需要注意下:

ServiceMethod<Object, Object> serviceMethod =
                (ServiceMethod<Object, Object>) loadServiceMethod(method);
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);

loadServiceMethod(method)方法会将我们前面配置好的属性去配置我们的ServiceMethod这个对象。也就是我们的请求信息都在里面。然后再去创建okHttpCall对象,最后调用 okHttpCall,并且根据callAdapter去转换成我们的网络请求对象。

loadServiceMethod这个方法上面有讲解过。

new OkHttpCall<>(serviceMethod, args);直接根据我们的serviceMethod和args的参数进行创建。我们来看下创建的OkhttpCall:

final class OkHttpCall<T> implements Call<T> {
  private final ServiceMethod<T, ?> serviceMethod;
  private final @Nullable Object[] args;

  private volatile boolean canceled;

  @GuardedBy("this")
  private @Nullable okhttp3.Call rawCall;
  @GuardedBy("this")
  private @Nullable Throwable creationFailure; // Either a RuntimeException or IOException.
  @GuardedBy("this")
  private boolean executed;

  OkHttpCall(ServiceMethod<T, ?> serviceMethod, @Nullable Object[] args) {
    this.serviceMethod = serviceMethod;
    this.args = args;
  }

这里是变量和构造方法
第一个和第二个就是我们传的serviceMethod和args参数。一个代表网络请求的所有请求数值,args则表示请求的参数。

第四个参数是个okhttp3.call的对象,这个就是实际进行网络访问的类。

构造函数就比较简单,传入参数赋值。

我们下面看下第三个重要的代码:return serviceMethod.callAdapter.adapt(okHttpCall);

这里用到callAdapter,之前说过callAdapter有三种类型,DefaultCallAdapter、ExecutorCallAdapter、rxjava2CallAdapter三种。之类看下rxjava2的:

 @Override public Object adapt(Call<R> call) {
    Observable<Response<R>> responseObservable = isAsync
        ? new CallEnqueueObservable<>(call)
        : new CallExecuteObservable<>(call);

    Observable<?> observable;
    if (isResult) {
      observable = new ResultObservable<>(responseObservable);
    } else if (isBody) {
      observable = new BodyObservable<>(responseObservable);
    } else {
      observable = responseObservable;
    }

    if (scheduler != null) {
      observable = observable.subscribeOn(scheduler);
    }
    
    ......
    return observable;
  }

这个就是通过scheduler这个线程控制的管理工具来完成线程和主线程中的切换。

 

那么可以看出后面这两行重要代码实现的内容也比较简单。下面来总结下网络请求接口实例创建

1.动态创建网络请求接口的实例

2.创建serviceMethod对象(包含网络请求的所有注解属性等,通过建造者模式和单例模式完成-对应loadServiceMethod方法)

3.对serviceMethod对象进行网络请求参数配置(即解析网络请求的方法参数如url、请求类型、网络请求适配器、转换器等信息)

4.对serviceMethod对象假如线程切换的操作

5.最终创建并返回一个OkHttpCall类型的网络请求对象

 

2.3、发送网络请求:

下面以异步请求作为例子:

    //对 发送请求 进行封装
    Call<String> call = request.getCall();
    public void callRequest(){
        //发送网络请求
        call.enqueue(new Callback<String>() {
            @Override
            public void onResponse(Call<String> call, Response<String> response) {
            }
            @Override
            public void onFailure(Call<String> call, Throwable t) {
            }
        });
    }

创建好Call对象后,调用enqueue方法来发送异步的网络请求。我们来看下实现:

  void enqueue(Callback<T> callback);

一个接口,我们来看下实现类ExecutorCallAdapterFactory :

    @Override public void enqueue(final Callback<T> callback) {
      checkNotNull(callback, "callback == null");

      delegate.enqueue(new Callback<T>() {
        @Override public void onResponse(Call<T> call, final Response<T> response) {
          callbackExecutor.execute(new Runnable() {
            @Override public void run() {
              if (delegate.isCanceled()) {
                // Emulate OkHttp's behavior of throwing/delivering an IOException on cancellation.
                callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled"));
              } else {
                callback.onResponse(ExecutorCallbackCall.this, response);
              }
            }
          });
        }

        @Override public void onFailure(Call<T> call, final Throwable t) {
          callbackExecutor.execute(new Runnable() {
            @Override public void run() {
              callback.onFailure(ExecutorCallbackCall.this, t);
            }
          });
        }
      });
    }

这里用了delegate调用了enqueue方法。这个delegate就是个代理的作用,调用的其实是Okhttp的enqueue方法,我们来看下okHttpCall的enqueue方法:

  @Override public void enqueue(final Callback<T> callback) {
    checkNotNull(callback, "callback == null");

    okhttp3.Call call;
    Throwable failure;

    synchronized (this) {
      if (executed) throw new IllegalStateException("Already executed.");
      executed = true;

      call = rawCall;
      failure = creationFailure;
      if (call == null && failure == null) {
        try {
          call = rawCall = createRawCall();
        } catch (Throwable t) {
          failure = creationFailure = t;
        }
      }
    }

    ......

    call.enqueue(new okhttp3.Callback() {
      @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse)
          throws IOException {
       ......
    });
  }

首先是看createRawCall方法:创建OKhttp Request对象和封装我们okhttp的call对象:

  private okhttp3.Call createRawCall() throws IOException {
    Request request = serviceMethod.toRequest(args);
    okhttp3.Call call = serviceMethod.callFactory.newCall(request);
    if (call == null) {
      throw new NullPointerException("Call.Factory returned null.");
    }
    return call;
  }

代码比较简单:就是创建request和call对象。但是就是用这个两个去进行网络请求。我们看回去直接就是调用了call的enqueue方法。然后就是通过callbackExecutor 切换主线程返回解析数据。

总结下流程:

1.对网络请求接口的方法中的每个参数利用对应ParameterHandler进行解析

2.使用OkHttp的Request发送网络请求

3.对返回的数据使用之前设置的数据转换器(GsonConverterFactory)解析返回的数据

4.进行现场奇幻从而在主线程处理返回的数据结果

 

写到这里Retrofit基本也写完了,写起来还是比较长的,但是如果自己去看这些代码就会快很多。主要还是理解了解为何内部这么写。写的内容可能会有些理解上的错误或者笔误,还望看出端的能够不惜指点。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章