Retrofit2.0源碼解析

歡迎訪問我的個(gè)人博客 槐瑞,原文鏈接:http://wensibo.net/2017/09/05/retrofit/ 对扶,未經(jīng)允許不得轉(zhuǎn)載!


今天是九月的第四天了,學(xué)校也正式開學(xué)是整,趁著大學(xué)最后一年的這大好時(shí)光瞧剖,抓緊時(shí)間趕快學(xué)習(xí)新知識(shí)吧拭嫁!今天想要與大家一起分享的是Retrofit可免,由于網(wǎng)上已經(jīng)有許多講解Retrofit使用的文章了,本篇文章只會(huì)給一個(gè)小小的示例做粤,以這個(gè)示例作為入口分析其源碼浇借,同樣也會(huì)貼上流程圖,以免迷路怕品。話不多說妇垢,我們開始吧!H饪怠闯估!

關(guān)于Retrofit

簡介

Retrofit是近來十分火熱的一個(gè)網(wǎng)絡(luò)請(qǐng)求開源庫,Android開發(fā)者使用的網(wǎng)絡(luò)請(qǐng)求開源庫從最早的HttpClient與HttpURLConnection到2013年Google官方推出的Volley吼和,接著就到了現(xiàn)在很火的OKHttp涨薪,最后才到了Retrofit。網(wǎng)絡(luò)請(qǐng)求開源庫的演變也正是移動(dòng)互聯(lián)網(wǎng)下用戶對(duì)網(wǎng)絡(luò)需求的真實(shí)寫照纹安。有哪個(gè)用戶不想使用APP的時(shí)候網(wǎng)絡(luò)加載速度更快尤辱,更省流量,更加安全呢厢岂?也就是基于用戶的這些需求光督,才有了許多開源庫的不斷迭代,而Retrofit可以說正是當(dāng)下最適合開發(fā)者使用的網(wǎng)絡(luò)請(qǐng)求開源庫之一塔粒。
何出此言呢结借?首先它是由大名鼎鼎的square公司出品的,或許你不知道square公司卒茬,但你應(yīng)該認(rèn)識(shí)Jake Wharton船老,不過他最近已經(jīng)到谷歌去了,倘若你連他都不知道圃酵,那你應(yīng)該使用過他開發(fā)的這些開源庫:OkHttp,picasso,butterknife,RxAndroid等等柳畔,可以說Retrofit是由一個(gè)十分厲害的公司開發(fā)和維護(hù)的,所以你大可以放心地在你的項(xiàng)目中使用郭赐。

什么場(chǎng)景下適合使用呢薪韩?

盡管Retrofit十分強(qiáng)大,但是他卻不一定適合所有場(chǎng)景捌锭,正所謂術(shù)業(yè)有專攻俘陷,我們也不必大材小用,如果是一些頻繁但是訪問量很小的網(wǎng)絡(luò)請(qǐng)求观谦,那么Volley就足以對(duì)付了拉盾,接下來我列舉一下Retrofit普遍的使用場(chǎng)景。

  • 服務(wù)器后臺(tái)遵循RESTful API的設(shè)計(jì)風(fēng)格豁状。如果你對(duì)這種風(fēng)格不熟悉捉偏,建議你看看阮一峰大神的這篇文章倒得,或者向你的后臺(tái)小伙伴請(qǐng)教一番。
  • 項(xiàng)目中使用了RxJava夭禽。如果你的項(xiàng)目中使用了RxJava屎暇,那么使用Retrofit絕對(duì)會(huì)讓你的開發(fā)效率翻倍。
  • 項(xiàng)目中的網(wǎng)絡(luò)數(shù)據(jù)請(qǐng)求量比較大驻粟。如果你的應(yīng)用經(jīng)常會(huì)有數(shù)據(jù)量比較大的網(wǎng)絡(luò)請(qǐng)求,那么使用Retrofit也會(huì)很有效凶异,因?yàn)镽etrofit底層的實(shí)現(xiàn)是使用OKHttp蜀撑,而OKHttp就是適用于這種場(chǎng)景的。

如果你符合以上三種情況剩彬,當(dāng)然是選擇Retrofit啦酷麦!


