Android 网络框架之Retrofit2使用详解及从源码中解析原理

就目前来讲Retrofit2使用的已至关的普遍,那么咱们先来了解下两个问题:java

1 . 什么是Retrofit?python

Retrofit是针对于Android/Java的、基于okHttp的、一种轻量级且安全的、并使用注解方式的网络请求框架。android

2 . 咱们为何要使用Retrofit,它有哪些优点?git

首先,Retrofit使用注解方式,大大简化了咱们的URL拼写形式,并且注解含义一目了然,简单易懂;github

其次,Retrofit使用简单,结构井井有条,每一步都能清晰的表达出之因此要使用的寓意;json

再者,Retrofit支持同步和异步执行,使得请求变得异常简单,只要调用enqueue/execute便可完成;api

最后,Retrofit更大自由度的支持咱们自定义的业务逻辑,如自定义Converters。缓存

好,知道了Retrofit是什么,有了哪些优点,如今咱们来学习下怎么使用。安全

一 Retrofit2使用详解:

在使用以前,你必须先导入必要的jar包,以androidStudio为例:服务器

添加依赖:

compile 'com.squareup.retrofit2:retrofit:2.0.2'
    compile 'com.squareup.retrofit2:converter-gson:2.0.2'

由于Retrofit2是依赖okHttp请求的,并且请查看它的META-INF->META-INF\maven\com.squareup.retrofit2\retrofit->pom.xml文件,

<dependencies>
    <dependency>
      <groupId>com.squareup.okhttp3</groupId>
      <artifactId>okhttp</artifactId>
    </dependency>
    ...
</dependencies>

因而可知,它确实是依赖okHttp,okHttp有会依赖okio因此它会制动的把这两个包也导入进来。

添加权限:

既然要请求网络,在咱们android手机上是必需要有访问网络的权限的,下面把权限添加进来

<uses-permission android:name="android.permission.INTERNET"/>

好了,下面开始介绍怎么使用Retrofit,既然它是使用注解的请求方式来完成请求URL的拼接,那么咱们就按注解的不一样来分别学习:

首先,咱们须要建立一个java接口,用于存放请求方法的:

public interface GitHubService {
}

而后逐步在该方法中添加咱们所须要的方法(按照请求方式):

1 :Get : 是咱们最多见的请求方法,它是用来获取数据请求的。

①:直接经过URL获取网络内容:

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

在这里咱们定义了一个listRepos()的方法,经过@GET注解标识为get请求,请求的URL为“users/octocat/repos”。

而后看看Retrofit是怎么调用的,代码以下:

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

GitHubService service = retrofit.create(GitHubService.class);
Call<List<Repo>> repos = service.listRepos();
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){

    }
});

代码解释:首先获取Retrofit对象,而后经过动态代理获取到所定义的接口,经过调用接口里面的方法获取到Call类型返回值,最后进行网络请求操做(这里不详细说明Retrofit 实现原理,后面会对它进行源码解析),这里必需要说的是请求URL的拼接:在构建Retrofit对象时调用baseUrl所传入一个String类型的地址,这个地址在调用service.listRepos()时会把@GET("users/octocat/repos")的URL拼接在尾部。

ok,这样就完成了,咱们此次的请求,可是咱们不能每次请求都要建立一个方法呀?这时咱们就会想起动态的构建URL了

②:动态获取URL地址:@Path

咱们再上面的基础上进行修改,以下:

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

这里在Get注解中包含{user},它所对应的是@Path注解中的“user”,它所标示的正是String user,而咱们再使用Retrofit对象动态代理的获取到GitHubService,当调用listRepos时,咱们就必须传入一个String类型的User,如:

...

Call<List<Repo>> repos = service.listRepos("octocat");

...

如上代码,其余的代码都是不变的,而咱们只须要使用@Path注解就彻底的实现了动态的URL地址了,是否是很方便呢,这还不算什么,一般状况下,咱们去获取一些网络信息,由于信息量太大,咱们会分类去获取,也就是携带一些必要的元素进行过滤,那咱们该怎么实现呢?其实也很简单,由于Retrofit已经为咱们封装好了注解,请看下面(官网实例):

