源碼解析-retrofit2.0

Retrofit的本質是RESTful的HTTP網絡請求框架的封裝,網絡請求的工作本質是OkHttp完成,而Retrofit只是負責網絡請求接口的封裝。但是現在這麼多人用,也看得出這個框架封裝的十分受程序員喜愛。

現在先看目錄:

一、Retrofit的使用

二、源碼解析

 

一、Retrofit的使用

我們先來看步驟:
1)創建描述網絡請求的接口

2)創建Retrofit實例

3)創建網絡請求接口實例並配置網絡請求參數

4)發送網絡請求

5)處理服務器返回的數據

 

先看網絡請求的接口

public interface retrofit_interface {
    @GET("部分URL")
    Call<String> getCall();
}

這就是第一步,這個接口類,@GET註解方法和Call都是屬於 Retrofit的類。註解方法也是Retrofit方便使用的體現。

@GET代表使用GET請求。Call是個泛型,返回的是string。

我們看下後面的4步:

public class RetrofitDemo {
    //創建Retrofit實例
    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl("http://www.baidu.com/")//設置網絡請求基礎url地址
            .addConverterFactory(GsonConverterFactory.create())//設置數據解析器
            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())//支付rxjava
            .build();

    //創建 網絡請求接口的實例
    retrofit_interface request = retrofit.create(retrofit_interface.class);

    //對 發送請求 進行封裝
    Call<String> call = request.getCall();

    public void callRequest(){
        //發送網絡請求
        call.enqueue(new Callback<String>() {
            @Override
            public void onResponse(Call<String> call, Response<String> response) {

            }

            @Override
            public void onFailure(Call<String> call, Throwable t) {

            }
        });
    }

}

通過builder創建實例並且賦值。這裏我們可以看到有個baseurl的設置,這和接口註解@Get(“部分url”)裏的部分url是分開的,實際請求則是兩個url拼接起來。

而後通過retrofit實例創建網絡請求實例。再通過網絡請求實例去進行網絡請求。

二、源碼解析

解析的主要內容:

2.1、如何創建我們的Retrofit實例

2.2、網絡請求接口實例創建

2.3、發送網絡請求

 

2.1、如何創建我們的Retrofit實例:

我們看到上面創建Retrofit的時候使用的是builder模式:

    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl("http://www.baidu.com/")//設置網絡請求基礎url地址
            .addConverterFactory(GsonConverterFactory.create())//設置數據解析器
            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())//支付rxjava
            .build();

builder模式簡單說就是將複雜對象的創建和表示相分離。就說Retrofit的創建實際上很複雜,但是它將大部分的重要內容都予以默認值,如果你不去改變就不需要去設置。

我們先來看下Retrofit內部的一些變量:

public final class Retrofit {
  private final Map<Method, ServiceMethod<?, ?>> serviceMethodCache = new ConcurrentHashMap<>();

  final okhttp3.Call.Factory callFactory;
  final HttpUrl baseUrl;
  final List<Converter.Factory> converterFactories;
  final List<CallAdapter.Factory> adapterFactories;
  final @Nullable Executor callbackExecutor;
  final boolean validateEagerly;

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

我們看到第一個變量就是一個Map,這個map是用來放置我們網絡請求的配置對象。這個配置對象裏面有網絡請求的方法、數據轉換器、網絡請求適配器、網絡請求工廠、url等。

再看第二個變量callFactory,這個Call代表的是我們的網絡請求器,同步異步都是通過這個call調用,而且我們可以看到這個類是屬於okhttp3的。

第三個變量 baseUrl,網絡請求的基礎地址。

第四個converterFactories,數據轉換器工廠的集合,數據轉換器工廠:用於生產我們數據轉換器converter。如json,服務返回數據給我們json類型,然後通過這個轉換器解析,然後在主線程中顯示。

第五個adapterFactories,網絡請求適配器的集合,網絡請求適配器的工廠:用於生成我們的CallAdapter網絡請求適配器。

第六個callbackExecutor,回調方法的執行器,主線程切換到子線程、子線程切換到主線程都是用這個方法執行的。

第七個validateEagerly,用於判斷是否提前對我們接口當中的註解進行轉換的標誌位。

這裏我們可以看到,成功建立Retrofit的標準就是這麼幾個變量。

我們先看下關於CallAdapter.Factory:

我們可以看到Factory是在CallAdapter裏的抽象類,而這個抽象類的實現類有三種,Default、Executor、rxjava2。

用個例子說明下:假如我們一開始想用ExecutorCall來替換線程,然後我們發現rxjava2更加方便,不需要用handler來替換線程。那麼我們只需要加上RxJava2CallAdapterFactory就可以完成我們okhttpCall到Rxjava間的轉換。

現在我們回到前面

    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl("http://www.baidu.com/")//設置網絡請求基礎url地址
            .addConverterFactory(GsonConverterFactory.create())//設置數據解析器
            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())//支付rxjava
            .build();

