Retrofit

以下都是本人收集和總結(jié)的內(nèi)容:

1. 什么是Retrofit

Retrofit是一個(gè) RESTful 的 HTTP 網(wǎng)絡(luò)請(qǐng)求框架的封裝。注意這里并沒有說它是網(wǎng)絡(luò)請(qǐng)求框架籽腕,主要原因在于網(wǎng)絡(luò)請(qǐng)求的工作并不是 Retrofit來完成的。Retrofit2.0 開始內(nèi)置 OkHttp


640.png

我們的應(yīng)用程序通過Retrofit請(qǐng)求網(wǎng)絡(luò),實(shí)際上是使用Retrofit接口層封裝請(qǐng)求參數(shù)敷待、Header、Url 等信息仁热,之后由OkHttp完成后續(xù)的請(qǐng)求操作榜揖,在服務(wù)端返回?cái)?shù)據(jù)之后,OkHttp將原始的結(jié)果交給Retrofit抗蠢,后者根據(jù)用戶的需求對(duì)結(jié)果進(jìn)行解析的過程举哟。講到這里,你就會(huì)發(fā)現(xiàn)所謂Retrofit迅矛,其實(shí)就是 Retrofitting OkHttp 了妨猩。

2. 如何實(shí)現(xiàn)Retrofit

2.1.定義請(qǐng)求接口

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

接口當(dāng)中的 listRepos 方法,就是我們想要訪問的 api 了, 如下:

https://api.github.com/users/{user}/repos

2.2.通過retrofit生成一個(gè)剛才定義接口的實(shí)現(xiàn)類

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

2.3.通過接口直接進(jìn)行請(qǐng)求

其中秽褒,在發(fā)起請(qǐng)求時(shí)壶硅, {user} 會(huì)被替換為方法的第一個(gè)參數(shù) octocat.

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

發(fā)請(qǐng)求的代碼就像前面這一句,返回的 repos其實(shí)并不是真正的數(shù)據(jù)結(jié)果震嫉,它更像一條指令森瘪,你可以在合適的時(shí)機(jī)去執(zhí)行它:

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


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

//執(zhí)行
Call<List<Repo>> repos = service.listRepos("octocat");
List<Repo> data = repos.execute();

2.4.參數(shù)的配置

發(fā)請(qǐng)求時(shí),需要傳入?yún)?shù)票堵,Retrofit 通過注解的形式令 Http 請(qǐng)求的參數(shù)變得更加直接扼睬,而且類型安全。
2.4.1 Query & QueryMap

@GET("/list")
Call<ResponseBody> list(@Query("page") int page);

Query 其實(shí)就是 Url 中 ‘?’ 后面的 key-value悴势,比如:

http://www.baidu.com/?tn=56060048_4_pg

這里的tn=56060048_4_pg 就是一個(gè) Query窗宇,而我們?cè)谂渲盟臅r(shí)候只需要在接口方法中增加一個(gè)參數(shù)

這時(shí)候你肯定想,如果我有很多個(gè) Query特纤,多參數(shù)的版本的 QueryMap 橫空出世了军俊,使用方法如下

@GET("/list")
Call<ResponseBody> list(@QueryMap Map<String, String> options);

2.4.2 Field & FieldMap
其實(shí)我們用 POST 的場(chǎng)景相對(duì)較多,絕大多數(shù)的服務(wù)端接口都需要做加密捧存、鑒權(quán)和校驗(yàn)粪躬,GET顯然不能很好的滿足這個(gè)需求担败。使用 POST 提交表單的場(chǎng)景就更是剛需了,怎么提呢镰官?

  @FormUrlEncoded  
  @POST("/")     
  Call<ResponseBody> example( @Field("name") String name, 
                              @Field("occupation") String occupation);

其實(shí)也很簡(jiǎn)單提前,我們只需要定義上面的接口就可以了,我們用 Field聲明了表單的項(xiàng)泳唠,這樣提交表單就跟普通的函數(shù)調(diào)用一樣簡(jiǎn)單直接了狈网。
同樣多版本版本FieldMap,使用方式和QueryMap差不多笨腥,不講了

2.4.3 Part & PartMap
這個(gè)是用來上傳文件的

public interface FileUploadService {     
 @Multipart    
 @POST("upload")   
 Call<ResponseBody> upload(@Part("description") RequestBody description,  
                           @Part MultipartBody.Part file);
}

