Retrofit 2.0源碼解析

Retrofit

Retrofit 是square公司開發(fā)的一款對OKHttp進(jìn)行了進(jìn)一步封裝的網(wǎng)絡(luò)框架,現(xiàn)在也是android網(wǎng)絡(luò)請求中非常火的一個網(wǎng)絡(luò)請求框架,花了點時間研究了一下Retrofit2.0源碼。

Retrofit2.0原理

Retrofit2.0用了動態(tài)代理技術(shù),通過解析注解生成Http請求,把請求交給OkHttp,然后通過我們設(shè)置的ConverterFactory進(jìn)行serialization和deserialization,最后通過CallAdapter把結(jié)果進(jìn)行進(jìn)一步適配,實現(xiàn)了對Rxjava,Guava和java8的支持。

源碼剖析

Retrofit retrofit = new Retrofit.Builder()
                             .baseUrl("http://www.baidu.com/")
                             .addConverterFactory(GsonConverterFactory.create())
                             .build();
retrofit.create(GitHub.class);

Retrofit實例是使用建造者模式通過Builder類進(jìn)行創(chuàng)建的

建造者模式:將一個復(fù)雜對象的構(gòu)建與表示分離,使得用戶在不知道對象的創(chuàng)建細(xì)節(jié)情況下就可以直接創(chuàng)建復(fù)雜的對象

Retrofit類

public final class Retrofit {

private final Map<Method, ServiceMethod> serviceMethodCache = new LinkedHashMap<>();
// 網(wǎng)絡(luò)請求配置對象(對網(wǎng)絡(luò)請求接口中方法注解進(jìn)行解析后得到的對象)
// 作用:存儲網(wǎng)絡(luò)請求相關(guān)的配置,如網(wǎng)絡(luò)請求的方法、數(shù)據(jù)轉(zhuǎn)換器、網(wǎng)絡(luò)請求適配器、網(wǎng)絡(luò)請求工廠、基地 
址等

private final HttpUrl baseUrl;
// 網(wǎng)絡(luò)請求的url地址

private final okhttp3.Call.Factory callFactory;
// 網(wǎng)絡(luò)請求器的工廠
// 作用:生產(chǎn)網(wǎng)絡(luò)請求器(Call)
// Retrofit是默認(rèn)使用okhttp

private final List<CallAdapter.Factory> adapterFactories;
// 網(wǎng)絡(luò)請求適配器工廠的集合
// 作用:放置網(wǎng)絡(luò)請求適配器工廠
// 網(wǎng)絡(luò)請求適配器工廠作用:生產(chǎn)網(wǎng)絡(luò)請求適配器(CallAdapter)
// 下面會詳細(xì)說明


private final List<Converter.Factory> converterFactories;
// 數(shù)據(jù)轉(zhuǎn)換器工廠的集合
// 作用:放置數(shù)據(jù)轉(zhuǎn)換器工廠
// 數(shù)據(jù)轉(zhuǎn)換器工廠作用:生產(chǎn)數(shù)據(jù)轉(zhuǎn)換器(converter)
 private final Executor callbackExecutor;
// 回調(diào)方法執(zhí)行器

private final boolean validateEagerly; 
// 標(biāo)志位
// 作用:是否提前對業(yè)務(wù)接口中的注解進(jìn)行驗證轉(zhuǎn)換的標(biāo)志位

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); 
this.adapterFactories = unmodifiableList(adapterFactories);   
// unmodifiableList(list)近似于UnmodifiableList<E>(list)
// 作用:創(chuàng)建的新對象能夠?qū)ist數(shù)據(jù)進(jìn)行訪問,但不可通過該對象對list集合中的元素進(jìn)行修改
this.callbackExecutor = callbackExecutor;  
this.validateEagerly = validateEagerly;  
}

1、serviceMethod:包含所有網(wǎng)絡(luò)請求信息的對象
2、baseUrl:網(wǎng)絡(luò)請求的url地址
3、callFactory:網(wǎng)絡(luò)請求工廠
4、adapterFactories:網(wǎng)絡(luò)請求適配器工廠的集合
5、converterFactories:數(shù)據(jù)轉(zhuǎn)換器工廠的集合
6、callbackExecutor:回調(diào)方法執(zhí)行器

new Retrofit.Builder()做了什么?我們來揭開神秘面紗

