Android進階:7、Retrofit2.0原理解析之最簡流程【上】



retrofit 已經流行好久了,它是Square開源的一款優秀的網絡框架,這個框架對okhttp進行了封裝,讓咱們使用okhttp作網路請求更加簡單。可是光學會使用只是讓咱們多了一個技能,學習其源碼才能讓咱們更好的成長。java

本篇文章是在分析retrofit的源碼流程,有大量的代碼,讀者最好把源碼下載下來導入IDE,而後跟着一塊兒看,效果會更好(文末有源碼獲取方式)android

一.retrofit入門

  • 定義網絡請求的API接口:
interface GithubApiService {
        @GET("users/{name}/repos")
        Call<ResponseBody> searchRepoInfo(@Path("name") String name);
    }
複製代碼

使用了註解代表請求方式,和參數類型,這是retrofit的特性,也正是簡化了咱們的網絡請求過程的地方!git

  • 初始化一個retrofit的實例:
Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("https://api.github.com/")
                .build();
複製代碼

retrofit的實例化很簡單,採用鏈式調用的設計,把須要的參數傳進去便可,複雜的參數咱們這裏就不舉例了。github

  • 生成接口實現類:
GithubApiService githubService = retrofit.create(service)
Call<ResponseBody> call = githubService.searchRepoInfo("changmu175");
複製代碼

咱們調用retrofit的create方法就能夠把咱們定義的接口轉化成實現類,咱們能夠直接調用咱們定義的方法進行網絡請求,可是咱們只定義了一個接口方法,也沒有方法體,請求方式和參數類型都是註解,create是如何幫咱們整理參數,實現方法體的呢?一會咱們經過源碼解析再去了解。設計模式

  • 發起網絡請求
//同步請求方式
 call.request();
 //異步請求方式
 call.enqueue(new Callback<ResponseBody>() {
            @Override
            public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
                //請求成功回調
            }

            @Override
            public void onFailure(Call<ResponseBody> call, Throwable t) {
                //請求與失敗回調
            }
        });
複製代碼

至此,retrofit的一次網絡請求示例已經結束,基於對okhttp的封裝,讓網絡請求已經簡化了不少。固然retrofit最適合的仍是REST API類型的接口,方便簡潔。api

下面咱們就看看retrofit的核心工做是如何完成的!安全

二.retrofit初始化

retrofit的初始化採用了鏈式調用的設計bash

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

很明顯這個方法是在傳一些須要的參數,咱們簡單的跟蹤一下:網絡

首先看看Builder()的源碼:框架

public Builder() {
      this(Platform.get());
    }
複製代碼

這句代碼很簡單就是調用了本身的另外一個構造函數:

Builder(Platform platform) {
      this.platform = platform;
    }
複製代碼

這個構造函數也很簡單,就是一個賦值,咱們把以前的Platform.get()點開,看看裏面作在什麼:

private static final Platform PLATFORM = findPlatform();

static Platform get() {
    return PLATFORM;
  }
複製代碼

咱們發現這裏使用使用了一個餓漢式單例,使用Platform.get()返回一個實例,這樣寫的好處是簡單,線程安全,效率高,不會生成多個實例!

咱們再看看findPlatform() 裏作了什麼:

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

    ....省略部分代碼...
 }
複製代碼

因此是判斷了一下系統,而後根據系統實例化一個對象。這裏面應該作了一些和Android平臺相關的事情,屬於細節,咱們追究,感興趣的能夠只看看。
再看看baseUrl(url)的源碼

public Builder baseUrl(String baseUrl) {
      checkNotNull(baseUrl, "baseUrl == null");
      HttpUrl httpUrl = HttpUrl.parse(baseUrl);
      ....
      return baseUrl(httpUrl);
    }

public Builder baseUrl(HttpUrl baseUrl) {
      checkNotNull(baseUrl, "baseUrl == null");
      ....
      this.baseUrl = baseUrl;
      return this;
    }
複製代碼

這兩段代碼也很簡單,校驗URL,生成httpUrl對象,而後賦值給baseUrl

看看build() 方法在作什麼
參數基本設置完了,最後就要看看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();
      }
      ....

      return new Retrofit(callFactory, baseUrl, unmodifiableList(converterFactories),
          unmodifiableList(callAdapterFactories), callbackExecutor, validateEagerly);
    }
  }
}
複製代碼

代碼中有大量的參數校驗,有些複雜的參數咱們沒有傳,因此我就把那些代碼刪除了。簡單看一下也能知道,這段代碼就是作一些參數校驗,baseUrl不能爲空不然會拋異常,至於其餘的參數若是爲null則會建立默認的對象。其中callFactory就是okhttp的工廠實例,用於網絡請求的。
最後咱們看到,這個方法最終返回的是一個Retrofit的對象,初始化完成。

三.生成接口實現類

剛纔咱們就講過retrofit.create這個方法很重要,它幫咱們生成了接口實現類,並完成了方法體的建立,省去了咱們不少工做量。那咱們來看看它是如何幫咱們實現接口的。

public <T> T create(final Class<T> 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, @Nullable 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<Object, Object> serviceMethod =
                (ServiceMethod<Object, Object>) loadServiceMethod(method);
            OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
            return serviceMethod.adapt(okHttpCall);
          }
        });
  }
複製代碼

這段代碼其實是使用了動態代理的設計模式,並且這個方法封裝的很是好,咱們只須要調用 方法就能夠得到咱們須要的實現類,遵循了迪米特法則(最少知道原則)。

瞭解動態代理的人都知道咱們要重寫Object invoke(Object proxy, Method method,[@Nullable](https://xiaozhuanlan.com/u/undefined) Object[] args) 方法,這個方法會傳入咱們須要的實現的方法,和參數,並返回咱們須要的返回值。

retrofit在重寫這個方法的時候作了三件事:

  • 先判斷了這個方法的類是否是一個Object.class),就直接返回方法原有的返回值。
  • 判斷這個方法是否是DefaultMethod,你們都知道這個方法是Java 8出來的新屬性,表示接口的方法體。
  • 構建一個ServiceMethod<Object, Object>對象和OkHttpCall<Object>對象,並調用
    serviceMethod.adapt(okHttpCall)方法將兩者綁定。

咱們看看這個方法的源碼:

T adapt(Call<R> call) {
    return callAdapter.adapt(call);
  }
複製代碼

這個callAdapter咱們在初始化retrofit的時候沒有使用:
addCallAdapterFactory(CallAdapterFactory)傳值,因此這裏是默認的DefaultCallAdapterFactory

那咱們再看看DefaultCallAdapterFactory裏的adapt(call)方法:

@Override public Call<Object> adapt(Call<Object> call) {
        return call;
      }
複製代碼

直接返回參數,也就是OkHttpCall<Object>的對象。因此若是沒有自定義callAdapter的時候,咱們定義接口的時候返回值類型應該是個Call類型的。
那麼,至此這個create方法已經幫咱們實現了咱們定義的接口,並返回咱們須要的值。

因爲文字過長的原因,咱們暫且分爲上下兩文,下文講講到請求參數整理丶Retrofit網絡請求已經本身的一些總結,固然凡事無絕對,只是本身Retrofit原理的一些見解

文章開頭說的源碼領取方式:關注我私信【資料】

相關文章
相關標籤/搜索