如果你需要上傳文件拓哺,和我們前面的做法類似,定義一個(gè)接口方法脖母,需要注意的是士鸥,這個(gè)方法不再有 @FormUrlEncoded 這個(gè)注解,而換成了 @Multipart镶奉,后面只需要在參數(shù)中增加 Part
就可以了础淤。也許你會(huì)問,這里的 Part 和 Field 究竟有什么區(qū)別哨苛,其實(shí)從功能上講鸽凶,無非就是客戶端向服務(wù)端發(fā)起請(qǐng)求攜帶參數(shù)的方式不同,并且前者可以攜帶的參數(shù)類型更加豐富建峭,包括數(shù)據(jù)流玻侥。也正是因?yàn)檫@一點(diǎn),我們可以通過這種方式來上傳文件亿蒸,下面我們就給出這個(gè)接口的使用方法:


上傳文件.png

在實(shí)驗(yàn)時(shí)凑兰,我上傳了一個(gè)只包含一行文字的文件:

Visit me: http://www.println.net
那么我們?nèi)シ?wù)端看下我們的請(qǐng)求是什么樣的:

HEADERS

HEADERS.png

FORM/POST PARAMETERS

description: This is a description

RAW BODY

BODY.png

我們看到,我們上傳的文件的內(nèi)容出現(xiàn)在請(qǐng)求當(dāng)中了边锁。如果你需要上傳多個(gè)文件姑食,就聲明多個(gè) Part參數(shù),或者試試 PartMap

2.5 Converter茅坛,讓你的入?yún)⒑头祷仡愋拓S富起來

2.5.1 RequestBodyConverter
2.4.3 當(dāng)中音半,我為大家展示了如何用 Retrofit上傳文件,這個(gè)上傳的過程其實(shí)贡蓖。曹鸠。還是有那么點(diǎn)兒不夠簡(jiǎn)練,我們只是要提供一個(gè)文件用于上傳斥铺,可我們前后構(gòu)造了三個(gè)對(duì)象:


fdfdf.png

天哪彻桃,肯定是哪里出了問題。實(shí)際上晾蜘,Retrofit 允許我們自己定義入?yún)⒑头祷氐念愋土诰欤贿^眠屎,如果這些類型比較特別,我們還需要準(zhǔn)備相應(yīng)的 Converter肆饶,也正是因?yàn)?Converter 的存在组力,Retrofit 在入?yún)⒑头祷仡愋蜕媳憩F(xiàn)得非常靈活。

下面我們把剛才的 Service 代碼稍作修改:

public interface FileUploadService {      
@Multipart    
@POST("upload")        
Call<ResponseBody> upload(@Part("description") RequestBody description,                
//注意這里的參數(shù) "aFile" 之前是在創(chuàng)建 MultipartBody.Part 的時(shí)候傳入的        
                          @Part("aFile") File file);
}

現(xiàn)在我們把入?yún)㈩愋透某闪宋覀兪煜さ?File抖拴,如果你就這么拿去發(fā)請(qǐng)求,服務(wù)端收到的結(jié)果會(huì)讓你哭了的腥椒。阿宅。。
RAW BODY

BODY.png

服務(wù)端收到了一個(gè)文件的路徑,這明顯是 Retrofit 在發(fā)現(xiàn)自己收到的實(shí)際入?yún)⑹莻€(gè) File時(shí)饭豹,不知道該怎么辦翎承,情急之下給 toString了脓豪,而且還是個(gè) JsonString(后來查證原來是使用了 GsonRequestBodyConverter。往湿。)。
接下來我們就自己實(shí)現(xiàn)一個(gè) FileRequestBodyConverter

 static class FileRequestBodyConverterFactory extends Converter.Factory {       
 @Override    
 public Converter<File, RequestBody> requestBodyConverter(Type type, Annotation[]           
             parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {             
     return new FileRequestBodyConverter();   
 }  
}          
static class FileRequestBodyConverter implements Converter<File, RequestBody> {             
       @Override    
       public RequestBody convert(File file) throws IOException {            
         return RequestBody.create(MediaType.parse("application/otcet-stream"), file);  
  }  
}

在創(chuàng)建 Retrofit 的時(shí)候記得配置上它:

addConverterFactory(new FileRequestBodyConverterFactory())

這樣惋戏,我們的文件內(nèi)容就能上傳了领追。來,看下結(jié)果吧:

RAW BODY

BODY.png

文件內(nèi)容成功上傳了.

2.5.2 ResponseBodyConverter

前面我們?yōu)榇蠹液?jiǎn)單示例了如何自定義 RequestBodyConverter响逢,對(duì)應(yīng)的绒窑,Retrofit 也支持自定義 ResponseBodyConverter。