Retrofit綠

一個(gè)簡單的栗子

說了這么多,我們就通過下面這個(gè)栗子來看看他究竟好在哪里喉恋?
需要說明的是:這個(gè)例子是用來獲取干貨集中營API上面的數(shù)據(jù)
1沃饶、首先定義一個(gè)常量用來描述要訪問的服務(wù)器主機(jī)的地址

public class GankConfig {
    public static final String HOST = "http://gank.io/api/";
}

2、定義返回?cái)?shù)據(jù)的bean類

public class GankData {
    public List<String> category;
    public Result results;

    public class Result{
            @SerializedName("Android")
            public List<Gank> androidList;
            @SerializedName("休息視頻")
            public List<Gank> restVideoList;
            @SerializedName("iOS")
            public List<Gank> iosList;
            @SerializedName("福利")
            public List<Gank> meiZiList;
            @SerializedName("拓展資源")
            public List<Gank> extendResourceList;
            @SerializedName("瞎推薦")
            public List<Gank> suggestionList;
            @SerializedName("App")
            public List<Gank> appList;
            @SerializedName("前端")
            public List<Gank> webList;
    }
}

3轻黑、定義要訪問的接口

public interface GankRetrofit {

    //這里以獲取指定日期的內(nèi)容為例子
    @GET("day/{year}/{month}/{day}")
    GankData getDailyData(@Path("year") int year, @Path("month") int month, @Path("day") int day);

}

4糊肤、用單例模式創(chuàng)建一個(gè)Retrofit客戶端

public class GankRetrofitClient {
        private volatile static GankRetrofit gankRetrofit;
        private static Retrofit retrofit;

        private GankRetrofitClient(){}

        static{
                Gson date_gson = new GsonBuilder().setDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'").create();
                retrofit = new Retrofit.Builder()
                        .baseUrl(GankConfig.HOST)
                        .addConverterFactory(GsonConverterFactory.create(date_gson))//添加一個(gè)轉(zhuǎn)換器,將gson數(shù)據(jù)轉(zhuǎn)換為bean類
                        .addCallAdapterFactory(RxJava2CallAdapterFactory.create())//添加一個(gè)適配器氓鄙,與RxJava配合使用
                        .build();
        }

        public static GankRetrofit getGankRetrofitInstance() {
                if (gankRetrofit==null){
                        synchronized (GankRetrofitClient.class){
                                if (gankRetrofit==null){
                                        gankRetrofit=retrofit.create(GankRetrofit.class);
                                }
                        }
                }
                return gankRetrofit;
        }
}

5馆揉、使用Retrofit進(jìn)行網(wǎng)絡(luò)請(qǐng)求

GankData data= GankRetrofitClient.getGankRetrofitInstance().getDailyData(2017, 9, 1);

源碼解析

從Builder模式創(chuàng)建實(shí)例開始看起

首先我們先從上面的第4步開始解析源碼,有下面這段代碼:

retrofit = new Retrofit.Builder()
            .baseUrl(GankConfig.HOST)
            .addConverterFactory(GsonConverterFactory.create(date_gson))//添加一個(gè)轉(zhuǎn)換器抖拦,將gson數(shù)據(jù)轉(zhuǎn)換為bean類
            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())//添加一個(gè)適配器升酣,與RxJava配合使用
            .build();

很明顯這個(gè)是使用了Builder模式,接下來我們一步一步來看里面做了什么态罪?首先是Builder()噩茄。

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

Builder(Platform platform) {
      this.platform = platform;
      //添加轉(zhuǎn)換器,請(qǐng)見下面關(guān)于addConverterFactory()的講解
      converterFactories.add(new BuiltInConverters());
    }

構(gòu)造方法中的參數(shù)是Platform的靜態(tài)方法get()复颈,接下來就看看get()绩聘。

private static final Platform PLATFORM = findPlatform();

  static Platform get() {
    return PLATFORM;
  }

  private static Platform findPlatform() {
    try {
      Class.forName("android.os.Build");
      if (Build.VERSION.SDK_INT != 0) {
        return new Android();
      }
    } catch (ClassNotFoundException ignored) {
    }
    try {
      Class.forName("java.util.Optional");
      return new Java8();
    } catch (ClassNotFoundException ignored) {
    }
    return new Platform();
  }
}

