Android進階知識:Retrofit相關

1.前言

Retrofit是什麼?Retrofit是一個遵循RESTful設計的進行HTTP網絡請求框架的封裝,是如今Android端最火的進行網絡請求的庫。就像Volley是谷歌官方對HttpURLConnectionHttpClient的封裝,或者像Github上許多開發者發佈的對HttpURLConnectionOkHttp封裝庫同樣,Retrofit也是Square公司發佈的對OkHttp進行了封裝,所以Retrofit中的底層網絡請求是由OkHttp來完成的。java

2.基礎使用

Retrofit的使用步驟很簡單,首先是建立一個網絡請求接口描述文件。android

// 建立一個接口文件ApiService,Retrofit將每一個Http接口請求,抽象爲接口中的一個方法。
public interface ApiService {
    @GET("api")
    Call<WeatherBean> getWeather(@Query("version") String key, @Query("cityid") String city);
}
複製代碼

接着就能夠建立Retrofit對象了。算法

// 建立Retrofit對象
Retrofit retrofit = new Retrofit.Builder()
        // 設置baseUrl
        .baseUrl("http://www.tianqiapi.com/")
        // 添加響應數據轉換器工廠,這裏是Gson將得到響應結果轉換成Gson對象
        .addConverterFactory(GsonConverterFactory.create())
        // 添加網絡請求適配器工廠,這裏是RxJava2將Call轉換成Rxjava中的Observable
        .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
        .build();
// 建立接口實例
ApiService service = retrofit.create(ApiService.class);
// 調用接口中方法
Call<WeatherBean> responseCall = service.getWeather("v1", "101110101");
// 發送同步請求
responseCall.execute();
// 發送異步請求
responseCall.enqueue(new Callback<WeatherBean>() {
     @Override
     public void onResponse(Call<WeatherBean> call, Response<WeatherBean> response) {
     // 主線程
        String responseStr = response.body().toString();
        Log.e("result", responseStr);
    }

    @Override
    public void onFailure(Call<WeatherBean> call, Throwable t) {
      // 主線程
        Log.e("result", t.getMessage());
    }
});
複製代碼

這裏稍微注意下這兩行:編程

// 添加響應數據轉換器工廠,這裏是Gson將得到響應結果轉換成Gson對象
 .addConverterFactory(GsonConverterFactory.create())
 // 添加網絡請求適配器工廠,這裏是RxJava2將Call轉換成Rxjava中的Observable
 .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
複製代碼

這裏的ConverterFactory表示數據轉換器工廠,就是將接收到的響應結果數據轉換成想要的類型,例如通常使用Gson來解析服務器返回的數據,這裏就添加了一個GsonConverterFactoryCallAdapterFactory表示的是網絡請求適配器的工廠,它對應的是接口請求方法的返回類型,是將默認的ExecutorCallbackCall轉換成適配其它不一樣平臺所須要的類型。例如這裏添加了RxJava的適配器,那麼接口中就能夠寫成如下這樣,返回一個RxJava中的Observable類型。json

@GET("api")
 Observable<WeatherBean> getWeather(@Query("version") String key, @Query("cityid") String city);
複製代碼

3.源碼運行流程

本篇文章中的源碼皆是基於Retrofit2.6.0版本,下面開始經過看源碼來進一步瞭解Retrofit,固然這裏仍是按照使用方法的順序開始。設計模式

3.1 建立Retrofit.Builder。

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

首先經過Platform.get得到了一個Platform,而後調用重載的構造方法。先來看看Platformget方法,瞭解下Platform是作什麼的。api

private static final Platform PLATFORM = findPlatform();
  // get方法之間返回靜態成員變量PLATFORM
  static Platform get() {
    return PLATFORM;
  }
 // 不一樣平臺返回不一樣
  private static Platform findPlatform() {
    try {
      Class.forName("android.os.Build");
      if (Build.VERSION.SDK_INT != 0) {
        return new Android();
      }
    } catch (ClassNotFoundException ignored) {
    }
    try {
      Class.forName("java.util.Optional");
      return new Java8();
    } catch (ClassNotFoundException ignored) {
    }
    return new Platform();
  }
複製代碼

get方法中看到直接返回了靜態成員變量PLATFORM,而PLATFORM又是經過findPlatform方法得到的,最後在findPlatform方法裏看到這個方法是用來作多平臺兼容的,根據不一樣的平臺返回不一樣的Platform,這裏有AndroidJava,接下來進一步看看Android平臺下的返回的Android這個對象。緩存

static class Android extends Platform {
    // 判斷是不是默認方法:API24以前統一返回false,以後調用Method類中方法判斷
    @IgnoreJRERequirement // Guarded by API check.
    @Override boolean isDefaultMethod(Method method) {
      if (Build.VERSION.SDK_INT < 24) {
        return false;
      }
      return method.isDefault();
    }
    // 初始化默認的主線程執行器
    @Override public Executor defaultCallbackExecutor() {
      return new MainThreadExecutor();
    }
    // 初始化默認網絡請求適配器工廠集合
    @Override List<? extends CallAdapter.Factory> defaultCallAdapterFactories(
        @Nullable Executor callbackExecutor) {
      if (callbackExecutor == null) throw new AssertionError();
      DefaultCallAdapterFactory executorFactory = new DefaultCallAdapterFactory(callbackExecutor);
      return Build.VERSION.SDK_INT >= 24
        ? asList(CompletableFutureCallAdapterFactory.INSTANCE, executorFactory)
        : singletonList(executorFactory);
    }
    // 默認網絡請求適配器工廠集合大小
    @Override int defaultCallAdapterFactoriesSize() {
      return Build.VERSION.SDK_INT >= 24 ? 2 : 1;
    }
    // 初始化默認數據轉換器工廠集合
    @Override List<? extends Converter.Factory> defaultConverterFactories() {
      return Build.VERSION.SDK_INT >= 24
          ? singletonList(OptionalConverterFactory.INSTANCE)
          : Collections.<Converter.Factory>emptyList();
    }
    // 默認數據轉換器工廠集合大小
    @Override int defaultConverterFactoriesSize() {
      return Build.VERSION.SDK_INT >= 24 ? 1 : 0;
    }
	// MainThreadExecutor主線程執行器
    static class MainThreadExecutor implements Executor {
     // 主線程Handler
      private final Handler handler = new Handler(Looper.getMainLooper());
      @Override public void execute(Runnable r) {
        // 將傳入的Runnable使用主線程Handler處理
        handler.post(r);
      }
    }
  }
複製代碼

大概看一下Android中的方法,主要是初始化Retrofit須要的功能的。defaultCallbackExecutor方法初始化主線程的執行器,默認狀況下異步請求結束後經過它來回到主線程。defaultCallAdapterFactories是初始化請求默認轉換器工廠集合,默認建立了一個轉換器工廠DefaultCallAdapterFactorydefaultConverterFactories是初始化默認數據轉換器工廠集合。能夠看出Retrofit在這裏提供了設置默認的數據轉換器工廠和網絡請求適配器工廠的方法,默認會調用這些方法設置默認的數據轉換器工廠和網絡請求適配器工廠。安全

3.2 設置baseUrl

public Builder baseUrl(String baseUrl) {
      checkNotNull(baseUrl, "baseUrl == null");
      return baseUrl(HttpUrl.get(baseUrl));
}
    
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;
}
複製代碼

baseUrl方法實現很是簡單就是將baseUrl設置到Retrofit.Builder中,參數類型爲String的方法將傳入的字符串封裝成了一個HttpUrl對象,調用對應重載方法,重載方法中調用pathSegments方法將url分割,而後判斷url是不是以斜槓結尾,不是則拋出異常。bash

3.3 添加數據轉換器工廠

private final List<Converter.Factory> converterFactories = new ArrayList<>();

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

該方法就是將傳入的數據轉換器工廠加入到成員變量中的數據轉換器工廠集合中。

3.4 添加網絡請求適配器工廠

private final List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>();

public Builder addCallAdapterFactory(CallAdapter.Factory factory) {
      callAdapterFactories.add(checkNotNull(factory, "factory == null"));
      return this;
}
複製代碼

該方法一樣是將傳入的網絡請求適配器工廠加入到成員變量中的網絡請求適配器工廠集合中。

3.5 調用build方法構建Retrofit對象

