Retrofit源碼分析

目錄介紹

  • 1.首先回顧Retrofit簡單使用方法
  • 2.Retrofit的建立流程源碼分析php

    • 2.1 Retrofit對象調用Builder()源碼解析
    • 2.2 Retrofit對象調用baseUrl(url)源碼解析
    • 2.3 addConverterFactory(Converter.Factory factory)源碼分析
    • 2.4 addCallAdapterFactory(RxJava2CallAdapterFactory.create())源碼分析
    • 2.5 client(okHttpClient)源碼分析
    • 2.6 Retrofit對象調用build()源碼解析
  • 3.建立ServiceMethod流程源碼分析java

    • 3.1 首先看看請求網絡代碼過程
    • 3.2 分析create(final Class<T> service)源碼
    • 3.3 serviceMethod對象的建立過程
  • 4.註解的解析android

    • 4.1 callAdapter的建立源碼分析
    • 4.2 responseConverter的建立源碼分析
  • 5.OkHttpCall的建立源碼分析git

    • 5.1 new OkHttpCall<>(serviceMethod, args)源碼分析
  • 6.OkHttpCall的網絡請求github

    • 6.1 OkHttpCall.execute()同步請求
    • 6.2 OkHttpCall.enqueue()異步請求
    • 6.3 parseResponse解析網絡數據源碼解析

好消息

  • 博客筆記大彙總【16年3月到至今】,包括Java基礎及深刻知識點,Android技術博客,Python學習筆記等等,還包括平時開發中遇到的bug彙總,固然也在工做之餘收集了大量的面試題,長期更新維護而且修正,持續完善……開源的文件是markdown格式的!同時也開源了生活博客,從12年起,積累共計47篇[近20萬字],轉載請註明出處,謝謝!
  • 連接地址:https://github.com/yangchong2...
  • 若是以爲好,能夠star一下,謝謝!固然也歡迎提出建議,萬事起於忽微,量變引發質變!
  • 關於Retrofit的使用,能夠參考這篇文章:https://www.jianshu.com/p/989...

0.思考問題,針對如下問題,看了這篇博客,應該有了初步的認識

  • 0.0.1 Retrofit建立中用了哪些設計模式,請談談使用這些設計模式的優點?
  • 0.0.2 Retrofit在建立的時候爲何要判斷是否在Android環境中,是如何作到的?
  • 0.0.3 爲何設置baseUrl的時候,會以/結尾,若是沒有/會出現什麼問題?
  • 0.0.4 addConverterFactory的主要做用是什麼?
  • 0.0.5 Factory生產的是CallAdapter,那麼CallAdapter又是什麼呢?
  • 0.0.6 網絡請求的類 service爲何要定義成接口?若是不定義成接口會出現什麼狀況?
  • 0.0.7 建立了ServiceMethod對象是幹什麼用的?它是用什麼進行存儲的?
  • 0.0.8 建立ServiceMethod對象爲何要添加synchronized同步鎖
  • 0.0.9 call調用enqueue異步方法中源碼是如何實現異步切換線程的?原理是怎樣的?
  • 0.1.0 ServiceMethod是如何保存網絡請求所須要的數據,具體保存了哪些數據呢?
  • 0.1.1 網絡傳輸都是二進制流,那麼解析數據時,如何經過ServiceMethod使用Converter轉換成Java對象進行數據解析面試

    //AdvertCommon是javabean實體類,並無序列化,那麼網絡解析數據如何解析java對象呢?
    Call<AdvertCommon> getSplashImage(@Query("type") int type);
  • 0.1.2 以下所示,爲何說apiService對象其實是動態代理對象,而不是真正的網絡請求接口建立的對象json

    ApiService apiService = retrofit.create(ApiService.class);
  • 0.1.3 如何理解動態代理的機制。retrofit是如何加載接口類ApiService的,爲何這個類要設置成接口?