可以看到,Retrofit支持多平臺(tái)券膀,包括Android與JAVA8君纫,它會(huì)根據(jù)不同的平臺(tái)設(shè)置不同的線程池。
先來看看到目前為止我們分析到哪里了

接下來看一下baseUrl()方法芹彬。

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

很容易理解蓄髓,baseUrl()是配置服務(wù)器的地址的,如果為空舒帮,那么就會(huì)拋出異常会喝。

接著是addConverterFactory()

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

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

大家是不是還記得剛才在Builder()方法初始化的時(shí)候陡叠,有這樣一行代碼:

converterFactories.add(new BuiltInConverters());

可以看到,converterFactories在初始化的時(shí)候就已經(jīng)添加了一個(gè)默認(rèn)的Converter肢执,那我們手動(dòng)添加的這個(gè)GsonConverter是干什么用的呢枉阵?

public final class GsonConverterFactory extends Converter.Factory {
 
  public static GsonConverterFactory create() {
    return create(new Gson());
  }

  
  public static GsonConverterFactory create(Gson gson) {
    return new GsonConverterFactory(gson);
  }

  private final Gson gson;

  private GsonConverterFactory(Gson gson) {
    if (gson == null) throw new NullPointerException("gson == null");
    this.gson = gson;
  }

  @Override
  public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
      Retrofit retrofit) {
    TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
    return new GsonResponseBodyConverter<>(gson, adapter);
  }

  @Override
  public Converter<?, RequestBody> requestBodyConverter(Type type,
      Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
    TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
    return new GsonRequestBodyConverter<>(gson, adapter);
  }
}

其實(shí)這個(gè)Converter主要的作用就是將HTTP返回的數(shù)據(jù)解析成Java對(duì)象,我們常見的網(wǎng)絡(luò)傳輸數(shù)據(jù)有Xml预茄、Gson兴溜、protobuf等等,而GsonConverter就是將Gson數(shù)據(jù)轉(zhuǎn)換為我們的Java對(duì)象耻陕,而不用我們重新去解析這些Gson數(shù)據(jù)拙徽。

接著看addCallAdapterFactory()

private final List<CallAdapter.Factory> adapterFactories = new ArrayList<>();

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

可以看到,CallAdapter同樣也被一個(gè)List維護(hù)诗宣,也就是說用戶可以添加多個(gè)CallAdapter膘怕,那Retrofit總得有一個(gè)默認(rèn)的吧,默認(rèn)的是什么呢召庞?請(qǐng)看接下來的build()岛心。

最后看一下build()

public Retrofit build() {
  //檢驗(yàn)baseUrl
  if (baseUrl == null) {
    throw new IllegalStateException("Base URL required.");
  }

  //創(chuàng)建一個(gè)call,默認(rèn)情況下使用okhttp作為網(wǎng)絡(luò)請(qǐng)求器
  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);
  //添加一個(gè)默認(rèn)的callAdapter
  adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));

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

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

首先Retrofit會(huì)新建一個(gè)call篮灼,其實(shí)質(zhì)就是OKHttp忘古,作用就是網(wǎng)絡(luò)請(qǐng)求器;接著在上一點(diǎn)中我們困惑的callAdapter也已經(jīng)能夠得到解決了诅诱,首先Retrofit有一個(gè)默認(rèn)的callAdapter存皂,請(qǐng)看下面這段代碼:

adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));

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

final class ExecutorCallAdapterFactory extends CallAdapter.Factory {
  final Executor callbackExecutor;

  ExecutorCallAdapterFactory(Executor callbackExecutor) {
    this.callbackExecutor = callbackExecutor;
  }

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

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

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

    @Override public boolean isExecuted() {
      return delegate.isExecuted();
    }

    @Override public Response<T> execute() throws IOException {
      return delegate.execute();
    }

    @Override public void cancel() {
      delegate.cancel();
    }

    @Override public boolean isCanceled() {
      return delegate.isCanceled();
    }