我們對Retrofit的內部變量有了些瞭解後,我們再來下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);
    }

第一步就baseUrl的非空判斷。

第二步將成員變量callFactory賦給我們的callFactory對象,然後做非空判斷。爲空就新建個OkhttpClient。這裏我們可以看出retrofit本質使用okhttp進行請求。

第三步將成員變量callbackExecutor賦給我們的callbackExecutor對象,然後做非空,爲空則新建defaultCallbackExecutor。callbackExecutor實際上就是主線程切換到子線程的執行器。

第四步向集合中添加我們這個的請求適配器adapterFactories。

第五步新建一個數據轉換器的集合converterFactories ,在這個存儲集合中存儲的就是我們默認的轉換器工廠,以及我們可以自定我們的轉換器工廠,比如前面寫的RxJava2CallAdapterFactory。

這裏的adapterFactories和converterFactories它們的存儲方式都是從首位到末尾遍歷的。在集合當做位置越靠前就有越高的使用權限。

第六步創建Retrofit並且將剛剛的對象放入,然後返回。

看到這裏我們就已經看完了關於Retrofit的接口實例創建。這個是上面retrofit使用的第二部,下面講解的是第三步網絡請求接口實例創建。

 

2.2、網絡請求接口實例創建:

public interface RetrofitService {

    String BASE_URL = "http://IP:port/api/";


    @GET("self/getUserSelfInfo")
    Observable<LoginResponse> login(@Body String request);

}
//創建 網絡請求接口的實例
    retrofit_interface request = retrofit.create(retrofit_interface.class);

第一步的網絡請求接口就是在第三步這使用retrofit實例create出網絡請求的實例。那麼現在來看下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, @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.callAdapter.adapt(okHttpCall);
          }
        });
  }

首先會根據前面說到validateEagerly標識位來判斷是否需要進行驗證。我們來看下eagerlyValidateMethods方法

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

這個方法顯示遍歷我們所聲明好的方法,然後依次去調用loadServiceMethod方法。loadServiceMethod使用的是一個單例的形式去獲取serviceMethodCache,serviceMethodCache就是Retrofit內的第一個變量。然後通過get方法去獲取相應的ServiceMethod。如果爲空則創建個新的Method。這裏可以看到採用的也是builder模式:ServiceMethod.Builder<>(this, method).build()。

那我們就需要來看下builder和build到底做了什麼:

  Builder(Retrofit retrofit, Method method) {
      this.retrofit = retrofit;
      this.method = method;
      this.methodAnnotations = method.getAnnotations();
      this.parameterTypes = method.getGenericParameterTypes();
      this.parameterAnnotationsArray = method.getParameterAnnotations();
    }

getAnnotations()方法:獲取我們網絡請求方法裏的註釋

getGenericParameterTypes():獲取網絡請求的參數類型

getParameterAnnotations():獲取網絡請求的註解內容

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

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

第一創建了callAdapter,作用是回調傳遞到UI線程。這個創建是根據Retrofit創建裏設置的callAdapter來判斷創建的。

第二callAdapter.responseType()。返回我們想要的類型。如我上面寫的是Call<String>,則返回的是String,然後對這個類型進行判斷。

第三createResponseConverter()。創建我們的數據類型轉換器。即Retrofit創建裏設置的GsonConverterFactory.create()。如服務器返回的是json類型的String,我們想要拿到的是User類型,轉換器就會將String轉成User返回。

第四遍歷我們所有方法的註解去調用 parseMethodAnnotation(annotation);方法去解析網絡請求的註解。

第五 parameterAnnotationsArray.length獲取我們當前方法的參數數量。然後通過parseParameter(p, parameterType, parameterAnnotations);去解析我們的方法參數和註解。

 

我們看回網絡請求接口的實例創建下一步,Proxy.newProxyInstance:

return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
        new InvocationHandler()

一看 Proxy就知道是用了動態代理,最終執行方法在InvocationHandler裏的invoke(Object proxy, Method method, @Nullable Object[] args)方法中。這個方法調用的其實就是我們定義的接口方法getCall()。