我們?cè)賮砜聪挛覀兌x的接口:

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

返回值的類型為 List<Repo>舔亭,而我們直接拿到的原始返回肯定就是字符串(或者字節(jié)流)些膨,那么這個(gè)返回值類型是怎么來的呢?首先說明的一點(diǎn)是钦铺,GitHub 的這個(gè) api 返回的是 Json 字符串订雾,也就是說,我們需要使用 Json 反序列化得到 List<Repo>矛洞,這其中用到的其實(shí)是 GsonResponseBodyConverter洼哎。

問題來了,如果請(qǐng)求得到的 Json 字符串與返回值類型不對(duì)應(yīng)缚甩,比如:

接口返回的 Json 字符串:

{"err":0, "content":"This is a content.", "message":"OK"}

返回值類型

class Result {
int code;//等價(jià)于 err
String body;//等價(jià)于 content
String msg;//等價(jià)于 message
}

這種情況下谱净, Gson 就是再牛逼,也只能默默無語倆眼淚了擅威,它哪兒知道字段的映射關(guān)系怎么這么任性啊壕探。好,現(xiàn)在讓我們自定義一個(gè) Converter 來解決這個(gè)問題吧郊丛!

GsonConverter .png

當(dāng)然李请,別忘了在構(gòu)造 Retrofit 的時(shí)候添加這個(gè) Converter瞧筛,這樣我們就能夠愉快的讓接口返回 Result 對(duì)象了。

注意5贾选较幌!Retrofit 在選擇合適的 Converter 時(shí),主要依賴于需要轉(zhuǎn)換的對(duì)象類型白翻,在添加 Converter 時(shí)乍炉,注意 Converter 支持的類型的包含關(guān)系以及其順序。

3.結(jié)合Retrofit源碼深度學(xué)習(xí)

retrofit的最大特點(diǎn)就是解耦滤馍,要解耦就需要大量的設(shè)計(jì)模式
精簡(jiǎn)流程圖


Retrofit流程圖.png

舉個(gè)列子岛琼,跟隨他進(jìn)入主題

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


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

//執(zhí)行
Call<List<Repo>> repos = service.listRepos("octocat");
List<Repo> data = repos.execute();

3.1首先通過@GET來標(biāo)識(shí)這個(gè)接口是一個(gè)GET請(qǐng)求。那么看一下這個(gè)GET注解的定義巢株。

  @Documented
  //@Target表示Annotation可用在什么地方槐瑞。
  //METHOD:方法聲明 
  @Target(METHOD)
  //@Retention表示在什么級(jí)別保存該注解信息
  //RUNTIME:VM會(huì)在運(yùn)行時(shí)保留注解,這時(shí)可以通過反射讀取注解信息
  @Retention(RUNTIME)
  public @interface GET {
    String value() default "";
  }

3.2 接下來看一下是如何創(chuàng)建Retrofit對(duì)象的阁苞。

public final class Retrofit  {
   ......代碼省略.......
  //對(duì)平臺(tái)的支持
  private Platform platform;
  //發(fā)起請(qǐng)求OKHttp3的Client工廠
  private okhttp3.Call.Factory callFactory;
  //OKHttp2的HttpUrl對(duì)象困檩,也就是將我們傳入的baseUrl字符串包裝成HttpUrl
  private HttpUrl baseUrl;
  //轉(zhuǎn)換器工廠集合,retrofit可以插入多個(gè)轉(zhuǎn)化器那槽,例如:Gson悼沿,Jackson等等
  private List<Converter.Factory> converterFactories = new ArrayList<>();
  //用于發(fā)起請(qǐng)求和接收響應(yīng)的Call適配器工廠集合,
  //Retrofit對(duì)RxJava的支持就是通過在該集合中添加RxJavaCallAdapterFactory,
  //而RxJavaCallAdapterFactory正是繼承自CallAdapter.Factory
  private List<CallAdapter.Factory> adapterFactories = new ArrayList<>();
  //Executor并發(fā)框架骚灸,用于對(duì)請(qǐng)求后響應(yīng)結(jié)果的回調(diào)執(zhí)行
  private Executor callbackExecutor;
  //是否需要立即生效
  private boolean validateEagerly;