    @SuppressWarnings("CloneDoesntCallSuperClone") // Performing deep clone.
    @Override public Call<T> clone() {
      return new ExecutorCallbackCall<>(callbackExecutor, delegate.clone());
    }

    @Override public Request request() {
      return delegate.request();
    }
  }
}

可以看到默認(rèn)的callAdapter是ExecutorCallAdapterFactory。callAdapter其實(shí)也是運(yùn)用了適配器模式逢艘,其實(shí)質(zhì)就是網(wǎng)絡(luò)請(qǐng)求器Call的適配器旦袋,而在Retrofit中Call就是指OKHttp,那么CallAdapter就是用來將OKHttp適配給不同的平臺(tái)的它改,在Retrofit中提供了四種CallAdapter疤孕,分別如下:

  • ExecutorCallAdapterFactory(默認(rèn)使用)
  • GuavaCallAdapterFactory
  • Java8CallAdapterFactory
  • RxJavaCallAdapterFactory

為什么要提供如此多的適配器呢?首先是易于擴(kuò)展央拖,例如用戶習(xí)慣使用什么適配器祭阀,只需要添加即可使用;再者RxJava如此火熱鲜戒,因?yàn)槠淝袚Q線程十分的方便专控,不需要手動(dòng)使用handler切換線程,而Retrofit使用了支持RxJava的適配器之后遏餐,功能也會(huì)更加強(qiáng)大伦腐。

綜上我們已經(jīng)將使用Builder模式創(chuàng)建出來的Retrofit實(shí)例分析完畢了,我們只需要對(duì)相關(guān)的功能進(jìn)行配置即可失都,Retrofit負(fù)責(zé)接收我們配置的功能然后進(jìn)行對(duì)象的初始化柏蘑,這個(gè)也就是Builder模式屏蔽掉創(chuàng)建對(duì)象的復(fù)雜過程的好處⌒叶常現(xiàn)在我們?cè)俅斡昧鞒虉D來梳理一下剛才的思路。


網(wǎng)絡(luò)請(qǐng)求接口的創(chuàng)建

我最初使用Retrofit的時(shí)候覺得有一個(gè)地方十分神奇咳焚,如下:

GankRetrofit gankRetrofit=retrofit.create(GankRetrofit.class);
GankData data= gankRetrofit.getDailyData(2017, 9, 1);

要想解惑洽损,首先得對(duì)動(dòng)態(tài)代理有所了解,如果你對(duì)動(dòng)態(tài)代理還不是很清楚革半,請(qǐng)點(diǎn)擊這里了解動(dòng)態(tài)代理的原理碑定,之后再接著往下看。

前方高能預(yù)警

我們就以這里為切入點(diǎn)開始分析吧又官!首先是create()

public <T> T create(final Class<T> service) {
    Utils.validateServiceInterface(service);
    if (validateEagerly) {
      eagerlyValidateMethods(service);
    }
    //重點(diǎn)看這里
    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);
            }
            //下面就會(huì)講到哦
            ServiceMethod<Object, Object> serviceMethod =
                (ServiceMethod<Object, Object>) loadServiceMethod(method);

            //下一小節(jié)講到哦
            OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
            
            //下兩個(gè)小節(jié)講哦
            return serviceMethod.callAdapter.adapt(okHttpCall);
          }
        });
  }

我們主要看Proxy.newProxyInstance方法不傅,它接收三個(gè)參數(shù),第一個(gè)是一個(gè)類加載器赏胚,其實(shí)哪個(gè)類的加載器都無所謂,這里為了方便就選擇了我們所定義的借口的類加載器;第二個(gè)參數(shù)是我們定義的接口的class對(duì)象商虐,第三個(gè)則是一個(gè)InvocationHandler匿名內(nèi)部類。
那大家應(yīng)該會(huì)有疑問了,這個(gè)newProxyInstance到底有什么用呢斩芭?其實(shí)他就是通過動(dòng)態(tài)代理生成了網(wǎng)絡(luò)請(qǐng)求接口的代理類桅锄,代理類生成之后,接下來我們就可以使用ankRetrofit.getDailyData(2017, 9, 1);這樣的語句去調(diào)用getDailyData方法叮趴,當(dāng)我們調(diào)用這個(gè)方法的時(shí)候就會(huì)被動(dòng)態(tài)代理攔截割笙,直接進(jìn)入InvocationHandler的invoke方法。下面就來講講它眯亦。