public Retrofit build() {
      // 判斷baseUrl不爲空
      if (baseUrl == null) {
        throw new IllegalStateException("Base URL required.");
      }
     // 建立callFactory 默認爲OkHttpClient
      okhttp3.Call.Factory callFactory = this.callFactory;
      if (callFactory == null) {
        callFactory = new OkHttpClient();
      }
    // 獲取默認的回調執行器,沒有就會調用platform.defaultCallbackExecutor
      Executor callbackExecutor = this.callbackExecutor;
      if (callbackExecutor == null) {
        callbackExecutor = platform.defaultCallbackExecutor();
      }

      // 建立網絡請求適配器工廠集合,調用platform.defaultCallAdapterFactories加入默認網絡請求適配器工廠
      List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
      callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));

      // 建立數據轉換器工廠集合
      List<Converter.Factory> converterFactories = new ArrayList<>(
          1 + this.converterFactories.size() + platform.defaultConverterFactoriesSize());

      // 添加內置的數據轉換器工廠
      converterFactories.add(new BuiltInConverters());
	  // 添加add方法添加的數據轉換器工廠
      converterFactories.addAll(this.converterFactories);
	  // 添加默認的數據轉換器工廠
      converterFactories.addAll(platform.defaultConverterFactories());
	  // 建立一個Retrofit對象返回
      return new Retrofit(callFactory, baseUrl, unmodifiableList(converterFactories),
          unmodifiableList(callAdapterFactories), callbackExecutor, validateEagerly);
    }
複製代碼

build方法中就作了許多初始化工做,包括調用了平臺適配器Android中看到的那些方法,最終建立了所需的Retrofit對象。具體來看它作了哪些工做。首先是判斷了baseUrl是否爲空,爲空會拋出異常,這也對應了咱們在使用Retrofit時必需要設置baseUrl。接着初始化了一個callFactory網絡請求工廠,默認的就是OkHttpClient,這也看出了Retrofit默認底層網絡請求採用的就是OkHttp框架。而後建立了回調執行器默認會調用以前看過的Platform中的對應方法拿到主線程的執行器。下兩步其實差很少,就是建立網絡請求適配器工廠集合和數據轉換器工廠集合而且添加默認的網絡請求適配器工廠數據轉換器工廠。最後初始化工做完成後建立一個Retrofit對象返回。這裏再看一下Retrofit的成員變量和構造函數。

public final class Retrofit {
  // 一個支持併發的ConcurrentHashMap,存放請求方法的緩存,這裏的ServiceMethod是解析請求方法以後的封裝
  private final Map<Method, ServiceMethod<?>> serviceMethodCache = new ConcurrentHashMap<>();
  // 網絡請求工廠
  final okhttp3.Call.Factory callFactory;
  // baseUrl
  final HttpUrl baseUrl;
  // 數據轉換器工廠集合
  final List<Converter.Factory> converterFactories;
  // 網絡請求適配器工廠集合
  final List<CallAdapter.Factory> callAdapterFactories;
  // 回調執行器
  final @Nullable Executor callbackExecutor;
  // 是否提早驗證解析的標誌
  final boolean validateEagerly;

  Retrofit(okhttp3.Call.Factory callFactory, HttpUrl baseUrl,
      List<Converter.Factory> converterFactories, List<CallAdapter.Factory> callAdapterFactories,
      @Nullable Executor callbackExecutor, boolean validateEagerly) {
    this.callFactory = callFactory;
    this.baseUrl = baseUrl;
    this.converterFactories = converterFactories; // Copy+unmodifiable at call site.
    this.callAdapterFactories = callAdapterFactories; // Copy+unmodifiable at call site.
    this.callbackExecutor = callbackExecutor;
    this.validateEagerly = validateEagerly;
  }
  ......
  }
複製代碼

這裏看到構造方法中就只是作了初始化賦值操做,再來看看它的成員變量,除了以前看過的那些,這裏多了一個ConcurrentHashMap用來緩存ServiceMethod。還有一個validateEagerly布爾值標誌,這個標誌的做用是判斷是否提早驗證解析,先大概瞭解下,具體的在後面的代碼裏會看到。

3.6 調用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();
          private final Object[] emptyArgs = new Object[0];

          @Override public @Nullable 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);
            }
            return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
          }
        });
  }
複製代碼

這個方法裏就是Retrofit設計的精髓,採用了外觀模式和動態代理模式。也是這個方法建立了API接口的實例。這個方法步驟有點多,一步步來看。首先看到的是validateEagerly標誌,它在這裏被使用,判斷爲true時會調用eagerlyValidateMethods方法,進入eagerlyValidateMethods方法。

private void eagerlyValidateMethods(Class<?> service) {
    Platform platform = Platform.get();
    for (Method method : service.getDeclaredMethods()) {
      if (!platform.isDefaultMethod(method) && !Modifier.isStatic(method.getModifiers())) {
        loadServiceMethod(method);
      }
    }
  }
複製代碼

eagerlyValidateMethods方法中循環遍歷了由參數傳入的網絡請求接口類中的全部方法,而且判斷方法不是默認方法且不是靜態方法就調用loadServiceMethod將該方法傳入。loadServiceMethod是一個用來解析API接口中的方法的方法,具體點來講就是上面示例中ApiService中的每一個方法會被解析成一個ServiceMethod對象並進行緩存。具體後面再看,仍是回到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();
          private final Object[] emptyArgs = new Object[0];

          @Override public @Nullable 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);
            }
            return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
          }
        });
複製代碼

eagerlyValidateMethods默認爲false因此默認會走到return (T) Proxy.newProxyInstance這個方法。這裏使用了動態代理,invoke方法中先判斷方法是不是Object類中方法,是就不作修改按照原樣執行。再調用platform.isDefaultMethod判斷是不是默認方法,是就調用platform.invokeDefaultMethod,該方法中拋出異常。最後是最重要的,也是解析正常接口方法走到的是最後這行return loadServiceMethod(method).invoke(args != null ? args : emptyArgs)。正常狀況下這裏仍是會先調用loadServiceMethod方法,如今再來詳細看一下這個方法。

ServiceMethod<?> loadServiceMethod(Method method) {
    ServiceMethod<?> result = serviceMethodCache.get(method);
    if (result != null) return result;

    synchronized (serviceMethodCache) {
      result = serviceMethodCache.get(method);
      if (result == null) {
        result = ServiceMethod.parseAnnotations(this, method);
        serviceMethodCache.put(method, result);
      }
    }
    return result;
  }
複製代碼

loadServiceMethod中首先在serviceMethodCache緩存中查找這個方法,若是找獲得直接返回。找不到進入同步代碼塊,這裏是DCL單例模式,同步代碼塊中再次判斷緩存中是否存在該方法,不存在調用ServiceMethod.parseAnnotations方法解析方法註解得到解析結果,並將結果ServiceMethod加入緩存。接下來看看ServiceMethodparseAnnotations這個解析註解的方法。

static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
    // 解析註解得到一個RequestFactory
    RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);
    // 獲取方法返回類型
    Type returnType = method.getGenericReturnType();
    // 返回類型中有變量或者通配符拋出異常
    if (Utils.hasUnresolvableType(returnType)) {
      throw methodError(method,
          "Method return type must not include a type variable or wildcard: %s", returnType);
    }
    // 返回類型爲空拋出異常
    if (returnType == void.class) {
      throw methodError(method, "Service methods cannot return void.");
    }

    return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
  }
複製代碼

ServiceMethodparseAnnotations方法中主要作了三件事。一是調用RequestFactoryparseAnnotations方法解析註解得到一個RequestFactory對象。二是獲取方法的返回類型並校驗異常狀況。三是繼續調用HttpServiceMethodparseAnnotations方法。先來看RequestFactoryparseAnnotations方法。

static RequestFactory parseAnnotations(Retrofit retrofit, Method method) {
    return new Builder(retrofit, method).build();
  }
複製代碼

RequestFactoryparseAnnotations方法中以建造者模式建立一個RequestFactory對象。先看Builder構造方法。

Builder(Retrofit retrofit, Method method) {
      this.retrofit = retrofit;
      this.method = method;
      // 初始化獲取方法上的註解,對應使用例子中的@GET
      this.methodAnnotations = method.getAnnotations();
      // 初始化獲取方法參數類型,對應使用例子中的方法參數key、city的類型String
      this.parameterTypes = method.getGenericParameterTypes();
      // 初始化獲取方法參數上的註解,對應使用例子中的方法參數key、city上的@Query
      this.parameterAnnotationsArray = method.getParameterAnnotations();
    }
複製代碼

構造函數中依舊作的是屬性的初始化工做,仍是繼續看其build方法。