  Builder(Platform platform) {
    this.platform = platform;
    // Add the built-in converter factory first. This prevents overriding its behavior but also
    // ensures correct behavior when using converters that consume all types.
    converterFactories.add(new BuiltInConverters());
  }

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

  //對(duì)屬性的配置
  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);
  //默認(rèn)添加ExecutorCallAdapterFactory 显沈,先記住~~~~~~
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);
  }

 Retrofit(okhttp3.Call.Factory callFactory, HttpUrl baseUrl,
      List<Converter.Factory> converterFactories, List<CallAdapter.Factory> adapterFactories,
      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;
  }


......代碼省略.......
}

Builder中的Platform.get()做什么了?我們繼續(xù)點(diǎn)擊查看:

class Platform {
  private static final Platform PLATFORM = findPlatform();

  static Platform get() {
    return PLATFORM;
  }
  //通過findPlatform方法我們可以看出Retrofit支持三個(gè)平臺(tái)
  private static Platform findPlatform() {
    try {
      //Android
      Class.forName("android.os.Build");
      if (Build.VERSION.SDK_INT != 0) {
        return new Android();
      }
    } catch (ClassNotFoundException ignored) {
    }
    try {
     //java
      Class.forName("java.util.Optional");
      return new Java8();
    } catch (ClassNotFoundException ignored) {
    }
    try {
      //IOS,這里的IOS指的是RoboVM。
      //RoboVM它是一種可以在iOS設(shè)備上運(yùn)行Java應(yīng)用程序的技術(shù)逢唤,這種技術(shù)主要還是用于在游戲開發(fā)中拉讯。
      Class.forName("org.robovm.apple.foundation.NSObject");
      return new IOS();
    } catch (ClassNotFoundException ignored) {
    }
    return new Platform();
  }

  CallAdapter.Factory defaultCallAdapterFactory(Executor callbackExecutor) {   
    //默認(rèn)為null,子類重寫賦值
    if (callbackExecutor != null) {
      return new ExecutorCallAdapterFactory(callbackExecutor);
    }
    return DefaultCallAdapterFactory.INSTANCE;
  }  
}

當(dāng)前平臺(tái)為Android平臺(tái)之后返回一個(gè)Android對(duì)象鳖藕,這個(gè)Android類是Platform中的一個(gè)靜態(tài)內(nèi)部類魔慷。我們看看他做了什么

static class Android extends Platform {
     @Override 
    public Executor defaultCallbackExecutor() {
         //重寫父類,返回的Handler對(duì)象
        return new MainThreadExecutor();
    }

    @Override 
    CallAdapter.Factory defaultCallAdapterFactory(Executor callbackExecutor) {
      //網(wǎng)絡(luò)請(qǐng)求時(shí)分析ExecutorCallAdapterFactory做了什么
      return new ExecutorCallAdapterFactory(callbackExecutor);
    }

    static class MainThreadExecutor implements Executor {
      private final Handler handler = new Handler(Looper.getMainLooper());

      @Override public void execute(Runnable r) {
        handler.post(r);
      }
    }
  }

3.3 接下來直入重點(diǎn),Retrofit對(duì)象的創(chuàng)建OK以后,便通過Reetrofit中的create方法創(chuàng)建一個(gè)我們請(qǐng)求接口,其實(shí)創(chuàng)建的一個(gè)代理對(duì)象了著恩,這里涉及點(diǎn)兒 Java 的動(dòng)態(tài)代理的知識(shí)院尔,直接來看create代碼:

public final class Retrofit {
  ......代碼省略.......
 public <T> T create(final Class<T> service) {
    Utils.validateServiceInterface(service);
    if (validateEagerly) {
      eagerlyValidateMethods(service);
    }
    //這里返回一個(gè)service的代理對(duì)象
    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);
            }
            //每一個(gè)接口最終初始化一個(gè)serviceMethod
            ServiceMethod serviceMethod = loadServiceMethod(method);
            //并且Retrofit 與 Okhttp 完全耦合
            OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
            //發(fā)送請(qǐng)求,并且解析服務(wù)端返回的結(jié)果
            return serviceMethod.callAdapter.adapt(okHttpCall);
          }
        });
  }
  //這個(gè)方法通過反射獲取我們創(chuàng)建service接口中所有的接口方法喉誊,然后根據(jù)接口方法和當(dāng)前的
  //retrofit對(duì)象來獲得ServiceMethod并且以接口方法作為Key邀摆,
  //ServiceMethod作為值添加到serviceMethodCache緩存中
  //下次便可以通過接口方法直接獲取ServiceMethod
  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;
    synchronized (serviceMethodCache) {
      result = serviceMethodCache.get(method);
      if (result == null) {
        //初始化ServiceMethod,并且執(zhí)行serviceMethod.callAdapter
        result = new ServiceMethod.Builder(this, method).build();
        serviceMethodCache.put(method, result);
      }
    }
    return result;
  }
  ......代碼省略.......
}