invoke方法
它接收三個(gè)參數(shù)伤溉,第一個(gè)是動(dòng)態(tài)代理,第二個(gè)是我們要調(diào)用的方法妻率,這里就是指getDailyData乱顾,第三個(gè)是一個(gè)參數(shù)數(shù)組,同樣的這里就是指2017, 9, 1宫静,收到方法名和參數(shù)之后走净,緊接著會(huì)調(diào)用loadServiceMethod方法來生產(chǎn)過一個(gè)ServiceMethod對(duì)象,這里的一個(gè)ServiceMethod對(duì)象就對(duì)應(yīng)我們?cè)诰W(wǎng)絡(luò)接口里定義的一個(gè)方法孤里,相當(dāng)于做了一層封裝伏伯。接下來重點(diǎn)來看loadServiceMethod方法。

loadServiceMethod方法

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

它調(diào)用了ServiceMethod類捌袜,而ServiceMethod也使用了Builder模式说搅,直接先看Builder方法。

    Builder(Retrofit retrofit, Method method) {
      this.retrofit = retrofit;
      //獲取接口中的方法名
      this.method = method;

      //獲取方法里的注解
      this.methodAnnotations = method.getAnnotations();

      //獲取方法里的參數(shù)類型 
      this.parameterTypes = method.getGenericParameterTypes();

      //獲取接口方法里的注解內(nèi)容 
      this.parameterAnnotationsArray = method.getParameterAnnotations();
    }

再來看build方法

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

      if (httpMethod == null) {
        throw methodError("HTTP method annotation is required (e.g., @GET, @POST, etc.).");
      }

      if (!hasBody) {
        if (isMultipart) {
          throw methodError(
              "Multipart can only be specified on HTTP methods with request body (e.g., @POST).");
        }
        if (isFormEncoded) {
          throw methodError("FormUrlEncoded can only be specified on HTTP methods with "
              + "request body (e.g., @POST).");
        }
      }

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

      if (relativeUrl == null && !gotUrl) {
        throw methodError("Missing either @%s URL or @Url parameter.", httpMethod);
      }
      if (!isFormEncoded && !isMultipart && !hasBody && gotBody) {
        throw methodError("Non-body HTTP method cannot contain @Body.");
      }
      if (isFormEncoded && !gotField) {
        throw methodError("Form-encoded method must contain at least one @Field.");
      }
      if (isMultipart && !gotPart) {
        throw methodError("Multipart method must contain at least one @Part.");
      }

      return new ServiceMethod<>(this);
    }

代碼稍微有點(diǎn)長虏等,但是思路很清晰蜓堕,主要的工作有
1抛虏、首先對(duì)注解的合法性進(jìn)行檢驗(yàn),例如套才,HTTP的請(qǐng)求方法是GET還是POST迂猴,如果不是就會(huì)拋出異常;
2背伴、根據(jù)方法的返回值類型和方法注解從Retrofit對(duì)象的的callAdapter列表和Converter列表中分別獲取到該方法對(duì)應(yīng)的callAdapter和Converter沸毁;
3、將傳遞進(jìn)來的參數(shù)與注解封裝在parameterHandlers中傻寂,為后面的網(wǎng)絡(luò)請(qǐng)求做準(zhǔn)備息尺。

先用流程圖梳理一下剛才的思路:



分析到這里,我們總算是明白了最初的兩行代碼原來干了這么多事情疾掰,J神真的是流弊奥в!接下來我們就來看一下網(wǎng)絡(luò)請(qǐng)求部分静檬。

使用OkHttpCall進(jìn)行網(wǎng)絡(luò)請(qǐng)求

回頭看一下上一小節(jié)講解create方法時(shí)我們有這一行代碼:

OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);