RequestFactory build() {
	  // 循環解析方法上的註解
      for (Annotation annotation : methodAnnotations) {
        parseMethodAnnotation(annotation);
      }
	  // HTTP請求方法類型爲空拋出異常
      if (httpMethod == null) {
        throw methodError(method, "HTTP method annotation is required (e.g., @GET, @POST, etc.).");
      }
	  // 註解錯誤拋出異常,Multipart與FormUrlEncoded必須在有請求體的post請求上使用
      if (!hasBody) {
        if (isMultipart) {
          throw methodError(method,
              "Multipart can only be specified on HTTP methods with request body (e.g., @POST).");
        }
        if (isFormEncoded) {
          throw methodError(method, "FormUrlEncoded can only be specified on HTTP methods with "
              + "request body (e.g., @POST).");
        }
      }
	  // 得到方法參數個數
      int parameterCount = parameterAnnotationsArray.length;
	  // 解析每一個參數
      parameterHandlers = new ParameterHandler<?>[parameterCount];
      for (int p = 0, lastParameter = parameterCount - 1; p < parameterCount; p++) {
        parameterHandlers[p] =
            parseParameter(p, parameterTypes[p], parameterAnnotationsArray[p], p == lastParameter);
      }
	  // 判斷各類使用錯誤狀況拋出對應異常
      if (relativeUrl == null && !gotUrl) {
        throw methodError(method, "Missing either @%s URL or @Url parameter.", httpMethod);
      }
      if (!isFormEncoded && !isMultipart && !hasBody && gotBody) {
        throw methodError(method, "Non-body HTTP method cannot contain @Body.");
      }
      if (isFormEncoded && !gotField) {
        throw methodError(method, "Form-encoded method must contain at least one @Field.");
      }
      if (isMultipart && !gotPart) {
        throw methodError(method, "Multipart method must contain at least one @Part.");
      }
	  // 建立RequestFactory對象返回
      return new RequestFactory(this);
    }
複製代碼

build方法中作了四件事。一是解析方法上的註解得到HTTP請求方法類型和其餘請求頭信息。二是解析方法的參數,三是拋出各類錯誤異常,四是建立RequestFactory實例返回。接着簡單看下parseMethodAnnotation方法。

private void parseMethodAnnotation(Annotation annotation) {
      if (annotation instanceof DELETE) {
        parseHttpMethodAndPath("DELETE", ((DELETE) annotation).value(), false);
      } else if (annotation instanceof GET) {
        parseHttpMethodAndPath("GET", ((GET) annotation).value(), false);
      } else if (annotation instanceof HEAD) {
        parseHttpMethodAndPath("HEAD", ((HEAD) annotation).value(), false);
      } else if (annotation instanceof PATCH) {
        parseHttpMethodAndPath("PATCH", ((PATCH) annotation).value(), true);
      } else if (annotation instanceof POST) {
        parseHttpMethodAndPath("POST", ((POST) annotation).value(), true);
      } else if (annotation instanceof PUT) {
        parseHttpMethodAndPath("PUT", ((PUT) annotation).value(), true);
      } else if (annotation instanceof OPTIONS) {
        parseHttpMethodAndPath("OPTIONS", ((OPTIONS) annotation).value(), false);
      } else if (annotation instanceof HTTP) {
        HTTP http = (HTTP) annotation;
        parseHttpMethodAndPath(http.method(), http.path(), http.hasBody());
      } else if (annotation instanceof retrofit2.http.Headers) {
        String[] headersToParse = ((retrofit2.http.Headers) annotation).value();
        if (headersToParse.length == 0) {
          throw methodError(method, "@Headers annotation is empty.");
        }
        headers = parseHeaders(headersToParse);
      } else if (annotation instanceof Multipart) {
        if (isFormEncoded) {
          throw methodError(method, "Only one encoding annotation is allowed.");
        }
        isMultipart = true;
      } else if (annotation instanceof FormUrlEncoded) {
        if (isMultipart) {
          throw methodError(method, "Only one encoding annotation is allowed.");
        }
        isFormEncoded = true;
      }
    }
複製代碼

該方法中主要是判斷方法上註解的各類類型包括GETPOST等各類請求類型又或者是其餘自定義請求頭retrofit2.http.Headers等,找到對應類型的解析方法進行進一步的解析。於此看來RequestFactory中是將咱們在ApiService中寫的方法和方法參數進行解析,解析後封裝成對象以供後續使用。看完RequestFactory再回到ServiceMethodparseAnnotations方法中。該方法最後又調用了HttpServiceMethodparseAnnotations方法因而繼續跟蹤查看。

static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
      Retrofit retrofit, Method method, RequestFactory requestFactory) {
    boolean isKotlinSuspendFunction = requestFactory.isKotlinSuspendFunction;
    boolean continuationWantsResponse = false;
    boolean continuationBodyNullable = false;
	// 得到方法上的全部註解
    Annotation[] annotations = method.getAnnotations();
    Type adapterType;
    if (isKotlinSuspendFunction) {
      ......
    } else {
	  // 非Kotlin方法走這邊,得到方法返回類型
      adapterType = method.getGenericReturnType();
    }
	// 調用createCallAdapter建立一個CallAdapter網絡請求適配器
    CallAdapter<ResponseT, ReturnT> callAdapter =
        createCallAdapter(retrofit, method, adapterType, annotations);
	// 得到CallAdapter的響應類型
    Type responseType = callAdapter.responseType();
	// 是okhttp中的Response拋出異常
    if (responseType == okhttp3.Response.class) {
      throw methodError(method, "'"
          + getRawType(responseType).getName()
          + "' is not a valid response body type. Did you mean ResponseBody?");
    }
	// 響應類型不包含泛型類型拋出異常
    if (responseType == Response.class) {
      throw methodError(method, "Response must include generic type (e.g., Response<String>)");
    }
    // TODO support Unit for Kotlin?
    if (requestFactory.httpMethod.equals("HEAD") && !Void.class.equals(responseType)) {
      throw methodError(method, "HEAD method must use Void as response type.");
    }
	// 調用createResponseConverter方法建立ResponseConverter數據轉換器
    Converter<ResponseBody, ResponseT> responseConverter =
        createResponseConverter(retrofit, method, responseType);
	 // 得到請求工廠
    okhttp3.Call.Factory callFactory = retrofit.callFactory;
    if (!isKotlinSuspendFunction) {// 不是Kotlin方法走這裏
	  // 建立CallAdapted傳入以前建立的requestFactory、callFactory、responseConverter、callAdapter
      return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
    } else if (continuationWantsResponse) {
      //noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
      return (HttpServiceMethod<ResponseT, ReturnT>) new SuspendForResponse<>(requestFactory,
          callFactory, responseConverter, (CallAdapter<ResponseT, Call<ResponseT>>) callAdapter);
    } else {
      //noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
      return (HttpServiceMethod<ResponseT, ReturnT>) new SuspendForBody<>(requestFactory,
          callFactory, responseConverter, (CallAdapter<ResponseT, Call<ResponseT>>) callAdapter,
          continuationBodyNullable);
    }
  }
複製代碼

先來梳理下HttpServiceMethodparseAnnotations方法的流程,該方法中主要作了這幾件事。首先調用createCallAdapter方法建立一個網絡請求適配器,而後調用createResponseConverter方法建立了響應數據轉換器,接着從傳入的retrofit對象中獲取到網絡請求工廠callFactory,最後經過以上這幾個對象建立了一個CallAdapted返回。下面仍是來深刻看這些方法先是createCallAdapter

private static <ResponseT, ReturnT> CallAdapter<ResponseT, ReturnT> createCallAdapter(
      Retrofit retrofit, Method method, Type returnType, Annotation[] annotations) {
    try {
      // 調用retrofit.callAdapter方法
      return (CallAdapter<ResponseT, ReturnT>) retrofit.callAdapter(returnType, annotations);
    } catch (RuntimeException e) { // Wide exception range because factories are user code.
      throw methodError(method, e, "Unable to create call adapter for %s", returnType);
    }
  }
  
 public CallAdapter<?, ?> callAdapter(Type returnType, Annotation[] annotations) {
    return nextCallAdapter(null, returnType, annotations);
 }
複製代碼

createCallAdapter中調用Retrofit類裏的callAdapter方法,進而又調用它的了nextCallAdapter方法返回了一個CallAdapter對象。

public CallAdapter<?, ?> nextCallAdapter(@Nullable CallAdapter.Factory skipPast, Type returnType,
      Annotation[] annotations) {
    checkNotNull(returnType, "returnType == null");
    checkNotNull(annotations, "annotations == null");
    // 從網絡請求適配器工廠集合中查找傳入的適配器的位置
    // 這裏注意到nextCallAdapter方法傳遞的skipPast參數爲null,因此這裏indexOf返回-1,最終start爲0
    int start = callAdapterFactories.indexOf(skipPast) + 1;
    // 循環遍歷網絡請求適配器工廠集合
    for (int i = start, count = callAdapterFactories.size(); i < count; i++) {
     // 調用Factory的get方法得到適配器
      CallAdapter<?, ?> adapter = callAdapterFactories.get(i).get(returnType, annotations, this);
      // 不爲空就返回adapter
      if (adapter != null) {
        return adapter;
      }
    }
   ......
  }