總結(jié)下伍茄,當(dāng)我們通過代理類(也就是我們調(diào)用create方法后返回的service)調(diào)用我們所創(chuàng)建的接口方法時(shí)栋盹。InvocationHandler中的invoke方法將會(huì)被調(diào)用。在invoke方法中由于method.getDeclaringClass()獲取到的是一個(gè)接口敷矫,并不是Object類例获,所以第一個(gè)條件不成立汉额。而在Android平臺(tái)下platform.isDefaultMethod(method)返回的為false,所以這個(gè)條件也不成立榨汤。之后通過loadServiceMethod方法來獲取ServiceMethod蠕搜。最后調(diào)用ServiceMethod中callAdapter的adapt方法。而ServiceMethod中的callAdapter屬性是通過ServiceMethod中createCallAdapter方法所創(chuàng)建收壕。在createCallAdapter中實(shí)際上是調(diào)用了Retrofit中的callAdapter方法來對(duì)ServiceMethod中的callAdapter進(jìn)行初始化妓灌。

簡(jiǎn)單的說,在我們調(diào)用 GitHubService.listRepos 時(shí)蜜宪,實(shí)際上調(diào)用的是這里的InvocationHandler.invoke 方法~~

3.4 下面再看一下ServiceMethod中的callAdapter方法如何執(zhí)行旬渠,

final class ServiceMethod<T> {
 
  final Retrofit retrofit;
    final Method method;
    final Annotation[] methodAnnotations;
    final Annotation[][] parameterAnnotationsArray;
    final Type[] parameterTypes; 


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();
      ......省略代碼......
      return new ServiceMethod<>(this);
    }   


    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 {
       //注意這里調(diào)用Retrofit的callAdapter
        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);
      }
    }

   
}

接下來查看Retrofit的callAdapter做了什么

public final class Retrofit {
  .......省略代碼.........
private List<CallAdapter.Factory> adapterFactories = new ArrayList<>();
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;
    //通過遍歷adapterFactories并根據(jù)我們的接口方法所中返回值類型來獲取相應(yīng)的適配器callAdapter。
    for (int i = start, count = adapterFactories.size(); i < count; i++) {
      CallAdapter<?> adapter = adapterFactories.get(i).get(returnType, annotations, this);
      if (adapter != null) {
        return adapter;
      }
    }

    .......省略代碼.........
  }

總結(jié):通過遍歷adapterFactories并根據(jù)我們的接口方法所中返回值類型來獲取相應(yīng)的適配器callAdapter,如果我們想返回的是RxJava中Observable對(duì)象端壳, 那我們添加了RxJavaCallAdapterFactory,那么返回的就是RxJavaCallAdapter枪蘑。如果沒有添加那么此處的adapter為null损谦,便會(huì)拋出異常。在正是通過這種適配器模式完成了對(duì)RxJava的完美結(jié)合岳颇。

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

service.listRepos("octocat")照捡;這個(gè)方法到底怎么執(zhí)行的我們上面還遺留了一個(gè)ExecutorCallAdapterFactory方法接下來我們一步一步研究它


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

  ExecutorCallAdapterFactory(Executor callbackExecutor) {
    this.callbackExecutor = callbackExecutor;
  }
   //get方法返回一個(gè)CallAdapter對(duì)象,
  @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;
      }
        //adapt方法返回ExecutorCallbackCall對(duì)象,它實(shí)現(xiàn)了Retrofit中的Call接口
       //其實(shí)就是InvocationHandler.invoke 那個(gè)callAdapter的實(shí)現(xiàn)類
      @Override public <R> Call<R> adapt(Call<R> 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(final 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(call, new IOException("Canceled"));
              } else {
                callback.onResponse(call, response);
              }
            }
          });
        }

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