他將我們剛才得到的serviceMethod與我們實(shí)際傳入的參數(shù)傳遞給了OkHttpCall炭懊,接下來就來瞧瞧這個(gè)類做了些什么?

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

  private volatile boolean canceled;

  // All guarded by this.
  private okhttp3.Call rawCall;
  private Throwable creationFailure; // Either a RuntimeException or IOException.
  private boolean executed;

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

很可惜拂檩,我們好像沒有看到比較有用的東西侮腹,只是將傳進(jìn)來的參數(shù)進(jìn)行了賦值,那我們就接著看create方法中的最后一行吧稻励!

callAdapter的使用

create方法的最后一行是這樣的:

return serviceMethod.callAdapter.adapt(okHttpCall);

最后是調(diào)用了callAdapter的adapt方法父阻,上面我們講到Retrofit在決定使用什么callAdapter的時(shí)候是看我們?cè)诮涌谥卸x的方法的返回值的,而在我們的例子中使用的是RxJava2CallAdapter望抽,因此我們就直接看該類中的adapt方法吧加矛!

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

    if (isFlowable) {
      return observable.toFlowable(BackpressureStrategy.LATEST);
    }
    if (isSingle) {
      return observable.singleOrError();
    }
    if (isMaybe) {
      return observable.singleElement();
    }
    if (isCompletable) {
      return observable.ignoreElements();
    }
    return observable;
  }

首先在adapt方法中會(huì)先判斷是同步請(qǐng)求還是異步請(qǐng)求,這里我們以同步請(qǐng)求為例煤篙,直接看CallExecuteObservable荒椭。

final class CallExecuteObservable<T> extends Observable<Response<T>> {
  private final Call<T> originalCall;

  CallExecuteObservable(Call<T> originalCall) {
    this.originalCall = originalCall;
  }

  @Override protected void subscribeActual(Observer<? super Response<T>> observer) {
    // Since Call is a one-shot type, clone it for each new observer.
    Call<T> call = originalCall.clone();
    observer.onSubscribe(new CallDisposable(call));

    boolean terminated = false;
    try {
      //重點(diǎn)看這里
      Response<T> response = call.execute();
      if (!call.isCanceled()) {
        observer.onNext(response);
      }
      if (!call.isCanceled()) {
        terminated = true;
        observer.onComplete();
      }
    } catch (Throwable t) {
      Exceptions.throwIfFatal(t);
      if (terminated) {
        RxJavaPlugins.onError(t);
      } else if (!call.isCanceled()) {
        try {
          observer.onError(t);
        } catch (Throwable inner) {
          Exceptions.throwIfFatal(inner);
          RxJavaPlugins.onError(new CompositeException(t, inner));
        }
      }
    }
  }

  private static final class CallDisposable implements Disposable {
    private final Call<?> call;

    CallDisposable(Call<?> call) {
      this.call = call;
    }

    @Override public void dispose() {
      call.cancel();
    }

    @Override public boolean isDisposed() {
      return call.isCanceled();
    }
  }
}

在subscribeActual方法中去調(diào)用了OKHttpCall的execute方法開始進(jìn)行網(wǎng)絡(luò)請(qǐng)求,網(wǎng)絡(luò)請(qǐng)求完畢之后舰蟆,會(huì)通過RxJava的操作符對(duì)返回來的數(shù)據(jù)進(jìn)行轉(zhuǎn)換趣惠,并進(jìn)行線程的切換,至此身害,Retrofit的一次使用也就結(jié)束了味悄。最后我們?cè)儆靡粡埻暾牧鞒虉D總結(jié)上述的幾個(gè)過程。


后記