③:动态指定条件获取信息:@Query

@GET("group/{id}/users")
Call<List<User>> groupList(@Path("id") int groupId, @Query("sort") String sort);

咱们只须要使用@Query注解便可完成咱们的需求,在@Query("sort")中,short就比如是URL请求地址中的键,而它说对应的String sort中的sort则是它的值。

可是咱们想,在网络请求中通常为了更精确的查找到咱们所须要的数据,过滤更多不须要的无关的东西,咱们每每须要携带多个请求参数,固然可使用@Query注解,可是太麻烦,很长,容易遗漏和出错,那有没有更简单的方法呢,有,固然后,咱们能够直接放到一个map键值对中:

④:动态指定条件组获取信息:@QueryMap

@GET("group/{id}/users")
Call<List<User>> groupList(@Path("id") int groupId, @QueryMap Map<String, String> options);

使用@QueryMap注解能够分别地从Map集合中获取到元素,而后进行逐个的拼接在一块儿。

ok,到这里,咱们使用@Get注解已经能够完成绝大部分的查询任务了,下面咱们再来看看另外一种经常使用的请求方式--post

2 POST : 一种用于携带传输数据的请求方式

稍微了解点Http的同窗们,可能都会知道:相对于get请求方式把数据存放在uri地址栏中,post请求传输的数据时存放在请求体中,因此post才能作到对数据的大小无限制。而在Retrofit中,它又是怎么使用的呢?请看下面:

①:携带数据类型为对象时:@Body

@POST("users/new")
Call<User> createUser(@Body User user);

当咱们的请求数据为某对象时Retrofit是这么处理使用的:

首先,Retrofit用@POST注解,标明这个是post的请求方式,里面是请求的url;
其次,Retrofit仿照http直接提供了@Body注解,也就相似于直接把咱们要传输的数据放在了body请求体中,这样应用能够更好的方便咱们理解。

来看下应用:

Call<List<User>> repos = service.createUser(new User(1, "管满满", "28", "http://write.blog.csdn.net/postlist"));

这样咱们直接把一个新的User对象利用注解@Body存放在body请求体,并随着请求的执行传输过去了。

可是有同窗在这该有疑问了,Retrofit就只能传输的数据为对象吗?固然不是,下面请看

②:携带数据类型为表单键值对时:@Field

@FormUrlEncoded
@POST("user/edit")
Call<User> updateUser(@Field("first_name") String first, @Field("last_name") String last);

当咱们要携带的请求数据为表单时,一般会以键值对的方式呈现,那么Retrofit也为咱们考虑了这种状况,它首先用到@FormUrlEncoded注解来标明这是一个表单请求,而后在咱们的请求方法中使用@Field注解来标示所对应的String类型数据的键,从而组成一组键值对进行传递。

那你是否是有该有疑问了,假如我是要上传一个文件呢?

③:单文件上传时:@Part

@Multipart
@PUT("user/photo")
Call<User> updateUser(@Part("photo") RequestBody photo, @Part("description") RequestBody description);

此时在上传文件时,咱们须要用@Multipart注解注明,它表示容许多个@Part,@Part则对应的一个RequestBody 对象,RequestBody 则是一个多类型的,固然也是包括文件的。下面看看使用

File file = new File(Environment.getExternalStorageDirectory(), "ic_launcher.png");
RequestBody photoRequestBody = RequestBody.create(MediaType.parse("image/png"), file);
RequestBody descriptionRequestBody = RequestBody.create(null, "this is photo.");

Call<User> call = service.updateUser(photoRequestBody, descriptionRequestBody);

这里咱们建立了两个RequestBody 对象,而后调用咱们定义的updateUser方法,并把RequestBody传递进入,这样就实现了文件的上传。是否是很简单呢?

相比单文件上传,Retrofit还进一步提供了多文件上传的方式:

④:多文件上传时:@PartMap

@Multipart
@PUT("user/photo")
Call<User> updateUser(@PartMap Map<String, RequestBody> photos, @Part("description") RequestBody description);