從ExecutorCallAdapterFactory類中可看出通過get方法返回一個(gè)CallAdapter對(duì)象话侧,從對(duì)CallAdapter的實(shí)現(xiàn)中可以看出在CallAdapter中的adapt方法返回的是ExecutorCallbackCall對(duì)象栗精。它實(shí)現(xiàn)了Retrofit中的Call接口。到這里我們也就明白了瞻鹏,當(dāng)通過代理調(diào)用我們創(chuàng)建的接口方法中所返回的Call對(duì)象就是這個(gè)ExecutorCallbackCall悲立。當(dāng)我們通過call.enqueue來完成網(wǎng)絡(luò)請(qǐng)求操作實(shí)際上就是調(diào)用ExecutorCallbackCall中的enqueue方法。在ExecutorCallbackCall中enqueue又將網(wǎng)絡(luò)請(qǐng)求委托給OkHttpCall去執(zhí)行新博。而這個(gè)OkHttpCall正是我們?cè)赗etrofit的create方法中所創(chuàng)建的OkHttpCall薪夕。由于OKHttp的CallBack接口中的onResponse和onFailure是在子線程中執(zhí)行的,所以在這時(shí)候又通過callbackExecutor將CallBack的onResponse和onFailure切換到主線程中執(zhí)行赫悄。

有了上面分析后我們知道repos.execute()其實(shí)就是一個(gè) OkHttpCall 實(shí)例原献,execute 就是要發(fā)起網(wǎng)絡(luò)請(qǐng)求

那么點(diǎn)進(jìn)去看看Call 的接口:

public interface Call<T> extends Cloneable {    
  //同步發(fā)起請(qǐng)求  
  Response<T> execute() throws IOException;    
  //異步發(fā)起請(qǐng)求,結(jié)果通過回調(diào)返回  
  void enqueue(Callback<T> callback);    
  boolean isExecuted();    
  void cancel();   
  boolean isCanceled();    
  Call<T> clone();    
  //返回原始請(qǐng)求  
  Request request();
}

然后在繼續(xù)查看OkHttpCall

final class OkHttpCall<T> implements Call<T> {

  @Override public Response<T> execute() throws IOException {
  //這個(gè)call 是 Okhttp的call埂淮,本質(zhì)對(duì)okhttpcall做了一層封裝
    okhttp3.Call call;
    
   synchronized (this) {
    //處理重復(fù)執(zhí)行的邏輯
      if (executed) throw new IllegalStateException("Already executed.");
      executed = true;

      if (creationFailure != null) {
        if (creationFailure instanceof IOException) {
          throw (IOException) creationFailure;
        } else {
          throw (RuntimeException) creationFailure;
        }
      }

      call = rawCall;
      if (call == null) {
        try {
          call = rawCall = createRawCall();
        } catch (IOException | RuntimeException e) {
          creationFailure = e;
          throw e;
        }
      }
    }

    if (canceled) {
      call.cancel();
    }
    //發(fā)送請(qǐng)求姑隅,并解析結(jié)果
    return parseResponse(call.execute());
  }
  .......省略代碼.........
}

我們看到 OkHttpCall 其實(shí)也是封裝了 okhttp3.Call,在這個(gè)方法中倔撞,我們通過 okhttp3.Call 發(fā)起了進(jìn)攻讲仰,額,發(fā)起了請(qǐng)求痪蝇。有關(guān) OkHttp 的內(nèi)容叮盘,我在這里就不再展開了秩贰。

parseResponse 主要完成了由 okhttp3.Response 向 retrofit.Response 的轉(zhuǎn)換,同時(shí)也處理了對(duì)原始返回的解析:

final class OkHttpCall<T> implements Call<T> {
  ......省略代碼.........
Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {   

 ResponseBody rawBody = rawResponse.body();          
//略掉一些代碼  
  try {          
//在這里完成了原始 Response 的解析柔吼,T 就是我們想要的結(jié)果毒费,
//比如GitHubService.listRepos 的 List<Repo>     
      T body = serviceMethod.toResponse(catchingBody);           
      return Response.success(body, rawResponse);   
 } catch (RuntimeException e) {         
      // If the underlying source threw an exception, propagate that rather than indicating it was      
      // a runtime exception.      
        catchingBody.throwIfCaught();          
  throw e; 
   }
  }
   .......省略代碼.........
}