複製代碼

nextCallAdapter方法中遍歷從網絡請求適配器工廠集合,根據方法的返回類型調用工廠的get得到CallAdapter。還記得建立工廠集合時默認添加了一個DefaultCallAdapterFactory嗎?Retrofit默認方法返回類型Call就對應了這個工廠。進入它的get方法查看一下。

@Override public @Nullable CallAdapter<?, ?> get(
      Type returnType, Annotation[] annotations, Retrofit retrofit) {
    if (getRawType(returnType) != Call.class) {
      return null;
    }
    if (!(returnType instanceof ParameterizedType)) {
      throw new IllegalArgumentException(
          "Call return type must be parameterized as Call<Foo> or Call<? extends Foo>");
    }
    final Type responseType = Utils.getParameterUpperBound(0, (ParameterizedType) returnType);

    final Executor executor = Utils.isAnnotationPresent(annotations, SkipCallbackExecutor.class)
        ? null
        : callbackExecutor;

    return new CallAdapter<Object, Call<?>>() {
      @Override public Type responseType() {
        return responseType;
      }

      @Override public Call<Object> adapt(Call<Object> call) {
        return executor == null
            ? call
            : new ExecutorCallbackCall<>(executor, call);
      }
    };
複製代碼

DefaultCallAdapterFactoryget方法中根據傳入的方法返回類型判斷,返回類型不是Call類型就直接返回null。類型正確會返回一個CallAdapter

再回到HttpServiceMethodparseAnnotations方法的流程,createCallAdapter方法以後調用createResponseConverter方法,這兩個方法的流程相似,這裏就大概的看下。

private static <ResponseT> Converter<ResponseBody, ResponseT> createResponseConverter(
      Retrofit retrofit, Method method, Type responseType) {
    Annotation[] annotations = method.getAnnotations();
    try {
      // 一樣是調用Retorfit中的方法responseBodyConverter
      return retrofit.responseBodyConverter(responseType, annotations);
    } catch (RuntimeException e) { // Wide exception range because factories are user code.
      throw methodError(method, e, "Unable to create converter for %s", responseType);
    }
  }
  public <T> Converter<ResponseBody, T> responseBodyConverter(Type type, Annotation[] annotations) {
    // 進而調用nextResponseBodyConverter方法
    return nextResponseBodyConverter(null, type, annotations);
  }
  // 該方法邏輯和nextCallAdapter相似
 public <T> Converter<ResponseBody, T> nextResponseBodyConverter(
      @Nullable Converter.Factory skipPast, Type type, Annotation[] annotations) {
    checkNotNull(type, "type == null");
    checkNotNull(annotations, "annotations == null");

    int start = converterFactories.indexOf(skipPast) + 1;
    for (int i = start, count = converterFactories.size(); i < count; i++) {
      // 調用數據轉換器工廠的responseBodyConverter方法
      Converter<ResponseBody, ?> converter =
          converterFactories.get(i).responseBodyConverter(type, annotations, this);
      if (converter != null) {
        //noinspection unchecked
        return (Converter<ResponseBody, T>) converter;
      }
    }

   ......
  }
複製代碼

createResponseConverter方法的邏輯流程和以前得到網絡請求適配器的流程相似。都是會調用Retrofit類中對應方法,以後再調用對應next方法遍歷數據轉換器集合,從中得到合適的轉換器。這裏來看下經常使用的Gson轉換器的responseBodyConverter方法。

public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
      Retrofit retrofit) {
    TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
    return new GsonResponseBodyConverter<>(gson, adapter);
  }
複製代碼

看到最終會返回一個GsonResponseBodyConverter對象。

終於又回到Retrofitcreate方法裏了,由以前分析可知loadServiceMethod方法最終會得到一個CallAdapted對象,這裏就會接着調用它的invoke方法,而CallAdapted中沒有實現invoke方法,invoke方法在其父類HttpServiceMethod中。

@Override final @Nullable ReturnT invoke(Object[] args) {
    Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
    return adapt(call, args);
  }
複製代碼

invoke方法中建立了一個Call,實現是OkHttpCall,注意這個Call還不是OkHttp中的Call類,它仍是Retrofit包中的類。

package retrofit2;
......
final class OkHttpCall<T> implements Call<T> {
  private final RequestFactory requestFactory;
  private final Object[] args;
  private final okhttp3.Call.Factory callFactory;
  private final Converter<ResponseBody, T> responseConverter;

  private volatile boolean canceled;
  // 這個rawCall纔是OkHttp中的Call
  @GuardedBy("this")
  private @Nullable okhttp3.Call rawCall;
  ......
  }
複製代碼

能夠看到這個類確實在retrofit2包下,而且這個類中有個rawCall成員變量,它纔是OkHttp包中的Call。回到invoke方法建立完OkHttpCall對象後將其傳入adapt方法,這個方法調用的是CallAdapted中的。

@Override protected ReturnT adapt(Call<ResponseT> call, Object[] args) {
      return callAdapter.adapt(call);
    }
複製代碼

方法中又調用了callAdapteradapt方法,這裏的callAdapter就是前面從適配器工廠集合中找到的,默認的就是DefaultCallAdapterFactoryget方法返回的。

@Override public @Nullable CallAdapter<?, ?> get(
      Type returnType, Annotation[] annotations, Retrofit retrofit) {
      ......
    return new CallAdapter<Object, Call<?>>() {
      @Override public Type responseType() {
        return responseType;
      }

      @Override public Call<Object> adapt(Call<Object> call) {
        return executor == null
            ? call
            : new ExecutorCallbackCall<>(executor, call);
      }
    };
  }
複製代碼

return方法實現了CallAdapter,其中adapt方法返回的是一個ExecutorCallbackCall類型,它實現了RetrofitCall接口,就是最終返回的類型。至此

3.7 調用網絡請求接口對象中的方法

由上一步可知得到網絡請求接口類實際使用了動態代理,因此得到的對象不是經過網絡請求接口建立的對象而是它的代理對象。因此在這一步調用接口中的方法例如使用例子中的getWeather方法時,會被動態代理對象攔截,進入它的invoke方法,invoke方法中的Method參數此時就是調用的方法getWeather,接着就會像上一步所講的那樣調用loadServiceMethod(method).invoke方法,最終返回一個ExecutorCallbackCall對象。接下來就是發同步和異步請求。

3.8 同步請求execute方法

同步請求方法調用的是execute方法,進入實現類ExecutorCallbackCallexecute方法查看。

@Override public Response<T> execute() throws IOException {
      return delegate.execute();
}
複製代碼

方法裏直接調用了delegate.execute方法,這個delegate由構造方法傳入,來看構造方法。

ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) {
      this.callbackExecutor = callbackExecutor;
      this.delegate = delegate;
    }
複製代碼

構造方法中初始化了兩個成員變量,一個callbackExecutor由以前的代碼調用能夠發現這個就是主線程的回調執行器,第二個delegate就是在invoke方法中建立的OkHttpCall。因此這裏再到OkHttpCall裏去查看它的execute方法。

@Override public Response<T> execute() throws IOException {
    okhttp3.Call call;
    synchronized (this) {
	  ......

      call = rawCall;
	  // 當前call爲空
      if (call == null) {
        try {
         // 調用createRawCall建立一個call
          call = rawCall = createRawCall();
        } catch (IOException | RuntimeException | Error e) {
          throwIfFatal(e); //  Do not assign a fatal error to creationFailure.
          creationFailure = e;
          throw e;
        }
      }
    }
	// 取消調用call.cancel
    if (canceled) {
      call.cancel();
    }
	// 調用call.execute方法得到OkHttp中Response再調用parseResponse方法解析響應結果返回
    return parseResponse(call.execute());
  }
複製代碼

execute方法中首先判斷當前call是否爲空,爲空則調用createRawCall方法建立一個Call,這個對象類型能夠看到就是OkHttp中的Call類型,接着調用call.execute方法得到OkHttp返回的Response,最後再調用parseResponse方法解析響應結果返回。這裏先看下createRawCall方法。

private okhttp3.Call createRawCall() throws IOException {
    okhttp3.Call call = callFactory.newCall(requestFactory.create(args));
    if (call == null) {
      throw new NullPointerException("Call.Factory returned null.");
    }
    return call;
  }