这里其实和单文件上传是差很少的,只是使用一个集合类型的Map封装了文件,并用@PartMap注解来标示起来。其余的都同样,这里就很少讲了。

3 Header : 一种用于携带消息头的请求方式

Http请求中,为了防止攻击或是过滤掉不安全的访问或是为添加特殊加密的访问等等以减轻服务器的压力和保证请求的安全,一般都会在消息头中携带一些特殊的消息头处理。Retrofit也为咱们提供了该请求方式:

@Headers("Cache-Control: max-age=640000")
@GET("widget/list")
Call<List<Widget>> widgetList();
-----------------------------------------------------------
@Headers({
    "Accept: application/vnd.github.v3.full+json",
    "User-Agent: Retrofit-Sample-App"
})
@GET("users/{username}")
Call<User> getUser(@Path("username") String username);

以上两种是静态的为Http请求添加消息头,只须要使用@Headers注解,以键值对的方式存放便可,若是须要添加多个消息头,则使用{}包含起来,如上所示。但要注意,即便有相同名字得消息头也不会被覆盖,并共同的存放在消息头中。

固然有静态添加那相对的也就有动态的添加消息头了,方法以下:

@GET("user")
Call<User> getUser(@Header("Authorization") String authorization)

使用@Header注解能够为一个请求动态的添加消息头,假如@Header对应的消息头为空的话,则会被忽略,不然会以它的.toString()方式输出。

ok,到这里已基本讲解完Retrofit的使用,还有两个重要但简单的方法也必须在这里提一下:

1 call.cancel();它能够终止正在进行的请求,程序只要一旦调用到它,无论请求是否在终止都会被中止掉。

2 call.clone();当你想要屡次请求一个接口的时候,直接用 clone 的方法来生产一个新的,不然将会报错,由于当你获得一个call实例,咱们调用它的 execute 方法,可是这个方法只能调用一次。屡次调用则发生异常。

好了,关于Retrofit的使用咱们就讲这么多,接下来咱们从源码的角度简单的解析下它的实现原理。

二 Retrofit2 从源码解析实现原理

首先先看一下Retrofit2标准示例

Retrofit retrofit = new Retrofit.Builder()
        .baseUrl(BASE_URL)
        .addConverterFactory(GsonConverterFactory.create())
        .build();
        
GitHubService service = retrofit.create(GitHubService.class);

service.enqueue();
service.execute();

由上面咱们基本能够看出,Retrofit是经过构造者模式建立出来的,那么咱们就来看看Builder这个构造器的源码:

public static final class Builder {
    ...
    Builder(Platform platform) {
      this.platform = platform;
      converterFactories.add(new BuiltInConverters());
    }

    public Builder() {
      this(Platform.get());
    }

    public Builder client(OkHttpClient client) {
      return callFactory(checkNotNull(client, "client == null"));
    }

    public Builder callFactory(okhttp3.Call.Factory factory) {
      this.callFactory = checkNotNull(factory, "factory == null");
      return this;
    }
    public Builder baseUrl(String baseUrl) {
      checkNotNull(baseUrl, "baseUrl == null");
      HttpUrl httpUrl = HttpUrl.parse(baseUrl);
      if (httpUrl == null) {
        throw new IllegalArgumentException("Illegal URL: " + baseUrl);
      }
      return baseUrl(httpUrl);
    }

    public Builder baseUrl(HttpUrl baseUrl) {
      checkNotNull(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;
    }

    public Builder addConverterFactory(Converter.Factory factory) {
      converterFactories.add(checkNotNull(factory, "factory == null"));
      return this;
    }

    public Builder addCallAdapterFactory(CallAdapter.Factory factory) {
      adapterFactories.add(checkNotNull(factory, "factory == null"));
      return this;
    }

    public Builder callbackExecutor(Executor executor) {
      this.callbackExecutor = checkNotNull(executor, "executor == null");
      return this;
    }

    public Builder validateEagerly(boolean validateEagerly) {
      this.validateEagerly = validateEagerly;
      return this;
    }

    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();
      }

      List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories);
      adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));

      List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);

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

源码讲解:

1:当咱们使用new Retrofit.Builder()来建立时,在Builder构造器中,首先就得到当前的设备平台信息,而且把内置的转换器工厂(BuiltInConverters)加添到工厂集合中,它的主要做用就是当使用多种Converters的时候可以正确的引导并找到能够消耗该类型的转化器。

2:从咱们的基本示例中看到有调用到.baseUrl(BASE_URL)这个方法,实际上没当使用Retrofit时,该方法都是必须传入的,而且还不能为空,从源码中能够看出,当baseUrl方法传进的参数来看,若是为空的话将会抛出NullPointerException空指针异常。

3:addConverterFactory该方法是传入一个转换器工厂,它主要是对数据转化用的,请网络请求获取的数据,将会在这里被转化成咱们所须要的数据类型,好比经过Gson将json数据转化成对象类型。

4 : 从源码中,咱们看到还有一个client方法,这个是可选的,若是没有传入则就默认为OkHttpClient,在这里能够对OkHttpClient作一些操做,好比添加拦截器打印log等

5:callbackExecutor该方法从名字上看能够得知应该是回调执行者,也就是Call对象从网络服务获取数据以后转换到UI主线程中。

6:addCallAdapterFactory该方法主要是针对Call转换了,好比对Rxjava的支持,从返回的call对象转化为Observable对象。

7:最后调用build()方法,经过new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
callbackExecutor, validateEagerly);构造方法把所须要的对象传递到Retrofit对象中。

ok,当咱们经过Builder构造器构造出Retrofit对象时,而后经过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, 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 serviceMethod = loadServiceMethod(method);
            OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
            return serviceMethod.callAdapter.adapt(okHttpCall);
          }
        });
  }

当看到Proxy时,是否是多少有点明悟了呢?没错就是动态代理,动态代理其实已经封装的很简单了,主要使用newProxyInstance()方法来返回一个类的代理实例,其中它内部须要传递一个类的加载器,类自己以及一个InvocationHandler处理器,主要的动做都是在InvocationHandler中进行的,它里面只有一个方法invoke()方法,每当咱们调用代理类里面的方法时invoke()都会被执行,而且咱们能够从该方法的参数中获取到所须要的一切信息,好比从method中获取到方法名,从args中获取到方法名中的参数信息等。

而Retrofit在这里使用到动态代理也不会例外:

首先,经过method把它转换成ServiceMethod ;

而后,经过serviceMethod, args获取到okHttpCall 对象;

最后,再把okHttpCall进一步封装并返回Call对象。

下面来逐步详解。

1:将method把它转换成ServiceMethod

ServiceMethod serviceMethod = loadServiceMethod(method);

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

loadServiceMethod源码方法中很是的好理解,主要就是经过ServiceMethod.Builder()方法来构建ServiceMethod,并把它给缓存取来,以便下次能够直接回去ServiceMethod。那下面咱们再来看看它是怎么构建ServiceMethod方法的:

public Builder(Retrofit retrofit, Method method) {
      this.retrofit = retrofit;
      this.method = method;
      this.methodAnnotations = method.getAnnotations();
      this.parameterTypes = method.getGenericParameterTypes();
      this.parameterAnnotationsArray = method.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();
      ...
      return new ServiceMethod<>(this);
    }

首先在Builder()中初始化一些参数,而后在build()中返回一个new ServiceMethod<>(this)对象。

下面来详细的解释下build()方法,彻底理解了该方法则便于理解下面的全部执行流程。

①:构建CallAdapter对象,该对象将会在第三步中起着相当重要的做用。

如今咱们先看看它是怎么构建CallAdapter对象的:createCallAdapter()方法源码以下:

private CallAdapter<?> createCallAdapter() {
      Type returnType = method.getGenericReturnType();
      if (Utils.hasUnresolvableType(returnType)) {
        throw methodError(
            "Method return type must not include a type variable or wildcard: %s", returnType);
      }
      if (returnType == void.class) {
        throw methodError("Service methods cannot return void.");
      }
      Annotation[] annotations = method.getAnnotations();
      try {
        return retrofit.callAdapter(returnType, annotations);
      } catch (RuntimeException e) { // Wide exception range because factories are user code.
        throw methodError(e, "Unable to create call adapter for %s", returnType);
      }
    }