public static final class Builder {
private Platform platform;
private okhttp3.Call.Factory callFactory;
private HttpUrl baseUrl;
private List<Converter.Factory> converterFactories = new ArrayList<>();
private List<CallAdapter.Factory> adapterFactories = new ArrayList<>();
private Executor callbackExecutor;
private boolean validateEagerly;
//Builder類的成員變量與Retrofit類的成員變量是對應(yīng)的 所以Retrofit類的成員變量基本上是通過Builder類進(jìn)行配置

Builder(Platform platform) {
  //接收Platform對象(Android平臺)
  this.platform = platform;

 // 通過傳入BuiltInConverters()對象配置數(shù)據(jù)轉(zhuǎn)換器工廠(converterFactories)
 // converterFactories是一個存放數(shù)據(jù)轉(zhuǎn)換器Converter.Factory的數(shù)組
 // 配置converterFactories即配置里面的數(shù)據(jù)轉(zhuǎn)換器
  converterFactories.add(new BuiltInConverters());

}

public Builder() {
  this(Platform.get());
}
//Platform.get()通過源碼可以看到這里面有三種平臺支持android、ios、java,這里返回的是一個Android對象

接下來看一下Platformy源碼

class Platform {

private static final Platform PLATFORM = findPlatform();

static Platform get() {
return PLATFORM;    
}

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平臺
  Class.forName("org.robovm.apple.foundation.NSObject");
  return new IOS();
} catch (ClassNotFoundException ignored) {
}
platform)  
return new Platform();
}
}

這樣就很清晰了,主要就是這個findPlatform()方法。這里面的代碼,就是判斷當(dāng)前運行的平臺??梢钥吹嚼锩嬗蠥ndroid、Java、IOS。
我們在Android上運行的話,就調(diào)用了return new Android()。

看一下new Android里面是什么東東

static class Android extends Platform {
@Override public Executor defaultCallbackExecutor() {
  return new MainThreadExecutor();
}

@Override CallAdapter.Factory defaultCallAdapterFactory(Executor callbackExecutor) {
  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);
  }
}
}

Android繼承了Platform重寫了defaultCallbackExecutor和defaultCallAdapterFactory方法。
defaultCallbackExecutor:返回的是用于執(zhí)行 Callback 的 線程池。可以看到MainThreadExecutor 獲取了主線程的 Looper 并構(gòu)造了一個主線程的 Handler,調(diào)用 Callback 時會將該請求 post 到主線程上去執(zhí)行。這就解釋了為什么請求后完成的回調(diào)都是在主線中。
defaultCallAdapterFactory:將返回的適配類型默認(rèn)為Call類型(如果使用RxJava的話,就可以通過配置.addCallAdapterFactory(RxJavaCallAdapterFactory.create())將配置類型改成Observable。)

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

這里有兩個重載的方法,創(chuàng)建了okhttp3 的 HttpUrl 實例。

addConverterFactory(GsonConverterFactory.create())

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

往轉(zhuǎn)換工廠集合中添加了我們指定的轉(zhuǎn)換工廠,最后將返回的數(shù)據(jù)類型轉(zhuǎn)換成對應(yīng)的實體類對象的Converter類型。在我們的例子里面 GsonConverterFactory 將選用 GsonConverter 來轉(zhuǎn)換。

build()

public Retrofit build() {
  if (baseUrl == null) {
    throw new IllegalStateException("Base URL required.");
  }

  okhttp3.Call.Factory callFactory = this.callFactory;
  if (callFactory == null) {
    callFactory = new OkHttpClient();
  }

  Executor callbackExecutor = this.callbackExecutor;
  if (callbackExecutor == null) {
    callbackExecutor = platform.defaultCallbackExecutor();
  }

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

  // Make a defensive copy of the converters.
  List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);
  return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
      callbackExecutor, validateEagerly);
}

new Retrofit這里才是創(chuàng)建Retrofit對象的地方,之前的只是一些配置。里面的參數(shù):
callFactory(Call工廠):看到了吧callFactory = new OkHttpClient();,這里用的是okhttp3;
baseUrl(服務(wù)器基本地址):這個我們上面配置過;
converterFactories(對象的序列號/反序列化組件):我們上面配置過。
adapterFactories(適配類型)、callbackExecutor(執(zhí)行 Callback 的線程池):從我們上面提到的platform中獲取默認(rèn)值。