複製代碼

該方法很簡單,先調用requestFactory.create方法建立一個OkHttp中的Request,這裏就不詳細看了,requestFactory建立時大概看過,其中封裝了一個請求方法的詳細信息。以後調用callFactory.newCall方法得到了返回的Call對象。這裏的callFactory經過前面的代碼知道默認是一個OkHttpClient。因此這個方法就至關因而執行了OkHttpClient.newCall(request),這就是OkHttp的基礎請求操做了。得到到OkHttp返回的Response後繼續調用了parseResponse方法解析,將響應結果轉換成RetrofitResponse

Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
	// 得到響應中的body
    ResponseBody rawBody = rawResponse.body();
    // 移除rawResponse中的body只包含狀態
    rawResponse = rawResponse.newBuilder()
        .body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength()))
        .build();
	// 得到響應中的響應碼
    int code = rawResponse.code();
	// 響應碼小於200大於300表示錯誤
    if (code < 200 || code >= 300) {
      try {
        // Buffer the entire body to avoid future I/O.
        ResponseBody bufferedBody = Utils.buffer(rawBody);
	// 調用Response.error將body和rawResponse返回
        return Response.error(bufferedBody, rawResponse);
      } finally {
        rawBody.close();
      }
    }
	// 響應碼爲204或者205
    if (code == 204 || code == 205) {
      rawBody.close();
	  // 響應碼爲204或者205調用Response.success返回響應體爲空
      return Response.success(null, rawResponse);
    }
	// 將響應體body封裝成一個ExceptionCatchingResponseBody對象
    ExceptionCatchingResponseBody catchingBody = new ExceptionCatchingResponseBody(rawBody);
    try {
	   // 調用responseConverter.convert方法轉換響應結果
      T body = responseConverter.convert(catchingBody);
	  // 調用Response.success將轉換好的body和rawResponse返回
      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;
    }
  }
複製代碼

parseResponse方法中將響應體和其餘響應狀態信息分離,響應體單獨拿了出來進行解析。該方法裏的流程爲判斷響應碼不在200到300之間及異常狀況直接調用Response.error(bufferedBody, rawResponse)返回一個Retrofit中的Response,返回碼爲204或者205表示響應成功可是沒有響應體返回,因此調用Response.success傳入的響應體爲null。除此之外其餘正常狀況就調用數據轉換器responseConverterconvert方法轉換響應,轉換後的結果經過Response.success(body, rawResponse)返回Retrofit中完整的響應結果。

public static <T> Response<T> error(ResponseBody body, okhttp3.Response rawResponse) {
    checkNotNull(body, "body == null");
    checkNotNull(rawResponse, "rawResponse == null");
    if (rawResponse.isSuccessful()) {
      throw new IllegalArgumentException("rawResponse should not be successful response");
    }
    return new Response<>(rawResponse, null, body);
  }
  public static <T> Response<T> success(@Nullable T body, okhttp3.Response rawResponse) {
    checkNotNull(rawResponse, "rawResponse == null");
    if (!rawResponse.isSuccessful()) {
      throw new IllegalArgumentException("rawResponse must be successful response");
    }
    return new Response<>(rawResponse, body, null);
  }
複製代碼

這裏先看errorsuccess方法,這兩個方法比較相似,都是先判斷是否成功,與預期不符直接拋出異常。判斷完後都是建立了一個Retrofit中所須要的相應對象Response返回就好了。同步請求方法的流程到這裏就結束了。最後再來關注下數據轉換的responseConverter.convert方法,這裏調用了數據轉換器工廠裏的convert方法,由於日常使用Gson解析比較多,因此這裏仍是看從Gson對應的工廠GsonConverterFactory得到的轉換器GsonResponseBodyConverter

final class GsonResponseBodyConverter<T> implements Converter<ResponseBody, T> {
  private final Gson gson;
  private final TypeAdapter<T> adapter;
  GsonResponseBodyConverter(Gson gson, TypeAdapter<T> adapter) {
    this.gson = gson;
    this.adapter = adapter;
  }
  @Override public T convert(ResponseBody value) throws IOException {
    JsonReader jsonReader = gson.newJsonReader(value.charStream());
    try {
      T result = adapter.read(jsonReader);
      if (jsonReader.peek() != JsonToken.END_DOCUMENT) {
        throw new JsonIOException("JSON document was not fully consumed.");
      }
      return result;
    } finally {
      value.close();
    }
  }
}
複製代碼

GsonResponseBodyConverterconvert方法實現就比較簡單了,就是調用Gson中的方法了。經過構造中傳入的Gson對象建立一個JsonReader並將響應體的字符流傳入,最後調用TypeAdapter.read(jsonReader)將請求體解析獲得對應實體類。

3.9 異步請求enqueue方法

異步和同步同樣,一樣調用了ExecutorCallbackCall的對應enqueue方法,方法中傳入了一個異步回調Callback

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 OkHttps 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);
            }
          });
        }
      });
    }
複製代碼

ExecutorCallbackCallenqueue方法一樣進而又調用了OkHttpCall中的enqueue方法傳入又一個回調的Callback。在這個回調的成功onResponse和失敗onFailure方法中都先調用了callbackExecutor.execute進行了線程切換。callbackExecutor就是一開始初始化時建立的MainThreadExecutor,因此這裏再次回調的callback.onFailurecallback.onResponse都會執行在主線程中,將響應的結果傳遞到外層。至此異步調用流程也結束了。最後再進入OkHttpCall中的enqueue方法看一下。