而在這個invoke方法裏面有個最核心的代碼需要注意下:

ServiceMethod<Object, Object> serviceMethod =
                (ServiceMethod<Object, Object>) loadServiceMethod(method);
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);

loadServiceMethod(method)方法會將我們前面配置好的屬性去配置我們的ServiceMethod這個對象。也就是我們的請求信息都在裏面。然後再去創建okHttpCall對象,最後調用 okHttpCall,並且根據callAdapter去轉換成我們的網絡請求對象。

loadServiceMethod這個方法上面有講解過。

new OkHttpCall<>(serviceMethod, args);直接根據我們的serviceMethod和args的參數進行創建。我們來看下創建的OkhttpCall:

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

  private volatile boolean canceled;

  @GuardedBy("this")
  private @Nullable okhttp3.Call rawCall;
  @GuardedBy("this")
  private @Nullable Throwable creationFailure; // Either a RuntimeException or IOException.
  @GuardedBy("this")
  private boolean executed;

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

這裏是變量和構造方法
第一個和第二個就是我們傳的serviceMethod和args參數。一個代表網絡請求的所有請求數值,args則表示請求的參數。

第四個參數是個okhttp3.call的對象,這個就是實際進行網絡訪問的類。

構造函數就比較簡單,傳入參數賦值。

我們下面看下第三個重要的代碼:return serviceMethod.callAdapter.adapt(okHttpCall);

這裏用到callAdapter,之前說過callAdapter有三種類型,DefaultCallAdapter、ExecutorCallAdapter、rxjava2CallAdapter三種。之類看下rxjava2的:

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

這個就是通過scheduler這個線程控制的管理工具來完成線程和主線程中的切換。

 

那麼可以看出後面這兩行重要代碼實現的內容也比較簡單。下面來總結下網絡請求接口實例創建

1.動態創建網絡請求接口的實例

2.創建serviceMethod對象(包含網絡請求的所有註解屬性等,通過建造者模式和單例模式完成-對應loadServiceMethod方法)

3.對serviceMethod對象進行網絡請求參數配置(即解析網絡請求的方法參數如url、請求類型、網絡請求適配器、轉換器等信息)

4.對serviceMethod對象假如線程切換的操作

5.最終創建並返回一個OkHttpCall類型的網絡請求對象

 

2.3、發送網絡請求:

下面以異步請求作爲例子:

    //對 發送請求 進行封裝
    Call<String> call = request.getCall();
    public void callRequest(){
        //發送網絡請求
        call.enqueue(new Callback<String>() {
            @Override
            public void onResponse(Call<String> call, Response<String> response) {
            }
            @Override
            public void onFailure(Call<String> call, Throwable t) {
            }
        });
    }

創建好Call對象後,調用enqueue方法來發送異步的網絡請求。我們來看下實現:

  void enqueue(Callback<T> callback);

一個接口,我們來看下實現類ExecutorCallAdapterFactory :

    @Override public void enqueue(final Callback<T> callback) {
      checkNotNull(callback, "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);
            }
          });
        }
      });
    }

這裏用了delegate調用了enqueue方法。這個delegate就是個代理的作用,調用的其實是Okhttp的enqueue方法,我們來看下okHttpCall的enqueue方法:

  @Override public void enqueue(final Callback<T> callback) {
    checkNotNull(callback, "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;
        }
      }
    }

    ......

    call.enqueue(new okhttp3.Callback() {
      @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse)
          throws IOException {
       ......
    });
  }

首先是看createRawCall方法:創建OKhttp Request對象和封裝我們okhttp的call對象:

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

代碼比較簡單:就是創建request和call對象。但是就是用這個兩個去進行網絡請求。我們看回去直接就是調用了call的enqueue方法。然後就是通過callbackExecutor 切換主線程返回解析數據。

總結下流程:

1.對網絡請求接口的方法中的每個參數利用對應ParameterHandler進行解析

2.使用OkHttp的Request發送網絡請求

3.對返回的數據使用之前設置的數據轉換器(GsonConverterFactory)解析返回的數據

4.進行現場奇幻從而在主線程處理返回的數據結果

 

寫到這裏Retrofit基本也寫完了,寫起來還是比較長的,但是如果自己去看這些代碼就會快很多。主要還是理解了解爲何內部這麼寫。寫的內容可能會有些理解上的錯誤或者筆誤,還望看出端的能夠不惜指點。

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