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

就目前来说Retrofit2使用的已相当的广泛,那么我们先来了解下两个问题:

1 . 什么是Retrofit?

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

2 . 我们为什么要使用Retrofit,它有哪些优势?

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

其次,Retrofit使用简单,结构层次分明,每一步都能清晰的表达出之所以要使用的寓意;

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

最后,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我自己学习的都心花怒放了。看完记住关注微信平台。

更多资讯请关注微信平台,有博客更新会及时通知。爱学习爱技术。

时间: 2024-10-23 15:56:02

Android 网络框架之Retrofit2使用详解及从源码中解析原理的相关文章

android WebView详解,常见漏洞详解和安全源码

这篇博客主要来介绍 WebView 的相关使用方法,常见的几个漏洞,开发中可能遇到的坑和最后解决相应漏洞的源码,以及针对该源码的解析. 由于博客内容长度,这次将分为上下两篇,上篇详解 WebView 的使用,下篇讲述 WebView 的漏洞和坑,以及修复源码的解析. 下篇:android WebView详解,常见漏洞详解和安全源码(下) 转载请注明出处:http://blog.csdn.net/self_study/article/details/54928371. 对技术感兴趣的同鞋加群 54

android WebView详解,常见漏洞详解和安全源码(下)

上篇博客主要分析了 WebView 的详细使用,这篇来分析 WebView 的常见漏洞和使用的坑. 上篇:android WebView详解,常见漏洞详解和安全源码(上) 转载请注明出处:http://blog.csdn.net/self_study/article/details/55046348 对技术感兴趣的同鞋加群 544645972 一起交流. WebView 常见漏洞 WebView 的漏洞也是不少,列举一些常见的漏洞,实时更新,如果有其他的常见漏洞,知会一下我-- WebView

Android事件传递机制详解及最新源码分析——ViewGroup篇

在上一篇<Android事件传递机制详解及最新源码分析--View篇>中,详细讲解了View事件的传递机制,没掌握或者掌握不扎实的小伙伴,强烈建议先阅读上一篇. 好了,废话还是少说,直奔主题,开始本篇的ViewGroup事件传递机制探索之旅. 依然从简单的Demo例子现象开始分析 新建安卓工程,首先自定义一个Button以及一个RelativeLayout,很简单,只是重写了主要与事件传递机制相关的方法,代码如下: 自定义WLButton类: 1 public class WLButton e

详解聚富彩票源码搭建 HTML表单与PHP

表单的的与PHP相关联的属性 action属性指向处理表单的PHP脚本. method属性接受两个参数,post/get;详解聚富彩票源码搭建bbs.yasewl.com请添加链接描述 post与get的差异 1.get会公开地将用户输入表单的信息发送给php脚本,不安全: 2.get传送的信息有限. 3.使用get方式的表单创建的页面能够被添加为书签,post不可以. 4.重载post访问的页面,会显示提示信息,get不会. 所以get主要用于从服务器强求数据的行为例如,搜索请求等,post用

Android应用ViewDragHelper详解及部分源码浅析

[工匠若水 http://blog.csdn.net/yanbober 未经允许严禁转载,请尊重作者劳动成果.私信联系我] 1 背景 很久没有更新博客了,忙里偷闲产出一篇.写这片文章主要是去年项目中的一个需求,当时三下五除二的将其实现了,但是源码的阅读却一直扔在那迟迟没有时间理会,现在拣起来看看吧,否则心里一直不踏实. 关于啥是ViewDragHelper,这里不再解释,官方下面这个解释已经很牛逼了,如下: /** * ViewDragHelper is a utility class for

Docker网络详解及pipework源码解读与实践

Docker作为目前最火的轻量级容器技术,有很多令人称道的功能,如Docker的镜像管理.然而,Docker同样有着很多不完善的地方,网络方面就是Docker比较薄弱的部分.因此,我们有必要深入了解Docker的网络知识,以满足更高的网络需求.本文首先介绍了Docker自身的4种网络工作方式,然后通过3个样例 -- 将Docker容器配置到本地网络环境中.单主机Docker容器的VLAN划分.多主机Docker容器的VLAN划分,演示了如何使用pipework帮助我们进行复杂的网络设置,以及pi

JPA入门案例详解(附源码)

1.新建JavaEE Persistence项目 2.导入相关Jar包 3.修改persistence.xml <?xml version="1.0" encoding="UTF-8"?> <persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0"> <persistence-unit name="N

Linux 下编译Android-VLC开源播放器详解(附源码下载)

这两天需要做音视频播放相关的东西,所以重新找了目前android下的解码库.Android自带的解码库支持不全,因此很多第三方播放器都是自带解码器,绝大部分都是使用FFMpeg作为解码库.我11年的时候也弄过视频播放器,当时也是基于FFMpeg来做.那时候网上有关Android视频解码库的资料不多,只在git上找到一个人移植FFMpeg,把它弄下来编译,有兴趣可以看看当时的文章:Android 视频播放器 faplayer 编译 . 言归正传,今天的主角是大名鼎鼎的VLC,做过视频播放器的人,应

Pipeline的入站流程详解(netty源码死磕7)

精进篇:netty源码死磕7  巧夺天工--Pipeline入站流程详解 1. Pipeline的入站流程 在讲解入站处理流程前,先脑补和铺垫一下两个知识点: (1)如何向Pipeline添加一个Handler节点 (2)Handler的出站和入站的区分方式 1.1. HandlerContext节点的添加 在Pipeline实例创建的同时,Netty为Pipeline创建了一个Head和一个Tail,并且建立好了链接关系. 代码如下: protected DefaultChannelPipel