@Override public void enqueue(final Callback<T> callback) {
    checkNotNull(callback, "callback == null");
    okhttp3.Call call;
    Throwable failure;
    synchronized (this) {
	  ......
      call = rawCall;
      ......
      if (call == null && failure == null) {
        try {
        // 建立OkHttp中的Call
          call = rawCall = createRawCall();
        } catch (Throwable t) {
          throwIfFatal(t);
          failure = creationFailure = t;
        }
	......
	// 判斷取消
    if (canceled) {
      call.cancel();
    }
    // 調用OkHttp中的call.enqueue
    call.enqueue(new okhttp3.Callback() {
      @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
       // 成功回調
        Response<T> response;
        try {
         // 返回成功解析Response
          response = parseResponse(rawResponse);
        } catch (Throwable e) {
          throwIfFatal(e);
          callFailure(e);
          return;
        }
        try {
         // 調用callback.onResponse
          callback.onResponse(OkHttpCall.this, response);
        } catch (Throwable t) {
          throwIfFatal(t);
          t.printStackTrace(); // TODO this is not great
        }
      }
      @Override public void onFailure(okhttp3.Call call, IOException e) {
        // 失敗回調
        callFailure(e);
      }
      private void callFailure(Throwable e) {
        try {
	    // 調用callback.onFailure
          callback.onFailure(OkHttpCall.this, e);
        } catch (Throwable t) {
          throwIfFatal(t);
          t.printStackTrace(); // TODO this is not great
        }
      }
    });
  }
複製代碼

OkHttpCall中的enqueue方法的邏輯和同步方法的邏輯相似,一樣仍是先建立OkHttp中的Call,再調用OkHttpCallenqueue方法,成功得到到OkHttp的返回結果後再一樣經過parseResponse方法解析,解析以後回調callback.onResponse,若OkHttp返回失敗則回調callback.onFailure

工做流程

4. Retrofit源碼中的設計模式

4.1 建造者模式

這個模式不用找不管是OkHttp仍是Retrofit中都是隨處可見。RetrofitRetrofit這個類就是建造者模式。

建造者模式是將一個複雜對象的構建與他的表示分離,使得一樣的構建過程能夠建立不一樣的表示。

建造者模式中有以下角色:

  • Director:導演類。負責安排已有的模塊順序,通知Builder開始建造。
  • Builder:抽象Builder類。規範產品的組件,通常由子類實現。
  • ConcreteBuilder:具體建造者,實現抽象Builder類定義的方法,返回一個組建好的對象。
  • Product:產品類。

例:

/**
 * 產品類
 */
public class Computer {

    private String mCPU;
    private String mMainBoard;
    private String mRAM;

    public void setmCPU(String mCPU) {
        this.mCPU = mCPU;
    }

    public void setmMainBoard(String mMainBoard) {
        this.mMainBoard = mMainBoard;
    }

    public void setmRAM(String mRAM) {
        this.mRAM = mRAM;
    }
}

/**
 * 抽象builder類
 */
public abstract class Builder {
    public abstract void buildCpu(String cpu);
    public abstract void buildMainboard(String mainboard);
    public abstract void buildRam(String ram);
    public abstract Computer create();
}

/**
 * Builder實現類
 */
public class ComputerBuilder extends Builder {

    private Computer computer = new Computer();

    @Override
    public void buildCpu(String cpu) {
        computer.setmCPU(cpu);
    }

    @Override
    public void buildMainboard(String mainboard) {
        computer.setmMainBoard(mainboard);
    }

    @Override
    public void buildRam(String ram) {
        computer.setmRAM(ram);
    }

    @Override
    public Computer create() {
        return computer;
    }
}

/**
 * 導演類
 */
public class Director {

    Builder builder = null;

    public Director(Builder builder) {
        this.builder = builder;
    }

    public Computer createComputer(String cpu, String mainboard, String ram) {
        this.builder.buildCpu(cpu);
        this.builder.buildMainboard(mainboard);
        this.builder.buildRam(ram);
        return builder.create();
    }
}
複製代碼

調用:

Builder builder = new ComputerBuilder();
  Director director = new Director(builder);
  Computer computer = director.createComputer("i7 7700", "華碩M10H", "三星8G DDR4");
複製代碼

使用場景:

  1. 當建立複雜對象的算法應該獨立於該對象的組成部分以及它們的裝配方式時。
  2. 相同的方法不一樣的執行順序。產生不一樣的事件結果時。
  3. 多個零件頭能夠被裝配到一個對象中,可是產生的運行結果又不相同時。
  4. 產品類很是複雜,或者產品類中的調用順序不一樣而產生了不一樣的效能。
  5. 在建立一些複雜的對象時,這些對象的內部組成構件間的建造順序是穩定的,可是對象內部組成構件面臨複雜的變化。

優勢:

使用建造者模式可使客戶端沒必要知道產品內部組成的細節。 具體的建造這類之間是相互獨立的,容易擴展。 因爲具體的建造者是獨立的,所以能夠對建造過程逐步細化,而不對其餘的模塊產生任何影響

缺點:

產生多餘的Build對象及導演類。

4.2 外觀模式

Retrofit這個類也符合外觀模式。

外觀模式是要求一個子系統的外部與內部的通訊必須經過一個統一的對象進行。此模式提供一個高層的接口,使得子系統更容易使用。

外觀模式中有以下角色:

  • Facade:外觀類,知道哪些子系統負責請求,將客戶端的請求代理給適當的子系統對象。
  • Subsystem: 子系統類。能夠有一個或者多個子系統。實現子系統的功能,處理外觀類指派的任務,注意子系統類不含有外觀類的引用。

例:

/**
 * 子系統食材類
 */
public class Food {

    public void buy() {
        Log.e("msg", "購買食材");
    }
}

public class Wash {
    public void wash() {
        Log.e("msg", "清洗食材");
    }
}

public class Cook {

    public void cook() {
        Log.e("msg", "開始作菜");
    }
}

public class Serve {

    public void serve() {
        Log.e("msg", "作完上菜");
    }
}

/**
 * 外觀類
 */
public class Chef {
    private Food food;
    private Wash wash;
    private Cook cook;
    private Serve serve;

    public Chef() {
        food = new Food();
        wash = new Wash();
        cook = new Cook();
        serve = new Serve();
    }
    public void work(){
        food.buy();
        wash.wash();
        cook.cook();
        serve.serve();
    }
}

複製代碼

調用:

Chef chef = new Chef();
chef.work();
複製代碼

使用場景:

一、構建一個有層次結構的子系統時,使用外觀模式定義子系統中每層的入口點。若是子系統之間是相互依賴的,則可讓其經過外觀接口進行通訊。
二、子系統每每會由於不斷地重構煙花而變得愈來愈複雜,大多數的模式使用時也會產生不少小的類,這個外部調用他們的用戶帶來了使用上的困難。能夠經過外觀模式提供一個簡單的接口,對外隱藏子系統的具體實現並隔離變化。
三、當維護一個遺留的大型系統時,可能這個系統已經很是難以維護和拓展,可是由於它含有重要的功能,因此新的需求必須依賴於它,這時候可使用外觀類,爲設計粗糙或者複雜的遺留代碼提供一個簡單的接口,讓新系統和外觀類交互,而外觀類負責與遺留的代碼進行交互。

優勢:

減小系統的相互依賴,全部的依賴都是對外觀類的依賴,於子系統無關。 對用戶隱藏了子系統的具體實現,減小用戶對子系統的耦合。 增強了安全性。子系統中的方法若是不在外觀類中開通,就沒法訪問到子系統中的方法。

缺點:

不符合開放封閉原則,若是業務出現變動,則可能須要直接修改外觀類。

4.3 單例模式

loadServiceMethod方法中建立ServiceMethod時,就使用了單例模式。

單例模式是用來保證一個類僅有一個實例,而且提供一個訪問他的全局訪問點。

單例模式中有如下角色:

  • Client:高層客戶端。
  • Singleton:單例類。

單例模式有不少種寫法這裏列舉幾種:

// 餓漢式
// 類加載時就完成了初始化,因此類加載比較慢,但獲取對象的速度快,基於類加載機制,避免了多線程同步問題,可是沒有達到懶加載的效果,若是始終沒有使用過這個實例,就會形成內存浪費。
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
    return instance;
}
}

// 懶漢式(線程不安全)
// 在第一次調用時初始化,節約了資源但第一次加載時反應稍慢,且多線程下不能正常工做。
public class Singleton {
private static Singleton instance ;
private Singleton() {
}
public static Singleton getInstance() {
    if (instance==null){
        instance=new Singleton();
    }
    return instance;
}
}
    
// 懶漢式(線程安全)
// 多線程下能正常工做,可是每次代用getInstance方法都要同步,形成沒必要要的同步,而且大部分時候是用不到同步的,不建議使用。
public class Singleton {
private static Singleton instance ;
private Singleton() {
}
public static synchronized Singleton getInstance() {
    if (instance == null){
        instance = new Singleton();
    }
    return instance;
}
}
    
// 雙重檢查模式(DCL)
// 這種寫法進行了兩次判空,第一次爲了避免必要的同步,第二次是Singleton等於null的狀況下才建立實例。而且使用volatile雖然會影響性能可是保證程序的正確性。
// 資源利用率高效率高,第一次加載反應稍慢,在高併發下存在必定缺陷。會發生DCL失效。
public class Singleton {
private static volatile Singleton instance;
private Singleton() {
}
public static synchronized Singleton getInstance() {
    if (instance == null) {
        synchronized (Singleton.class) {
            if (instance == null) {
                instance = new Singleton();
            }
            }
    }
    return instance;
}
}
    
// 靜態內部類單例模式
// 外部類加載時並不須要當即加載內部類,內部類不被加載則不去初始化sInstance,只有調用getInstance 方法時加載SingletonHolder初始化sInstance,不只保證線程安全,也保證惟一性。推薦使用。
public class Singleton {
private Singleton() {
}
public static Singleton getInstance() {
   return SingletonHolder.sInstance;
}
private static class SingletonHolder {
    private static final Singleton sInstance = new Singleton();
}
}
  
// 枚舉單例
// 枚舉的實例建立是線程安全的,而且任何狀況下都是單例。
public enum Singleton {
INSTANCE;
public void doSomeThing() {
}
}
複製代碼

使用場景:

確保某個類有且只有一個對象的場景,避免產生多個對象消耗過多資源,或者某種類型對象只應該有且只有一個。

優勢:

一、因爲單例模式在內存中只有一個實例,減小了內存開支,特別是一個對象須要頻繁的建立銷燬時,並且建立或銷燬時性能又沒法優化,單例模式的優點就很是明顯。
二、單例模式只生產一個實例全部減小了系統性能的開銷,當一個對象產生須要較多的資源時,則能夠經過在應用啓動時直接生產一個對象,而後永久駐留內存的方式來解決。
三、單例模式能夠避免對資源的多重佔用,例如一個寫文件操做,因爲只有一個實例存在內存中,避免對同一個資源同時寫操做。
四、單例模式能夠在系統設置全局的訪問點,優化和共享資源訪問。

缺點:

一、單例模式通常沒有接口,擴展很困難,若要擴展除了修改代碼基本沒有第二種途徑能夠實現。 二、Android中單例對象若是持有Context,那麼很容易發生內存泄漏,所以傳遞給單例模式的Context,最好是Application Context。

4.4 適配器模式

RetrofitDefaultCallAdapterFactory中的get方法返回一個CallAdapter就是一個適配器模式。

適配器模式是把一個類的接口變換成客戶端所期待的另外一種接口,從而使本來因接口不匹配而沒法在一塊兒工做的兩個類可以在一塊兒工做。