1.首先回顧Retrofit簡單使用方法

  • Api接口segmentfault

    public interface DouBookApi {
        /**
        * 根據tag獲取圖書
        * @param tag  搜索關鍵字
        * @param count 一次請求的數目 最多100
*/
    @GET("v2/book/search")
    Observable<DouBookBean> getBook(@Query("tag") String tag,
                                    @Query("start") int start,
                                    @Query("count") int count);
}
```
  • Model類設計模式

    public class DouBookModel {
    
        private static DouBookModel bookModel;
        private DouBookApi mApiService;
    
        public DouBookModel(Context context) {
            mApiService = RetrofitWrapper
                    .getInstance(ConstantALiYunApi.API_DOUBAN)   //baseUrl地址
                    .create(DouBookApi.class);
        }
    
        public static DouBookModel getInstance(Context context){
            if(bookModel == null) {
                bookModel = new DouBookModel(context);
            }
            return bookModel;
        }
    
        public Observable<DouBookBean> getHotMovie(String tag, int start , int count) {
            Observable<DouBookBean> book = mApiService.getBook(tag, start, count);
            return book;
        }
    }
  • 抽取類api

    public class RetrofitWrapper {
    
        private static RetrofitWrapper instance;
        private Retrofit mRetrofit;
    
        public RetrofitWrapper(String url) {
            OkHttpClient.Builder builder = new OkHttpClient.Builder();
    
            //打印日誌
            HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
            logging.setLevel(HttpLoggingInterceptor.Level.BODY);
            builder.addInterceptor(logging).build();
            OkHttpClient client = builder.addInterceptor(new LogInterceptor("HTTP")).build();
    
            //解析json
            Gson gson = new GsonBuilder()
                    .setLenient()
                    .create();
            
            mRetrofit = new Retrofit
                    .Builder()
                    .baseUrl(url)
                    .addConverterFactory(GsonConverterFactory.create(gson))
                    .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                    .client(client)
                    .build();
        }
    
        public  static RetrofitWrapper getInstance(String url){
            //synchronized 避免同時調用多個接口,致使線程併發
            synchronized (RetrofitWrapper.class){
                instance = new RetrofitWrapper(url);
            }
            return instance;
        }
    
        public <T> T create(final Class<T> service) {
            return mRetrofit.create(service);
        }
    }
  • 使用

    DouBookModel model = DouBookModel.getInstance(activity);
    model.getHotMovie(mType,start,count)
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(new Subscriber<DouBookBean>() {
                @Override
                public void onCompleted() {
    
                }
    
                @Override
                public void onError(Throwable e) {
    
                }
    
                @Override
                public void onNext(DouBookBean bookBean) {
    
                }
            });
  • 針對Retrofit,須要注意

    • Retrofit 是一個 RESTful 的 HTTP 網絡請求框架的封裝。網絡請求的工做本質上是 OkHttp 完成,而 Retrofit 僅負責 網絡請求接口的封裝。看下圖所示,摘自網絡
    • image

2.Retrofit的建立流程源碼分析

  • Retrofit實例是使用建造者模式經過Builder類進行建立的。Builder模式的優點:將一個複雜對象的構建與表示分離,使得用戶在不知道對象的建立細節狀況下就能夠直接建立複雜的對象。
  • 針對builder設計模式,能夠參考個人這篇博客:https://blog.csdn.net/m0_3770...
  • 關於使用builder模式寫代碼的案例,能夠參考個人彈窗封裝lib:https://github.com/yangchong2...

2.1 Retrofit對象調用Builder()源碼解析

  • 首先看看裏面的源代碼,以下所示

    • 能夠看到Platform.get()獲取的是單利對象。那麼也許你會問,這個方法的做用主要是什麼呢?經過Class.forName獲取類名的方式,來判斷當前的環境是否在Android中,這在以後獲取默認的CallAdapterFactory時候將會用到。下面我會分析到……
    • 關於單利設計模式,若是還有疑問,或者想知道全部的獲取單利的方法,能夠參考個人這篇博客:設計模式之一:單例模式
    //第一步
    public Builder() {
      this(Platform.get());
    }
    
    
    //第二步,追蹤到Platform類中
    private static final Platform PLATFORM = findPlatform();
    static Platform get() {
        return PLATFORM;
    }
    
    private static Platform findPlatform() {
    try {
      Class.forName("android.os.Build");
      if (Build.VERSION.SDK_INT != 0) {
        //此處表示:若是是Android平臺,就建立並返回一個Android對象
        return new Android();
      }
    } catch (ClassNotFoundException ignored) {
    }
    try {
      Class.forName("java.util.Optional");
      return new Java8();
    } catch (ClassNotFoundException ignored) {
    }
    return new Platform();
    }
  • 而後看一下new Android()是作了什麼?

    static class Android extends Platform {
        @Override public Executor defaultCallbackExecutor() {
            // 返回一個默認的回調方法執行器
            // 該執行器做用:切換線程(子->>主線程),並在主線程(UI線程)中執行回調方法
          return new MainThreadExecutor();
        }
        
        @Override CallAdapter.Factory defaultCallAdapterFactory(@Nullable Executor callbackExecutor) {
          if (callbackExecutor == null) throw new AssertionError();
           // 建立默認的網絡請求適配器工廠 
           // 該默認工廠生產的 adapter 會使得Call在異步調用時在指定的 Executor 上執行回調 
           // 採用了策略模式
          return new ExecutorCallAdapterFactory(callbackExecutor);
        }
        
        static class MainThreadExecutor implements Executor {
        // 獲取與Android 主線程綁定的Handler 
          private final Handler handler = new Handler(Looper.getMainLooper());
        
          @Override public void execute(Runnable r) {
           // 該Handler是上面獲取的與Android 主線程綁定的Handler 
            // 在UI線程進行對網絡請求返回數據處理等操做。
            handler.post(r);
          }
        }
    }

2.2 Retrofit對象調用baseUrl(url)源碼解析

  • 都知道這個方法主要是設置baseUrl。源碼以下所示

    • 首先先對baseUrl進行非空判斷。而後再解析baseUrl,若是解析的httpUrl爲null,則會拋出IllegalArgumentException非法參數異常。那麼思考一下,什麼狀況下解析baseUrl會致使解析內容httpUrl爲null呢?
    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);
    }
  • HttpUrl是如何解析url,對url有什麼條件,來看一下parse方法源碼

    • 能夠看到url必須是以http或者https才能夠。若是隨便寫一個url,則會出問題
    public static @Nullable HttpUrl parse(String url) {
        Builder builder = new Builder();
        Builder.ParseResult result = builder.parse(null, url);
        return result == Builder.ParseResult.SUCCESS ? builder.build() : null;
    }
    
    ParseResult parse(@Nullable HttpUrl base, String input) {
      int pos = skipLeadingAsciiWhitespace(input, 0, input.length());
      int limit = skipTrailingAsciiWhitespace(input, pos, input.length());
    
      // Scheme.
      int schemeDelimiterOffset = schemeDelimiterOffset(input, pos, limit);
      if (schemeDelimiterOffset != -1) {
        if (input.regionMatches(true, pos, "https:", 0, 6)) {
          this.scheme = "https";
          pos += "https:".length();
        } else if (input.regionMatches(true, pos, "http:", 0, 5)) {
          this.scheme = "http";
          pos += "http:".length();
        } else {
          return ParseResult.UNSUPPORTED_SCHEME; // Not an HTTP scheme.
        }
      } else if (base != null) {
        this.scheme = base.scheme;
      } else {
        return ParseResult.MISSING_SCHEME; // No scheme.
      }
      
      //下面代碼省略了
  • 思考一下,傳遞的url爲何是String BASE_URL = "http://beta.goldenalpha.com.cn/"這個格式呢?接着看看baseUrl(httpUrl)源碼

    • 能夠看到這裏的url地址必須是以/結尾。因此若是是沒有加上/,則會出現異常
    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;
    }
  • 其實我的感受這塊工做並不難,只要有點英文基礎的,就能夠徹底看的明白。接着往下分析,若是想了解更多,歡迎看個人博客彙總:https://github.com/yangchong2...

2.3 addConverterFactory(Converter.Factory factory)源碼分析

  • 在建立的時候會調用addConverterFactory(GsonConverterFactory.create(JsonUtils.getJson()))添加Gson轉換器

    • 這個方法主要是添加用於對象序列化和反序列化的轉換器工廠
    • 將上面建立的GsonConverterFactory放入到 converterFactories數組
    .addConverterFactory(GsonConverterFactory.create(JsonUtils.getGson()))
    
    //看這行代碼
    public Builder addConverterFactory(Converter.Factory factory) {
        //將上面建立的GsonConverterFactory放入到 converterFactories數組
      converterFactories.add(checkNotNull(factory, "factory == null"));
      return this;
    }
  • 而後看看GsonConverterFactory.creat()方法源碼

    • 使用{@code gson}建立一個實例以進行轉換。編碼到JSON並從JSON解碼(當沒有由頭指定字符集時)將使用UTF-8。
    • 建立了一個含有Gson對象實例的GsonConverterFactory,並返回給addConverterFactory()
    public static GsonConverterFactory create(Gson gson) {
        if (gson == null) throw new NullPointerException("gson == null");
        return new GsonConverterFactory(gson);
    }

2.4 addCallAdapterFactory(RxJava2CallAdapterFactory.create())源碼分析

  • 添加一個調用適配器工廠,用於支持服務方法返回類型

    public Builder addCallAdapterFactory(CallAdapter.Factory factory) {
      callAdapterFactories.add(checkNotNull(factory, "factory == null"));
      return this;
    }
  • CallAdapterFactory:注意Factory生產的是CallAdapter,那麼CallAdapter又是什麼呢?

    • 能夠看到CallAdapter源代碼以下所示,它是一個接口。主要做用是:將響應類型{@代碼R}的{@連接調用}改編爲{@代碼T}的類型。實例由{@LinkplanFactory(一個工廠)建立,該工廠}是{@Link平原Retrofit.Builder#addCallAdapterFactory(Factory)已安裝}到{@LinkRetroflit}實例中。
    • 網絡請求執行器(Call)的適配器,而且在Retrofit中提供了三種CallAdapterFactory: ExecutorCallAdapterFactory(默認)、DefaultCallAdapterFactory、RxJava2CallAdapterFactory
    public interface CallAdapter<R, T> {
      Type responseType();
      T adapt(Call<R> call);
      abstract class Factory {
        public abstract @Nullable CallAdapter<?, ?> get(Type returnType, Annotation[] annotations,
            Retrofit retrofit);
        protected static Type getParameterUpperBound(int index, ParameterizedType type) {
          return Utils.getParameterUpperBound(index, type);
        }
        protected static Class<?> getRawType(Type type) {
          return Utils.getRawType(type);
        }
      }
    }
  • 接着,有夥伴可能會問它的做用是什麼呢?

    • 將默認的網絡請求執行器(OkHttpCall)轉換成適合被不一樣平臺來調用的網絡請求執行器形式
    • 一開始Retrofit只打算利用OkHttpCall經過ExecutorCallbackCall切換線程;但後來發現使用Rxjava更加方便(不須要Handler來切換線程)。想要實現Rxjava的狀況,那就得使用RxJavaCallAdapterFactoryCallAdapter將OkHttpCall轉換成Rxjava(Scheduler)
    .addCallAdapterFactory(RxJava2CallAdapterFactory.create())

2.5 client(okHttpClient)源碼分析

  • 用於請求的HTTP客戶端

    • 指定用於建立{@link Call}實例的自定義調用工廠。
    public Builder client(OkHttpClient client) {
      return callFactory(checkNotNull(client, "client == null"));
    }
    
    public Builder callFactory(okhttp3.Call.Factory factory) {
      this.callFactory = checkNotNull(factory, "factory == null");
      return this;
    }

2.6 Retrofit對象調用build()源碼解析

  • 看看源碼

    • 大概的流程就是建立適配器的防護性副本,並添加默認調用適配器。而後複製轉換器的防護性副本,在而後添加內置的轉化工廠.這能夠防止重寫其行爲,但也能夠確保在使用消耗全部類型的轉換器時的正確行爲。
    • 經過前面步驟設置的變量,將Retrofit類的全部成員變量都配置完畢。就成功建立了對象!
    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> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
      callAdapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));
    
      // Make a defensive copy of the converters.
      List<Converter.Factory> converterFactories =
          new ArrayList<>(1 + this.converterFactories.size());
    
      // Add the built-in converter factory first. This prevents overriding its behavior but also
      // ensures correct behavior when using converters that consume all types.
      converterFactories.add(new BuiltInConverters());
      converterFactories.addAll(this.converterFactories);
    
      return new Retrofit(callFactory, baseUrl, unmodifiableList(converterFactories),
          unmodifiableList(callAdapterFactories), callbackExecutor, validateEagerly);
    }
  • 而後看看Retrofit的構造方法

    • 成功創建一個Retrofit對象的標準:配置好Retrofit類裏的成員變量,即配置好:

      • serviceMethod:包含全部網絡請求信息的對象
      • baseUrl:網絡請求的url地址
      • callFactory:網絡請求工廠
      • adapterFactories:網絡請求適配器工廠的集合
      • converterFactories:數據轉換器工廠的集合
      • callbackExecutor:回調方法執行器
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;
}
  • 而後總結一下建立的過程

    • 平臺類型對象(Platform - Android)
    • 網絡請求的url地址(baseUrl)
    • 網絡請求工廠(callFactory) 默認使用OkHttpCall
    • 網絡請求適配器工廠的集合(adapterFactories) 本質是配置了網絡請求適配器工廠- 默認是ExecutorCallAdapterFactory
    • 數據轉換器工廠的集合(converterFactories) 本質是配置了數據轉換器工廠
    • 回調方法執行器(callbackExecutor) 默認回調方法執行器做用是:切換線程(子線程 - 主線程)

3.建立ServiceMethod流程源碼分析

3.1 首先看看請求網絡代碼過程

  • 大概的流程以下代碼所示

    • 定義網絡請求的接口類 ApiService
    public interface ApiService {
        @POST("api/v1/user/old")
        Call<ResEntity<UserOld>> isUserOld();
    }
    
    //建立接口類實例
    ApiService apiService = retrofit.create(ApiService.class);
    //生成最終的網絡請求對象
    Call<ResEntity<UserOld>> userOld = apiService.isUserOld();
    //異步機制
    userOld.enqueue(new Callback<ResEntity<UserOld>>() {
        @Override
        public void onResponse(Call<ResEntity<UserOld>> call, retrofit2.Response<ResEntity<UserOld>> response) {
            
        }
        @Override
        public void onFailure(Call<ResEntity<UserOld>> call, Throwable t) {
    
        }
    });

3.2 分析create(final Class<T> service)源碼

  • 源代碼以下所示,這段代碼很重要。

    • 建立接口定義的API端點的實現。給定方法的相對路徑是從描述請求類型的方法的註釋中得到的。
    • 先對service類進行判斷是不是接口。這個時候你就知道爲什麼只能定義service爲接口呢……
    • 接着就建立了ServiceMethod對象,而且把這個對象以鍵的形式存儲到ConcurrentHashMap集合中
    • 最後建立了網絡請求接口的動態代理對象,經過代理模式中的動態代理模式,動態生成網絡請求接口的代理類,並將代理類的實例建立交給InvocationHandler類 做爲具體的實現,並最終返回一個動態代理對象。

      • service.getClassLoader()做用是動態生成接口的實現類
      • new Class<?>[] { service }做用是動態建立實例
      • new InvocationHandler()做用是將代理類的實現交給 InvocationHandler類做爲具體的實現
    • 即經過動態生成的代理類,調用interfaces接口的方法其實是經過調用InvocationHandler對象的invoke方法來完成指定的功能
    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對象
            ServiceMethod<Object, Object> serviceMethod =
                (ServiceMethod<Object, Object>) loadServiceMethod(method);
            //根據配置好的serviceMethod對象建立okHttpCall對象 
            OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
            //調用OkHttp,並根據okHttpCall返回rejava的Observe對象或者返回Call
            return serviceMethod.adapt(okHttpCall);
          }
        });
    }
  • 接着看一下validateServiceInterface方法操做了什麼?

    • 經過這個方法可知,若是service類不是接口則會拋異常。同時須要注意API接口不能擴展其餘接口
    static <T> void validateServiceInterface(Class<T> service) {
        if (!service.isInterface()) {
          throw new IllegalArgumentException("API declarations must be interfaces.");
        }
        // Prevent API interfaces from extending other interfaces. This not only avoids a bug in
        // Android (http://b.android.com/58753) but it forces composition of API declarations which is
        // the recommended pattern.
        if (service.getInterfaces().length > 0) {
          throw new IllegalArgumentException("API interfaces must not extend other interfaces.");
        }
    }
  • 接着看看eagerlyValidateMethods這個方法的源碼

    • 判斷是否須要提早驗證,主要是給接口中每一個方法的註解進行解析並獲得一個ServiceMethod對象,而後以Method爲鍵將該對象存入serviceMethodCache集合中。該集合是一個ConcurrentHashMap集合。
    • 關於ConcurrentHashMap集合的源碼分析,能夠參考個人這篇博客ConcurrentHashMap
    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;
    }
  • 知道return (T) roxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler invocationHandler)經過代理模式中的動態代理模式,在面試中也常常會問到該模式,那麼該模式有什麼特色呢?

    • 當NetService對象調用getCall()接口中方法時會進行攔截,調用都會集中轉發到 InvocationHandler#invoke (),可集中進行處理
    • 得到網絡請求接口實例上的全部註解
  • 接着看看loadServiceMethod(Method method)方法源碼

    • 能夠看到先從serviceMethodCache集合中獲取result對象,而後對result進行非空判斷
    • 而且經過synchronized關鍵字設置了線程同步鎖,建立ServiceMethod對象前,先看serviceMethodCache有沒有緩存以前建立過的網絡請求實例,若沒緩存,則經過建造者模式建立 serviceMethod 對象。建立實例的緩存機制:採用單例模式從而實現一個 ServiceMethod 對象對應於網絡請求接口裏的一個方法
    • 針對synchronized關鍵字的做用能夠參考個人這篇博客:https://blog.csdn.net/m0_3770...
    • 針對單利設計模式總結筆記:https://blog.csdn.net/m0_3770...
    • image

3.3 serviceMethod對象的建立過程

  • 建立以前,首先會嘗試根據方法從一個緩存列表中取出ServiceMethod實例,若是沒有,在鎖保護以後,還有再嘗試一次,仍是沒有的狀況下,纔會去建立ServiceMethod。

    • image
  • 第一步,先看看ServiceMethod的Builder方法

    • 除了傳遞了兩個參數外,還獲取網絡請求接口方法裏的註釋,獲取網絡請求接口方法裏的參數類型,獲取網絡請求接口方法裏的註解內容
    Builder(Retrofit retrofit, Method method) {
      this.retrofit = retrofit;
      this.method = method;
      this.methodAnnotations = method.getAnnotations();
      this.parameterTypes = method.getGenericParameterTypes();
      this.parameterAnnotationsArray = method.getParameterAnnotations();
    }
  • 第二步,而後看看ServiceMethod的build()方法

    • 根據網絡請求接口方法的返回值和註解類型,從Retrofit對象中獲取對應的網絡請求適配器callAdapter對象
    • 網絡請求接口方法的返回值和註解類型,從Retrofit對象中獲取該網絡適配器返回的數據類型responseType
    • 而後對responseType類型進行判斷,若是是Response類型或者okhttp3.Response類型,則拋出異常
    • 根據網絡請求接口方法的返回值和註解類型,從Retrofit對象中獲取對應的數據轉換器responseConverter對象
    • 而後採用for循環解析網絡請求接口中方法的註解,註解包括:DELETE、GET、POST、HEAD、PATCH、PUT、OPTIONS、HTT等等
    • 若是httpMethod爲null。則拋出異常
    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);
      }
    
      if (httpMethod == null) {
        throw methodError("HTTP method annotation is required (e.g., @GET, @POST, etc.).");
      }
    
      if (!hasBody) {
        if (isMultipart) {
          throw methodError(
              "Multipart can only be specified on HTTP methods with request body (e.g., @POST).");
        }
        if (isFormEncoded) {
          throw methodError("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; 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);
      }
    
      if (relativeUrl == null && !gotUrl) {
        throw methodError("Missing either @%s URL or @Url parameter.", httpMethod);
      }
      if (!isFormEncoded && !isMultipart && !hasBody && gotBody) {
        throw methodError("Non-body HTTP method cannot contain @Body.");
      }
      if (isFormEncoded && !gotField) {
        throw methodError("Form-encoded method must contain at least one @Field.");
      }
      if (isMultipart && !gotPart) {
        throw methodError("Multipart method must contain at least one @Part.");
      }
    
      return new ServiceMethod<>(this);
    }
  • 第三步,看看ServiceMethod(Builder<R, T> builder) 構造方法

    • 能夠看到這裏都是參數賦值操做
    ServiceMethod(Builder<R, T> builder) {
        this.callFactory = builder.retrofit.callFactory();
        this.callAdapter = builder.callAdapter;
        this.baseUrl = builder.retrofit.baseUrl();
        this.responseConverter = builder.responseConverter;
        this.httpMethod = builder.httpMethod;
        this.relativeUrl = builder.relativeUrl;
        this.headers = builder.headers;
        this.contentType = builder.contentType;
        this.hasBody = builder.hasBody;
        this.isFormEncoded = builder.isFormEncoded;
        this.isMultipart = builder.isMultipart;
        this.parameterHandlers = builder.parameterHandlers;
    }

4.註解的解析

4.1 callAdapter的建立源碼分析

  • 首先獲取method的對象表示的方法的形式類型。而後獲取method的註解。重點看看retrofit.callAdapter(returnType, annotations)主要作了什麼?

    private CallAdapter<T, R> createCallAdapter() {
      Type returnType = method.getGenericReturnType();
      if (Utils.hasUnresolvableType(returnType)) {
        throw methodError(
            "Method return type must not include a type variable or wildcard: %s", returnType);
      }
      if (returnType == void.class) {
        throw methodError("Service methods cannot return void.");
      }
      Annotation[] annotations = method.getAnnotations();
      try {
        //noinspection unchecked
        return (CallAdapter<T, R>) retrofit.callAdapter(returnType, annotations);
      } catch (RuntimeException e) { // Wide exception range because factories are user code.
        throw methodError(e, "Unable to create call adapter for %s", returnType);
      }
    }
  • 看看retrofit.callAdapter(returnType, annotations)源碼

    public CallAdapter<?, ?> callAdapter(Type returnType, Annotation[] annotations) {
        return nextCallAdapter(null, returnType, annotations);
    }
    
    public CallAdapter<?, ?> nextCallAdapter(@Nullable CallAdapter.Factory skipPast, Type returnType,
      Annotation[] annotations) {
    checkNotNull(returnType, "returnType == null");
    checkNotNull(annotations, "annotations == null");
    
    int start = callAdapterFactories.indexOf(skipPast) + 1;
    for (int i = start, count = callAdapterFactories.size(); i < count; i++) {
      CallAdapter<?, ?> adapter = callAdapterFactories.get(i).get(returnType, annotations, this);
      if (adapter != null) {
        return adapter;
      }
    }
    
    StringBuilder builder = new StringBuilder("Could not locate call adapter for ")
        .append(returnType)
        .append(".\n");
    if (skipPast != null) {
      builder.append("  Skipped:");
      for (int i = 0; i < start; i++) {
        builder.append("\n   * ").append(callAdapterFactories.get(i).getClass().getName());
      }
      builder.append('\n');
    }
    builder.append("  Tried:");
    for (int i = start, count = callAdapterFactories.size(); i < count; i++) {
      builder.append("\n   * ").append(callAdapterFactories.get(i).getClass().getName());
    }
    throw new IllegalArgumentException(builder.toString());
    }

4.2 responseConverter的建立源碼分析

5.OkHttpCall的建立源碼分析

5.1 new OkHttpCall<>(serviceMethod, args)源碼分析

  • 能夠看到建立OkHttpCall對象須要兩個參數,參數分別是配置好的ServiceMethod對象和輸入的請求參數

    • 源碼以下所示
    final class OkHttpCall<T> implements Call<T> {
      private final ServiceMethod<T, ?> serviceMethod;
      private final @Nullable Object[] args;
    
      OkHttpCall(ServiceMethod<T, ?> serviceMethod, @Nullable Object[] args) {
        //含有全部網絡請求參數信息的對象
        this.serviceMethod = serviceMethod;
        //網絡請求接口的參數 
        this.args = args;
      }
    }
  • 接着看看return serviceMethod.adapt(okHttpCall)源碼分析

    • 建立的OkHttpCall對象傳給第一步建立的serviceMethod對象中對應的網絡請求適配器工廠的adapt()
    • 返回對象類型:Android默認的是Call<>;若設置了RxJavaCallAdapterFactory,返回的則是Observable<>。若是這個地方不理解,能夠繼續往下看
    T adapt(Call<R> call) {
        return callAdapter.adapt(call);
    }
  • 接着看看實際的調用

    • ApiService對象其實是動態代理對象Proxy.newProxyInstance(),並非真正的網絡請求接口建立的對象
    • 當ApiService對象調用isUserOld()時會被動態代理對象Proxy.newProxyInstance()攔截,而後調用自身的InvocationHandler # invoke()
    • invoke(Object proxy, Method method, Object... args)會傳入3個參數:Object proxy:(代理對象)、Method method(調用的isUserOld()),Object... args(方法的參數,即getCall(*)中的)
    • 接下來利用Java反射獲取到isUserOld()的註解信息,配合args參數建立ServiceMethod對象。
    • 最終建立並返回一個OkHttpCall類型的Call對象或者Observable

      • OkHttpCall類是OkHttp的包裝類
      • 建立了OkHttpCall類型的Call對象還不能發送網絡請求,須要建立Request對象【也就是異步請求方法】才能發送網絡請求
ApiService apiService = retrofit.create(ApiService.class);
//返回Android默認的Call
Call<ResEntity<UserOld>> userOld = apiService.isUserOld();


//返回的則是Observable<T>
Observable<AdvertCommon> advert = mApiService.getSplashImage(method)

6.OkHttpCall的網絡請求

6.1 OkHttpCall.execute()同步請求

  • 使用方法Response<Bean> response = call.execute();

    • 實際開發中這種我也沒有用過……哈哈
    • 首先添加一個synchronized同步鎖。先建立一個OkHttp的Request對象請求,而後調用OkHttpCall的execute()發送網絡請求,再而後解析網絡請求返回的數據。
    • 須要注意:

      • 發送網絡請求時,OkHttpCall須要從ServiceMethod中得到一個Request對象
@Override public Response<T> execute() throws IOException {
    okhttp3.Call call;

    synchronized (this) {
      if (executed) throw new IllegalStateException("Already executed.");
      executed = true;

      if (creationFailure != null) {
        if (creationFailure instanceof IOException) {
          throw (IOException) creationFailure;
        } else if (creationFailure instanceof RuntimeException) {
          throw (RuntimeException) creationFailure;
        } else {
          throw (Error) creationFailure;
        }
      }

      call = rawCall;
      if (call == null) {
        try {
          call = rawCall = createRawCall();
        } catch (IOException | RuntimeException | Error e) {
          throwIfFatal(e); //  Do not assign a fatal error to creationFailure.
          creationFailure = e;
          throw e;
        }
      }
    }

    if (canceled) {
      call.cancel();
    }

    return parseResponse(call.execute());
  }
  
  //從serviceMethod一個Request對象
private okhttp3.Call createRawCall() throws IOException {
    okhttp3.Call call = serviceMethod.toCall(args);
    if (call == null) {
      throw new NullPointerException("Call.Factory returned null.");
    }
    return call;
}

6.2 OkHttpCall.enqueue()異步請求

  • 關於異步操做封裝庫,能夠看個人開源線程池封裝庫:https://github.com/yangchong2...
  • 如何調用能夠看前面的代碼介紹。這裏就不介紹呢!

    • 首先添加一個synchronized同步鎖。建立OkHttp的Request對象,而後發送網絡請求,而後解析返回數據。在這裏,可能會想到,到底是如何作到異步操做的呢?
    @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) {
              throwIfFatal(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) {
            Response<T> response;
            try {
              response = parseResponse(rawResponse);
            } catch (Throwable e) {
              callFailure(e);
              return;
            }
    
            try {
              callback.onResponse(OkHttpCall.this, response);
            } catch (Throwable t) {
              t.printStackTrace();
            }
          }
    
          @Override public void onFailure(okhttp3.Call call, IOException e) {
            callFailure(e);
          }
    
          private void callFailure(Throwable e) {
            try {
              callback.onFailure(OkHttpCall.this, e);
            } catch (Throwable t) {
              t.printStackTrace();
            }
          }
        });
      }
      
      
    private okhttp3.Call createRawCall() throws IOException {
        okhttp3.Call call = serviceMethod.toCall(args);
        if (call == null) {
          throw new NullPointerException("Call.Factory returned null.");
        }
        return call;
     }
  • 從上能夠知道,call操做異步,那麼這個call是什麼呢?那麼咱們看一下ExecutorCallAdapterFactory這個類,關於CallAdapterFactory是作什麼用的?前面已經介紹呢!

    • 若是你對異步線程還不是很熟悉,能夠參考個人線程池封裝庫,裏面已經很詳細實現了異步線程操做,參考連接:https://github.com/yangchong2...
    • 線程切換,即將子線程切換到主線程,從而在主線程對返回的數據結果進行處理
    static final class ExecutorCallbackCall<T> implements Call<T> {
    final Executor callbackExecutor;
    final Call<T> delegate;
    
    ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) {
      this.callbackExecutor = callbackExecutor;
      this.delegate = delegate;
    }
    
    @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);
            }
          });
        }
      });
    }

6.3 parseResponse解析網絡數據源碼解析

  • 解析網絡數據

    • 關於網絡狀態欄,我已經整理了一篇十分詳細的博客,能夠看個人這篇文章:07.Http狀態碼詳解
    • 調用serviceMethod.toResponse(catchingBody),解析數據時,還須要經過ServiceMethod使用Converter(數據轉換器)轉換成Java對象進行數據解析
    • 關於網絡請求的基礎介紹,能夠參考個人這篇博客:https://blog.csdn.net/m0_3770...
    Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
        ResponseBody rawBody = rawResponse.body();
    
        // Remove the body's source (the only stateful object) so we can pass the response along.
        rawResponse = rawResponse.newBuilder()
            .body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength()))
            .build();
    
        int code = rawResponse.code();
        if (code < 200 || code >= 300) {
          try {
            // Buffer the entire body to avoid future I/O.
            ResponseBody bufferedBody = Utils.buffer(rawBody);
            return Response.error(bufferedBody, rawResponse);
          } finally {
            rawBody.close();
          }
        }
    
        if (code == 204 || code == 205) {
          rawBody.close();
          return Response.success(null, rawResponse);
        }
    
        ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody);
        try {
          T body = serviceMethod.toResponse(catchingBody);
          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;
        }
      }

關於其餘內容介紹

01.關於博客彙總連接

02.關於個人博客

相關文章
相關標籤/搜索