相信通過上面的詳解塌鸯,大家對(duì)Retrofit應(yīng)該有了一個(gè)比較全面的認(rèn)識(shí)侍瑟,與其說它是一個(gè)網(wǎng)絡(luò)請(qǐng)求框架不如說他做了一層封裝,使得我們能夠更方便的間接使用了RxJava與OkHttp。從某種意義上來講我們從源碼中更應(yīng)該學(xué)習(xí)其對(duì)設(shè)計(jì)模式的正確運(yùn)用涨颜,使得整個(gè)框架的耦合度大大降低费韭,調(diào)用者也使用得更加簡潔。最后希望這篇文章能夠?qū)Υ蠹业拿嬖囉兴鶐椭?/p>

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末庭瑰,一起剝皮案震驚了整個(gè)濱河市星持,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌弹灭,老刑警劉巖督暂,帶你破解...
    沈念sama閱讀 217,185評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異穷吮,居然都是意外死亡逻翁,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門捡鱼,熙熙樓的掌柜王于貴愁眉苦臉地迎上來八回,“玉大人,你說我怎么就攤上這事驾诈〔纾” “怎么了?”我有些...
    開封第一講書人閱讀 163,524評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵翘鸭,是天一觀的道長。 經(jīng)常有香客問我戳葵,道長就乓,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,339評(píng)論 1 293
  • 正文 為了忘掉前任拱烁,我火速辦了婚禮生蚁,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘戏自。我一直安慰自己邦投,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,387評(píng)論 6 391
  • 文/花漫 我一把揭開白布擅笔。 她就那樣靜靜地躺著志衣,像睡著了一般。 火紅的嫁衣襯著肌膚如雪猛们。 梳的紋絲不亂的頭發(fā)上念脯,一...
    開封第一講書人閱讀 51,287評(píng)論 1 301
  • 那天,我揣著相機(jī)與錄音弯淘,去河邊找鬼绿店。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的假勿。 我是一名探鬼主播借嗽,決...
    沈念sama閱讀 40,130評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼转培!你這毒婦竟也來了恶导?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,985評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤堡距,失蹤者是張志新(化名)和其女友劉穎甲锡,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體羽戒,經(jīng)...
    沈念sama閱讀 45,420評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡缤沦,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,617評(píng)論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了易稠。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片缸废。...
    茶點(diǎn)故事閱讀 39,779評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖驶社,靈堂內(nèi)的尸體忽然破棺而出企量,到底是詐尸還是另有隱情,我是刑警寧澤亡电,帶...
    沈念sama閱讀 35,477評(píng)論 5 345
  • 正文 年R本政府宣布届巩,位于F島的核電站,受9級(jí)特大地震影響份乒,放射性物質(zhì)發(fā)生泄漏恕汇。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,088評(píng)論 3 328
  • 文/蒙蒙 一或辖、第九天 我趴在偏房一處隱蔽的房頂上張望瘾英。 院中可真熱鬧,春花似錦颂暇、人聲如沸缺谴。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽湿蛔。三九已至,卻和暖如春县爬,著一層夾襖步出監(jiān)牢的瞬間煌集,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評(píng)論 1 269
  • 我被黑心中介騙來泰國打工捌省, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留苫纤,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,876評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像卷拘,于是被迫代替她去往敵國和親喊废。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,700評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • 在開發(fā)Android APP時(shí)栗弟,肯定會(huì)使用到Http請(qǐng)求與服務(wù)器通信污筷,上傳或下載數(shù)據(jù)等功能。目前開源的Http請(qǐng)求...
    wangling90閱讀 478評(píng)論 0 0
  • 前言 使用Retrofit已經(jīng)一段時(shí)間了乍赫,這貨挺好用的瓣蛀,還很特別,特別是使用接口來定義請(qǐng)求方式雷厂,這用法讓我對(duì)它的源...
    帶心情去旅行閱讀 3,359評(píng)論 3 21
  • 首先介紹下Retrofit基本用法惋增,先創(chuàng)建接口,注解申明改鲫、請(qǐng)求方式Post/Get等 基本使用如下 上面是簡單的網(wǎng)...
    _SHYII閱讀 1,006評(píng)論 0 3
  • 前言 在Android開發(fā)中诈皿,網(wǎng)絡(luò)請(qǐng)求十分常用 而在Android網(wǎng)絡(luò)請(qǐng)求庫中,Retrofit是當(dāng)下最熱的一個(gè)網(wǎng)...
    Carson帶你學(xué)安卓閱讀 70,722評(píng)論 48 393
  • 雖然年齡在不斷增長像棘,但覺得自己有時(shí)仍然像個(gè)孩子稽亏。這么說并不是說自己不愿長大,而是覺得有必要保留內(nèi)心深處的純凈缕题,像孩...
    簡JN閱讀 658評(píng)論 0 1