在createCallAdapter方法中主要作的是事情就是获取到method的类型和注解,而后调用retrofit.callAdapter(returnType, annotations);方法:

public CallAdapter<?> callAdapter(Type returnType, Annotation[] annotations) {
    return nextCallAdapter(null, returnType, annotations);
  }

public CallAdapter<?> nextCallAdapter(CallAdapter.Factory skipPast, Type returnType,
      Annotation[] annotations) {
    checkNotNull(returnType, "returnType == null");
    checkNotNull(annotations, "annotations == null");

    int start = adapterFactories.indexOf(skipPast) + 1;
    for (int i = start, count = adapterFactories.size(); i < count; i++) {
      CallAdapter<?> adapter = adapterFactories.get(i).get(returnType, annotations, this);
      if (adapter != null) {
        return adapter;
      }
    }
    ...
}

展转到Retrofit中nextCallAdapter()中,在for 循环中分别从adapterFactories中来获取CallAdapter对象,可是adapterFactories中有哪些CallAdapter对象呢,这就须要返回到构建Retrofit对象中的Builder 构造器中查看了

public static final class Builder {
    ...
    public Builder addCallAdapterFactory(CallAdapter.Factory factory) {
      adapterFactories.add(checkNotNull(factory, "factory == null"));
      return this;
    }
    ...
    public Retrofit build() {
      List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories);
      adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));
    ...
    }  
  }

CallAdapter.Factory defaultCallAdapterFactory(Executor callbackExecutor) {
    if (callbackExecutor != null) {
      return new ExecutorCallAdapterFactory(callbackExecutor);
    }
    return DefaultCallAdapterFactory.INSTANCE;
  }

从上面的代码中能够看到,无论有没有经过addCallAdapterFactory添加CallAdapter,adapterFactories集合至少都会有一个ExecutorCallAdapterFactory对象。当咱们从adapterFactories集合中回去CallAdapter对象时,那咱们都会得到ExecutorCallAdapterFactory这个对象。而这个对象将在第三步中和后面执行同步或异步请求时起着相当重要的做用。

②:构建responseConverter转换器对象,它的做用是寻找适合的数据类型转化

该对象的构建和构建CallAdapter对象的流程基本是一致的,这里就不在赘述。同窗们可自行查看源码。

2:经过serviceMethod, args获取到okHttpCall 对象

第二步相对比较简单,就是对象传递:

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

3:把okHttpCall进一步封装并返回Call对象

这一步也是一句话 return serviceMethod.callAdapter.adapt(okHttpCall);可是想理解清楚必须先把第一步理解透彻,经过第一步咱们找获得serviceMethod.callAdapter就是ExecutorCallAdapterFactory对象,那么调用.adapt(okHttpCall)把okHttpCall怎么进行封装呢?看看源码:

<R> T adapt(Call<R> call);

一看,吓死宝宝了,就这么一句,这是嘛呀,可是通过第一步的分析,咱们已知道serviceMethod.callAdapter就是ExecutorCallAdapterFactory,那么咱们能够看看在ExecutorCallAdapterFactory类中有没有发现CallAdapter的另类应用呢,一看,果不其然在重写父类的get()方法中咱们找到了答案:

@Override
  public CallAdapter<Call<?>> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
    if (getRawType(returnType) != Call.class) {
      return null;
    }
    final Type responseType = Utils.getCallResponseType(returnType);
    return new CallAdapter<Call<?>>() {
      @Override public Type responseType() {
        return responseType;
      }

      @Override public <R> Call<R> adapt(Call<R> call) {
        return new ExecutorCallbackCall<>(callbackExecutor, call);
      }
    };
  }

当看到return new CallAdapter中的adapt(Call call)咱们就彻底知其因此然了,至于ExecutorCallbackCall怎么应用的咱们在发起网络请求的时候讲解。

