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

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

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

一.retrofit入門

  • 定義網絡請求的API接口:

    interface GithubApiService {
        @GET("users/{name}/repos")
        Call<ResponseBody> searchRepoInfo(@Path("name") String name);
    }

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

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

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

  • 生成接口實現類:

    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類型的接口,方便簡潔。

下面我們就看看retrofit的核心工作是如何完成的!

二.retrofit初始化

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

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 Object[] args) 方法,這個方法會傳入我們需要的實現的方法,和參數,並返回我們需要的返回值。

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

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

T adapt(Call&lt;R&gt; 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原理的一些看法

文章開頭說的源碼領取方式技術交流羣862625886

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章