Retrofit 2.0所使用的動態(tài)代理

Retrofit2.0用了動態(tài)代理技術(shù),通過解析注解生成Http請求,把請求交給OkHttp,然后通過我們設(shè)置的ConverterFactory進(jìn)行serialization和deserialization,最后通過CallAdapter把結(jié)果進(jìn)行進(jìn)一步適配,實現(xiàn)了對Rxjava,Guava和java8的支持。

我們看一下retrofit.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);
        }
        //看緩存里面有沒有這個method,要是有,就返回,要是沒有,就生成一個,然后加入緩存
        ServiceMethod serviceMethod = loadServiceMethod(method);
        //生成一個OkHttpCall對象
        OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
        //調(diào)用OkHttp,然后根據(jù)okHttpCall返回rxjava的Observe對象或者返回Call
        return serviceMethod.callAdapter.adapt(okHttpCall);
      }
    });
}

這里的Platform 其實是檢測retrofit所運行的平臺,是java8還是android還是ios。這里主要是在builder的時候,如果沒有設(shè)置適配器,那么retrofit就會通過運行時的不同平臺,然后選擇不同的CallAdapterFactory。從上面的代碼可以看出,create 方法返回了一個動態(tài)代理對象,通過Github接口生成代理類,并將代理類的實現(xiàn)交給 InvocationHandler 作為具體的實現(xiàn)。這里使用動態(tài)代理的好處是簡化復(fù)雜的網(wǎng)絡(luò)請求和解析、封裝ServiceMethod。

看一下ServiceMethod

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要處理的事物
1.先去serviceMethodCache中查找否存在method(看來這貨是有緩存的,這里采用了LinkedHashMap來緩存這些Method的解析結(jié)果),存在的話跳過第二步;
2.method不存在的話就創(chuàng)建一個,然后添加到緩存中;
3.返回ServiceMethod 對像。

看一下OkHttpCall是如何請求和回調(diào)的

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

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

}
這里的call就是okhttp3.Call,是我們外面?zhèn)鬟M(jìn)來的serviceMethod來構(gòu)造出來的okhttp3.Call,在這里通過用call.enqueue(...)把請求交給okhttp的隊列中,然后再通過異步回調(diào)切換到主線程

總結(jié)

啊.....舒了一口氣,整個源碼使用大量的設(shè)計模式

  • retrofit.builder 建造者模式
  • Android extends Platform 適配器模式
  • 各種...Factory 工廠模式
  • 在Retrofit中提供了四種CallAdapterFactory: ExecutorCallAdapterFactory(默認(rèn))、GuavaCallAdapterFactory、Java8CallAdapterFactory、RxJavaCallAdapterFactory
    adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor)) 策略模式
  • create(final Class<T> service) 代理模式
    等等一系列的設(shè)計模式的組合,使得各個功能模塊高度解耦
本章主要說了一下Retrofit是如何構(gòu)建的,通過動態(tài)代理技術(shù),通過解析注解生成Http請求,把請求交給OkHttp隊列,然后在回調(diào)的這樣一個流程。

點贊加關(guān)注是給我最大的鼓勵!

相關(guān)文章閱讀
Android-設(shè)計模式

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 在開發(fā)Android APP時,肯定會使用到Http請求與服務(wù)器通信,上傳或下載數(shù)據(jù)等功能。目前開源的Http請求...
    wangling90閱讀 525評論 0 0
  • 前言 使用Retrofit已經(jīng)一段時間了,這貨挺好用的,還很特別,特別是使用接口來定義請求方式,這用法讓我對它的源...
    帶心情去旅行閱讀 3,472評論 3 21
  • 首先介紹下Retrofit基本用法,先創(chuàng)建接口,注解申明、請求方式Post/Get等 基本使用如下 上面是簡單的網(wǎng)...
    _SHYII閱讀 1,162評論 0 3
  • 說明:在文件管理器中,可以使用這個app來打開圖片 布局文件 ImageView 常用的一些XML屬性和方法: 支...
    KokutouDa閱讀 3,701評論 0 2
  • 剛看完韓寒導(dǎo)演的處女座《后會無期》。江河寫《旅行者》紀(jì)念一路走來,而我在替未來提供回憶。 陳喬恩飾演周沫,很美的名...
    公子明清閱讀 731評論 0 3

友情鏈接更多精彩內(nèi)容