3.5 結(jié)果適配,你是不是想用 RxJava愈魏?
前面我們已經(jīng)提到過 CallAdapter的事兒觅玻,默認(rèn)情況下,它并不會(huì)對(duì) OkHttpCall 實(shí)例做任何處理:

final class DefaultCallAdapterFactory extends CallAdapter.Factory {    
    static final CallAdapter.Factory INSTANCE = new DefaultCallAdapterFactory();        
    @Override  
    public CallAdapter<?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {    
      ... 毫不留情的省略一些代碼 ...       
     return new CallAdapter<Call<?>>() {    
      ... 省略一些代碼 ...          
    @Override 
    public <R> Call<R> adapt(Call<R> call) {            
      //看這里培漏,直接把傳入的 call 返回了    
      return call;   
   }}; 
 }
}

現(xiàn)在的需求是溪厘,我想要接入 RxJava,讓接口的返回結(jié)果改為 Observable:

 public interface GitHub {        
@GET("/repos/{owner}/{repo}/contributors")    
Observable<List<Contributor>> contributors(               
        @Path("owner") String owner,               
        @Path("repo") String repo); 
 }

Retrofit 的開發(fā)者們?cè)缇拖氲搅诉@個(gè)問題牌柄,并且為我們提供了相應(yīng)的 Adapter畸悬,只需在構(gòu)造 Retrofit時(shí),添加它:

addCallAdapterFactory(RxJavaCallAdapterFactory.create())

這樣我們的接口就可以以 RxJava 的方式工作了珊佣。
接著我們搞清楚 RxJavaCallAdapterFactory 是怎么工作的蹋宦,首先讓我們來看下 CallAdapter
的接口:

public interface CallAdapter<T> {
      //返回http解析后的類型,需注意這個(gè)類型并不是接口返回的類型咒锻,而是
      //接口返回類型中泛型參數(shù)的實(shí)參
      Type responseType();
       //T 是我們需要轉(zhuǎn)換成接口返回的類型冷冗。參數(shù)call 其實(shí)最初就是okhttpcall的實(shí)例
//在這里T 其實(shí)是rxjava 支持的類型  ,比如 observable
      <R> T adapt(Call<R> call);
       

     //我們需要將Factory的子類對(duì)應(yīng)的實(shí)例在構(gòu)造 retrofit 時(shí)添加到其中
      abstract class Factory {
      //根據(jù)接口的返回類型(observable<T>)惑艇。注釋類型等等來判斷是否當(dāng)前adapter 支持的類型蒿辙,不是則返回null
    public abstract CallAdapter<?> get(Type returnType, Annotation[] annotations,
        Retrofit retrofit);
  
    //獲取指定index的泛型參數(shù)的上限,比如對(duì)于
  //map <string, ? extends number >, index 為1 時(shí)的參數(shù)上限是number
    protected static Type getParameterUpperBound(int index, ParameterizedType type) {
      return Utils.getParameterUpperBound(index, type);
    }

      //獲取原始類型滨巴,比如List<String> 返回list.class 這里傳入的type情況
     //可能比較復(fù)雜思灌,不能直接當(dāng)做class 去做判斷,這個(gè)方法在判斷類型是否
     //為支持的類型時(shí)經(jīng)常用到
    protected static Class<?> getRawType(Type type) {
      return Utils.getRawType(type);
    }
  }
}

代碼中做了較為詳細(xì)的注釋恭取,簡(jiǎn)單來說习瑰,我們只需要實(shí)現(xiàn) CallAdapter 類來提供具體的適配邏輯,并實(shí)現(xiàn)相應(yīng)的 Factory秽荤,用來將當(dāng)前的 CallAdapter注冊(cè)到 Retrofit 當(dāng)中甜奄,并在 Factory.get方法中根據(jù)類型來返回當(dāng)前的 CallAdapter 即可(如果對(duì)這個(gè)方面或者邏輯混亂的話,可以把3.3和3.4的流程在走一遍你就會(huì)明白 了)窃款。知道了這些课兄,我們?cè)賮砜?RxJavaCallAdapterFactory:

public final class RxJavaCallAdapterFactory extends CallAdapter.Factory {
.......省略代碼.........
   @Override
  public CallAdapter<?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
     //下面代碼主要判斷returntype 是否為rxjava 支持的類型
    Class<?> rawType = getRawType(returnType);
    String canonicalName = rawType.getCanonicalName();
    boolean isSingle = "rx.Single".equals(canonicalName);
    boolean isCompletable = "rx.Completable".equals(canonicalName);
    if (rawType != Observable.class && !isSingle && !isCompletable) {
      return null;
    }
    if (!isCompletable && !(returnType instanceof ParameterizedType)) {
      String name = isSingle ? "Single" : "Observable";
      throw new IllegalStateException(name + " return type must be parameterized"
          + " as " + name + "<Foo> or " + name + "<? extends Foo>");
    }

    if (isCompletable) {
   
      return CompletableHelper.createCallAdapter(scheduler);
    }

    CallAdapter<Observable<?>> callAdapter = getCallAdapter(returnType, scheduler);
    if (isSingle) {
      return SingleHelper.makeSingle(callAdapter);
    }
    return callAdapter;
  } 

static final class SimpleCallAdapter implements CallAdapter<Observable<?>> {
    private final Type responseType;
    private final Scheduler scheduler;

    SimpleCallAdapter(Type responseType, Scheduler scheduler) {
      this.responseType = responseType;
      this.scheduler = scheduler;
    }

    @Override public Type responseType() {
      return responseType;
    }

    @Override public <R> Observable<R> adapt(Call<R> call) {
        在這里創(chuàng)建需作為返回值的 observable 實(shí)例,并持有call 的實(shí)例
        當(dāng)observable.subscribe 觸發(fā)時(shí)晨继,call.execute 將會(huì)調(diào)用
      Observable<R> observable = Observable.create(new CallOnSubscribe<>(call)) 
          .lift(OperatorMapResponseToBodyOrError.<R>instance());
      if (scheduler != null) {
        return observable.subscribeOn(scheduler);
      }
      return observable;
    }
  }
  .......省略代碼.........
}

至此烟阐,Retrofit 大部分結(jié)構(gòu)源碼有了新的認(rèn)知。

4.Retrofit結(jié)合Rxjava在項(xiàng)目中實(shí)踐

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市蜒茄,隨后出現(xiàn)的幾起案子唉擂,更是在濱河造成了極大的恐慌,老刑警劉巖檀葛,帶你破解...
    沈念sama閱讀 216,843評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件玩祟,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡屿聋,警方通過查閱死者的電腦和手機(jī)空扎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,538評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來润讥,“玉大人转锈,你說我怎么就攤上這事〕睿” “怎么了撮慨?”我有些...
    開封第一講書人閱讀 163,187評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)脆粥。 經(jīng)常有香客問我砌溺,道長(zhǎng),這世上最難降的妖魔是什么冠绢? 我笑而不...
    開封第一講書人閱讀 58,264評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮常潮,結(jié)果婚禮上弟胀,老公的妹妹穿的比我還像新娘。我一直安慰自己喊式,他們只是感情好孵户,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,289評(píng)論 6 390
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著岔留,像睡著了一般夏哭。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上献联,一...
    開封第一講書人閱讀 51,231評(píng)論 1 299
  • 那天竖配,我揣著相機(jī)與錄音,去河邊找鬼里逆。 笑死进胯,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的原押。 我是一名探鬼主播胁镐,決...
    沈念sama閱讀 40,116評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了盯漂?” 一聲冷哼從身側(cè)響起颇玷,我...
    開封第一講書人閱讀 38,945評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎就缆,沒想到半個(gè)月后帖渠,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,367評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡违崇,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,581評(píng)論 2 333
  • 正文 我和宋清朗相戀三年阿弃,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片羞延。...
    茶點(diǎn)故事閱讀 39,754評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡渣淳,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出伴箩,到底是詐尸還是另有隱情入愧,我是刑警寧澤,帶...
    沈念sama閱讀 35,458評(píng)論 5 344
  • 正文 年R本政府宣布嗤谚,位于F島的核電站棺蛛,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏巩步。R本人自食惡果不足惜旁赊,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,068評(píng)論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望椅野。 院中可真熱鬧终畅,春花似錦、人聲如沸竟闪。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,692評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽炼蛤。三九已至妖爷,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間理朋,已是汗流浹背絮识。 一陣腳步聲響...
    開封第一講書人閱讀 32,842評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留嗽上,地道東北人笋除。 一個(gè)月前我還...
    沈念sama閱讀 47,797評(píng)論 2 369
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像炸裆,于是被迫代替她去往敵國(guó)和親垃它。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,654評(píng)論 2 354

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