適配器模式中有如下角色:

  • Target:目標角色,也就是所期待獲得的接口。注意:因爲這裏討論的是類適配器模式,所以目標不能夠是類。

  • Adapee:源角色,如今須要適配的接口。

  • Adaper:適配器角色,適配器類是本模式的核心。適配器把源接口轉換成目標接口。顯然,這一角色不能夠是接口,而必須是具體類。

例: 類適配器模式:

/**
 * Target:須要的5伏電壓
 */
public interface FiveVolt {
    public int getVolt5();
}

/**
 * Adaotee:220電壓接口
 */
public class Volt220 {

    public int getVolt220() {
        return 220;
    }
}

/**
 * Adapter:將220伏電壓轉換成須要的5伏電壓
 */
public class VoltAdapter implements FiveVolt {

    private Volt220 volt220;

    public VoltAdapter(Volt220 volt220) {
        this.volt220 = volt220;
    }

    @Override
    public int getVolt5() {
        return 5;
    }
}

複製代碼

調用:

VoltAdapter adapter = new VoltAdapter(new Volt220());
adapter.getVolt5();
複製代碼

對象適配器模式:

/**
 * Target角色
 */
public interface FiveVolt {
    public int getVolt5();
}

/**
 * Adaotee:220電壓接口
 */
public class Volt220 {

    public int getVolt220() {
        return 220;
    }
}
/**
 * 對象適配器模式
 */
public class VoltAdapter implements FiveVolt{

    Volt220 mVolt220;

    public VoltAdapter1(Volt220 adaptee) {
        this.mVolt220 = adaptee;
    }

    public int getVolt220(){
        return mVolt220.getVolt220();
    }

    @Override
    public int getVolt5() {
        return 5;
    }
}
複製代碼

調用:

VoltAdapter adapter = new VoltAdapter(new Volt220());
adapter.getVolt5()
複製代碼

使用場景:

一、系統須要使用現有的類,但此類的接口不符合系統的須要,即接口不兼容。
二、想要創建一個能夠重複使用的類,用於與一些彼此之間沒有太大關聯的一些類,包括一些可能在未來引進的類一塊兒工做。 三、須要一個統一的輸出接口,而輸入端的類型不可預知。

優勢:

更好的複用性:系統須要使用現有的類,而此類的接口不符合系統的須要。那麼經過適配器模式就可讓這些功能獲得更好的複用。
更好的擴展性:在實現適配器功能的時候,能夠調用本身開發的功能,從而天然地擴展系統的功能。

缺點:

過多的使用適配器,會讓系統很是零亂,不易總體進行把握。好比,明明看到調用的是A接口,其實內部被適配成了B接口的實現,一個系統若是太多出現這種狀況,無異於一場災難。所以若是不是頗有必要,能夠不使用適配器,而是直接對系統進行重構。

4.5 策略模式

Retrofit中在addCallAdapterFactory時就是一種策略模式。

策略模式是定義一系列的算法,把每個算法封裝起來,而且是他們可互相替換。策略模式使得算法可獨立與它的客戶而獨立變化。

策略模式中有如下角色:

  • Context:上下文角色。用來操做策略的上下文環境,起到承上啓下的做用,屏蔽高層模塊對策略、算法的直接訪問。
  • Stragety:抽象策略角色,策略、算法的抽象,一般爲接口。
  • ConcreteStragety:具體策略的實現。

例:

/**
 * 抽象策略接口
 */
public interface Calculate {
    public double calculate(double num1,double num2);
}

/**
 * 具體策略實現類
 */
public class AddCalculate implements Calculate {
    @Override
    public double calculate(double num1, double num2) {
        return num1+num2;
    }
}
/**
 * 具體策略實現類
 */
public class SubtractCalculate  implements Calculate {
    @Override
    public double calculate(double num1, double num2) {
        return num1-num2;
    }
}
/**
 * 具體策略實現類
 */
public class MultiplyCalculate implements Calculate {
    @Override
    public double calculate(double num1, double num2) {
        return num1 * num2;
    }
}

/**
 * 上下文角色
 */
public class Context {
    private Calculate calculate;

    public Context(Calculate calculate) {
        this.calculate = calculate;
    }

    public double calculate(double num1, double num2) {
      return   calculate.calculate(num1, num2);
    }
}
複製代碼

調用:

Context context;
        context = new Context(new AddCalculate());
        double addResult = context.calculate(1, 1);

        context = new Context(new SubtractCalculate());
        double subtractResult = context.calculate(1, 1);

        context = new Context(new MultiplyCalculate());
        double multiplyResult = context.calculate(1, 1);
複製代碼

使用場景:

一、對客戶隱藏具體策略(算法)的實現細節,彼此徹底獨立。 二、針對同一類型問題多種處理方式,僅僅是具體行爲有差異。
三、在一個類中定義了不少行爲,並且這些行爲在這個類裏的操做以多個條件語句的形式出現。策略模式將相關的條件分支移入它們各自的Stragety類中,以代替這些條件語句。

優勢:

使用策略模式能夠避免使用多重條件語句。多重條件語句不易維護,並且容易出錯。
易於拓展。當要添加策略,只要實現接口就能夠了。

缺點:

每一個策略都是一個類,複用性小。若是策略過多,類的數量會增多。
上層模塊必須知道有哪些策略,纔可以使用這些策略,這與迪米特原則相違背。

4.6 裝飾模式

Retrofit中的ExecutorCallbackCall使用了裝飾模式,其中真正去執行網絡請求的仍是OkHttpCall

動態的給一個對象添加一些額外的職責,就增長功能來講,裝飾模式比生成子類更加靈活。

裝飾模式中有如下角色:

  • Component:抽象組件。能夠是接口或者抽象類被裝飾的最原始對象。
  • ConcreteComponent:組件具體實現類,被裝飾的具體對象。
  • Decorator:抽象裝飾者。從外來拓展Component類的功能,但對於Component來講無需知道Decorator的存在。在它的屬性中必然有一個private變量指向Decorator抽象組件。
  • ConcreteDecorator:裝飾者的具體實現類。

例:

/**
 * 抽象組件
 */
public abstract class People {
    public abstract void learn();
}
/**
 * 組件實現類
 */
public class XiaoMing extends People {
    @Override
    public void learn() {
        Log.e("msg","小明學過語文");
    }
}
/**
 * 抽象裝飾者
 */
public abstract class Teacher extends People {

    private People people ;

    public Teacher(People people) {
        this.people = people;
    }

    @Override
    public void learn() {
        people.learn();
    }
}
/**
 * 裝飾者具體實現類
 */
public class MathTeacher extends Teacher{

    public MathTeacher(People people) {
        super(people);
    }

    public  void  teachMath(){
        Log.e("msg","數學老師教數學");
        Log.e("msg","小明學會了數學");
    }
    @Override
    public void learn() {
        super.learn();
        teachMath();
    }
}
複製代碼

調用:

XiaoMing xiaoMing = new XiaoMing();
MathTeacher mathTeacher = new MathTeacher(xiaoMing);
mathTeacher.learn();
複製代碼

使用場景:

一、在不影響其餘對象的狀況下,以動態、透明地方式給單個對象添加職責。
二、須要動態的給一個對象增長功能。這些功能能夠動態的撤銷。
三、當不能採用繼承的方式對系統進行擴充或者採用繼承不利於系統若站和維護時。

優勢:

經過組合非繼承的方式,動態的擴展一個對象的功能,在運行時選擇不一樣的裝飾器,從而實現不一樣的行爲。
有效的避免了使用繼承的方式擴展一個對象的功能而帶來的靈活性差、子類無限制擴張的問題。
具體組件類與具體裝飾類能夠獨立變化。用戶能夠根據須要增長新的具體組件類和具體裝飾類,在使用時進行組合,原有代碼無需改變,符合開放封閉原則。

缺點:

由於全部對象都繼承自Component,因此若是Component內部發生變化,則不可避免影響全部子類,若是基類改變,勢必影響對象的內部。
比繼承更加靈活機動的特性,同時也意味着裝飾模式比繼承更容易出錯,排錯也很困難,對於屢次裝飾的對象,須要逐級排查,較爲繁瑣。因此只有在必要的時候使用裝飾模式。
裝飾層數不能過多,不然會影響效率。

4.7 簡單工廠模式

Retrofit中的Platform類就是簡單工廠模式。

簡單工廠模式又叫作靜態工廠方法模式,這是由一個工廠對象決定建立處哪種產品類的實例。