ok,当咱们获得接口的代理实例以后,经过代理接口调用里面的方法,就会触发InvocationHandler对象中的invoke方法,从而完成上面的三个步骤而且返回一个Call对象,经过Call对象就能够去完成咱们的请求了,Retrofit为咱们提供两种请求方式,一种是同步,一种是异步。咱们这里就以异步方式来说解:

service.enqueue(new Callback<List<User>>() {
    @Override
    public void onResponse(Call<List<User>> call, Response<List<User>> response) {
        Log.d("response body",response.body());
    }

    @Override
    public void onFailure(Call<BoardInfo> call, Throwable t) {
        Log.i("response Throwable",t.getMessage().toString());
    }
});

从上面咱们能够看到enqueue方法中有一个回调函数,回调函数里面重写了两个方法分别表明请求成功和失败的方法,可是咱们想知道它是怎么实现的原理呢?那么请往下面看:

在上面获取接口的代理实例时,经过代理接口调用里面的方法获取一个Call对象,咱们上面也分析了其实这个Call对象就是ExecutorCallbackCall,那么咱们来看看它里面是怎么实现的?

static final class ExecutorCallbackCall<T> implements Call<T> {
    final Executor callbackExecutor;
    final Call<T> delegate;

    ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) {
      this.callbackExecutor = callbackExecutor;
      this.delegate = delegate;
    }

    @Override public void enqueue(final Callback<T> callback) {
      if (callback == null) throw new NullPointerException("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()) {
                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);
            }
          });
        }
      });
    }

在ExecutorCallbackCall类中,封装了两个对象一个是callbackExecutor,它主要是把如今运行的线程切换到主线程中去,一个是delegate对象,这个对象就是真真正正的执行网络操做的对象,那么它的真身究竟是什么呢?还记得咱们在获取代理接口第三步执行的serviceMethod.callAdapter.adapt(okHttpCall)的分析吧,通过展转几步终于把okHttpCall传递到了new ExecutorCallbackCall<>(callbackExecutor, call);中,而后看看ExecutorCallbackCall的构造方法:

ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) {
      this.callbackExecutor = callbackExecutor;
      this.delegate = delegate;
    }

由此能够明白delegate 就是okHttpCall对象,那么咱们在看看okHttpCall是怎么执行异步网络请求的:

@Override 
public void enqueue(final Callback<T> callback) {
    if (callback == null) throw new NullPointerException("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;
        }
      }
    }

    if (failure != null) {
      callback.onFailure(this, failure);
      return;
    }

    if (canceled) {
      call.cancel();
    }

    call.enqueue(new okhttp3.Callback() {
      @Override 
      public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse)
          throws IOException {
        Response<T> response;
        try {
          response = parseResponse(rawResponse);
        } catch (Throwable e) {
          callFailure(e);
          return;
        }
        callSuccess(response);
      }

      @Override 
      public void onFailure(okhttp3.Call call, IOException e) {
        try {
          callback.onFailure(OkHttpCall.this, e);
        } catch (Throwable t) {
          t.printStackTrace();
        }
      }

      private void callFailure(Throwable e) {
        try {
          callback.onFailure(OkHttpCall.this, e);
        } catch (Throwable t) {
          t.printStackTrace();
        }
      }

      private void callSuccess(Response<T> response) {
        try {
          callback.onResponse(OkHttpCall.this, response);
        } catch (Throwable t) {
          t.printStackTrace();
        }
      }
    });
  }

从上面代码中,咱们很容易就看出,其实它就是这里面封装了一个okhttp3.Call,直接利用okhttp进行网络的异步操做,至于okhttp是怎么进行网络请求的咱们就再也不这里讲解了,感兴趣的朋友能够本身去查看源码。

好了,到这里整个Retrofit的实现原理基本已解析完毕,相信你们学习过都可以很好的掌握了。ok,今天就讲到这里吧,原本还打算再写个实例放上来的,看看篇幅也就放弃了,实战部分会在下一篇和Rxjava一块儿放出来,Rxjava我本身学习的都心花盛开了。看完记住关注微信平台。

更多资讯请关注微信平台,有博客更新会及时通知。爱学习爱技术。
这里写图片描述

相关文章
相关标签/搜索