簡單工廠模式中有如下角色:

  • Factory:工廠類,簡單工廠模式的核心,負責建立全部實例的內部邏輯。
  • IProduct:抽象產品類。這是簡單工廠模式所建立全部對象的父類,負責描述全部實例共有的公共接口。
  • Product:具體產品類。

例:

/**
 * 手機抽象類
 */
public abstract class MobilePhone {
    /**
     * 查看系統版本
     */
    public abstract void systemVersion();
}
/**
 * 具體產品類
 */
public class IPhoneX extends MobilePhone {
    @Override
    public void systemVersion() {
        Log.d("msg","IOS 11");
    }
}

public class SamsungNote9 extends MobilePhone {
    @Override
    public void systemVersion() {
        Log.d("msg","Android 8.0");

    }
}
/**
 * 手機工廠類
 */
public class MobilePhoneFactory {
    public static MobilePhone createMobilePhone(String name) {
        MobilePhone mobilePhone = null;
        switch (name) {
            case "IPhone":
                mobilePhone = new IPhoneX();
                break;
            case "Samsung":
                mobilePhone = new SamsungNote9();
                break;
            default:
                break;
        }
        return mobilePhone;
    }
}
複製代碼

調用:

MobilePhoneFactory.createMobilePhone("IPhone").systemVersion();
複製代碼

使用場景:

工廠類負責建立的對象比較少。客戶只需知道傳入工廠的參數,而無需關心建立對象的邏輯。

優勢:

用戶根據參數得到對應類的實例,無需關心建立對象的邏輯。下降了耦合性。

缺點:

可實例化的類型在編譯期間已被肯定,若是須要新類型,則須要修改工廠,這違背了開放封閉原則,簡單工廠須要知道全部生成類的類型,檔期子類過多或者子類層次過多時不適合使用。

4.8 抽象工廠模式

Retrofit中的Converter就是抽象工廠模式。

定義一個用於建立對象的接口,讓子類決定實例化哪一個類。工廠方法使一個類的實例化延遲到其子類。

工廠方法模式中有以下角色:

  • Product:抽象產品類。
  • ConcreteProduct: 具體產品類。實現Product接口。
  • Factory:抽象工廠類。返回一個Product對象。
  • ConcreteFactory:具體工廠類。返回ConcreteProduct實例。

例:

/**
 * 產品抽象類
 */
public abstract  class Computer {
    public abstract void systemVersion();
}
/**
 * 具體產品類
 */
public class MacBook extends Computer {
    @Override
    public void systemVersion() {
        Log.d("msg","MAC OS");
    }
}

public class LenovoComputer extends Computer {
    @Override
    public void systemVersion() {
        Log.d("msg","Windows10");
    }
}
/**
 * 抽象工廠類
 */
public abstract class ComputerFactory {
    public abstract <T extends Computer> T createComputer(Class<T> clz);
}
/**
 * 具體工廠
 */
public class ComputerFactoryImpl extends ComputerFactory {
    @Override
    public <T extends Computer> T createComputer(Class<T> clz) {
        Computer computer = null;
        String clzName = clz.getName();
        try {
            //反射生成具體計算機
            computer = (Computer) Class.forName(clzName).newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return (T) computer;
    }
}
複製代碼

調用:

ComputerFactory computerFactory = new ComputerFactoryImpl();
        LenovoComputer computer = computerFactory.createComputer(LenovoComputer.class);
        computer.systemVersion();
複製代碼

使用場景:

一個對象族有相同的約束,則可使用抽象工廠模式。

優勢:

分離接口與實現,客戶端使用抽象工廠來建立須要的對象,而客戶端根本就不知道具體的實現是誰,客戶端只是面向產品的接口編程而已,使其從具體的產品實現中解耦,同時基於接口與實現分離,使抽象該工廠方法模式在切換產品類時更加靈活、容易。

缺點:

一是對類文件的爆炸性增長,二是不太容易擴展新的產品類。

4.9 靜態代理模式

Retrofit中的ExecutorCallbackCall類就是個靜態代理模式,代理了OkHttpCall

代理模式是爲其餘對象提供一種代理以控制這個對象的訪問。

代理模式中有如下角色:

  • Subject:抽象主題類,聲明和真是主題與代理類共同的接口方法。
  • RealSubject:真實主題類。代理類所表明的真實主題。
  • Proxy:代理類。持有對真實主題的引用,在其所實現的接口方法中調用真實主題類中對應的接口方法執行。

例:

/**
 * 抽象接口Subject
 */
public interface Person {
    String sing(String name);

    String dance(String name);
}
/**
 * 委託對象 RealSubject
 */
public class ZhangYiXing implements Person {
    @Override
    public String sing(String name) {
        return "張藝興唱了一首"+name;
    }

    @Override
    public String dance(String name) {
        return "張藝興跳了一支"+name;
    }
}

/**
 * Proxy 代理類
 */
public class JingJiRenProxy implements Person {

    private Person zhangYiXing;

    public JingJiRenProxy(Person zhangYiXing) {
        if (zhangYiXing instanceof ZhangYiXing) {
            this.zhangYiXing = zhangYiXing;
        }
    }

    @Override
    public String sing(String name) {
        String money = "我是經紀人,找張藝興唱歌要出場費100萬,先付50萬\n";
        String sing = zhangYiXing.sing(name);
        String balance = "\n尾款結清50萬";
        return money + sing + balance;
    }

    @Override
    public String dance(String name) {
        String money = "我是經紀人,找張藝興跳舞要出場費200萬,先付100萬\n";
        String dance = zhangYiXing.dance(name);
        String balance = "\n尾款結清100萬";
        return money + dance + balance;
    }
}
複製代碼

調用:

JingJiRenProxy jingJiRenProxy = new JingJiRenProxy(zhangyixi);
   String sing1 = jingJiRenProxy.sing("夢不落雨林");
   String dance1 = jingJiRenProxy.dance("小天鵝");
複製代碼

使用場景:

當沒法或者不想直接訪問某個對象存在困難時能夠經過一個代理對象來簡介訪問,爲了保證客戶端的透明性,委託對象與代理對象須要實現相同的接口。

優勢:

對代理者與被代理者進行解耦。代理對象在客戶端和目標對象之間起到一箇中介的做用,這樣能夠起到對目標對象的保護。

缺點:

對類的增長。

4.10 動態代理模式

RetrofitRetrofit.create方法中就使用了動態代理模式。

動態代理經過反射機制動態的生成代理者的對象,也就是咱們在編碼階段壓根不須要知道代理誰,代理誰將會在執行階段決定。

例:

/**
 * Proxy 代理類
 */
public class SuperJingJiRenProxy {

    private Person zhangYiXing;

    public SuperJingJiRenProxy(Person zhangYiXing) {
        if (zhangYiXing instanceof ZhangYiXing) {
            this.zhangYiXing = zhangYiXing;
        }
    }
    public Person getProxy() {
        return (Person) Proxy.newProxyInstance(ZhangYiXing.class.getClassLoader(), zhangYiXing.getClass().getInterfaces(), new InvocationHandler() {

            /**
             * @param proxy     把代理對象本身傳遞進來
             * @param method    把代理對象當前調用的方法傳遞進來
             * @param args      把方法參數傳遞進來
             * @return
             * @throws Throwable
             */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                if (method.getName().equals("sing")) {
                    String money = "我是經紀人,找張藝興唱歌要出場費100萬,先付50萬\n";
                    String sing = (String) method.invoke(zhangYiXing, args);
                    String balance = "\n尾款結清50萬";
                    return money + sing + balance;
                }
                if (method.getName().equals("dance")) {
                    String money = "我是經紀人,找張藝興跳舞要出場費200萬,先付100萬\n";
                    String dance = (String) method.invoke(zhangYiXing, args);
                    String balance = "\n尾款結清100萬";

                    return money + dance + balance;
                }
                return null;
            }
        });
    }
}

複製代碼

調用:

SuperJingJiRenProxy superJingJiRenProxy = new SuperJingJiRenProxy(zhangyixi);
 Person proxy = superJingJiRenProxy.getProxy();
 String sing2 = proxy.sing("等你下課");
 String dance2 = proxy.dance("最好舞臺");
複製代碼

5. 總結

以上就是關於Retrofit的相關總結內容。畢竟如今Retrofit做爲主流MVP+Retrofit+RxJava這一套框架的必備成員是很是重要的,也是很是好用的。並且Retrofit中對各類設計模式的組合使用是很是精妙的,深刻了解學習仍是頗有必要的,這對咱們自身的編碼水平和架構能力都是大有裨益的。

參考資料

Android進階之光
Android源碼設計模式解析與實戰

相關文章
相關標籤/搜索