成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

Retrofit源碼分析

zero / 3783人閱讀

摘要:看下圖所示,摘自網(wǎng)絡(luò)的創(chuàng)建流程源碼分析實例是使用建造者模式通過類進行創(chuàng)建的。創(chuàng)建了一個含有對象實例的,并返回給源碼分析添加一個調(diào)用適配器工廠,用于支持服務(wù)方法返回類型注意生產(chǎn)的是,那么又是什么呢可以看到源代碼如下所示,它是一個接口。

目錄介紹

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

2.Retrofit的創(chuàng)建流程源碼分析

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

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

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

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

2.5 client(okHttpClient)源碼分析

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

3.創(chuàng)建ServiceMethod流程源碼分析

3.1 首先看看請求網(wǎng)絡(luò)代碼過程

3.2 分析create(final Class service)源碼

3.3 serviceMethod對象的創(chuàng)建過程

4.注解的解析

4.1 callAdapter的創(chuàng)建源碼分析

4.2 responseConverter的創(chuàng)建源碼分析

5.OkHttpCall的創(chuàng)建源碼分析

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

6.OkHttpCall的網(wǎng)絡(luò)請求

6.1 OkHttpCall.execute()同步請求

6.2 OkHttpCall.enqueue()異步請求

6.3 parseResponse解析網(wǎng)絡(luò)數(shù)據(jù)源碼解析

好消息

博客筆記大匯總【16年3月到至今】,包括Java基礎(chǔ)及深入知識點,Android技術(shù)博客,Python學(xué)習(xí)筆記等等,還包括平時開發(fā)中遇到的bug匯總,當(dāng)然也在工作之余收集了大量的面試題,長期更新維護并且修正,持續(xù)完善……開源的文件是markdown格式的!同時也開源了生活博客,從12年起,積累共計47篇[近20萬字],轉(zhuǎn)載請注明出處,謝謝!

鏈接地址:https://github.com/yangchong2...

如果覺得好,可以star一下,謝謝!當(dāng)然也歡迎提出建議,萬事起于忽微,量變引起質(zhì)變!

關(guān)于Retrofit的使用,可以參考這篇文章:https://www.jianshu.com/p/989...

0.思考問題,針對以下問題,看了這篇博客,應(yīng)該有了初步的認(rèn)識

0.0.1 Retrofit創(chuàng)建中用了哪些設(shè)計模式,請談?wù)勈褂眠@些設(shè)計模式的優(yōu)勢?

0.0.2 Retrofit在創(chuàng)建的時候為什么要判斷是否在Android環(huán)境中,是如何做到的?

0.0.3 為什么設(shè)置baseUrl的時候,會以/結(jié)尾,如果沒有/會出現(xiàn)什么問題?

0.0.4 addConverterFactory的主要作用是什么?

0.0.5 Factory生產(chǎn)的是CallAdapter,那么CallAdapter又是什么呢?

0.0.6 網(wǎng)絡(luò)請求的類 service為什么要定義成接口?如果不定義成接口會出現(xiàn)什么情況?

0.0.7 創(chuàng)建了ServiceMethod對象是干什么用的?它是用什么進行存儲的?

0.0.8 創(chuàng)建ServiceMethod對象為什么要添加synchronized同步鎖

0.0.9 call調(diào)用enqueue異步方法中源碼是如何實現(xiàn)異步切換線程的?原理是怎樣的?

0.1.0 ServiceMethod是如何保存網(wǎng)絡(luò)請求所需要的數(shù)據(jù),具體保存了哪些數(shù)據(jù)呢?

0.1.1 網(wǎng)絡(luò)傳輸都是二進制流,那么解析數(shù)據(jù)時,如何通過ServiceMethod使用Converter轉(zhuǎn)換成Java對象進行數(shù)據(jù)解析

//AdvertCommon是javabean實體類,并沒有序列化,那么網(wǎng)絡(luò)解析數(shù)據(jù)如何解析java對象呢?
Call getSplashImage(@Query("type") int type);

0.1.2 如下所示,為什么說apiService對象實際上是動態(tài)代理對象,而不是真正的網(wǎng)絡(luò)請求接口創(chuàng)建的對象

ApiService apiService = retrofit.create(ApiService.class);

0.1.3 如何理解動態(tài)代理的機制。retrofit是如何加載接口類ApiService的,為什么這個類要設(shè)置成接口?

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

Api接口

public interface DouBookApi {
    /**
    * 根據(jù)tag獲取圖書
    * @param tag  搜索關(guān)鍵字
    * @param count 一次請求的數(shù)目 最多100

    */
    @GET("v2/book/search")
    Observable 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 getHotMovie(String tag, int start , int count) {
        Observable book = mApiService.getBook(tag, start, count);
        return book;
    }
}

抽取類

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 避免同時調(diào)用多個接口,導(dǎo)致線程并發(fā)
        synchronized (RetrofitWrapper.class){
            instance = new RetrofitWrapper(url);
        }
        return instance;
    }

    public  T create(final Class service) {
        return mRetrofit.create(service);
    }
}

使用

DouBookModel model = DouBookModel.getInstance(activity);
model.getHotMovie(mType,start,count)
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(new Subscriber() {
            @Override
            public void onCompleted() {

            }

            @Override
            public void onError(Throwable e) {

            }

            @Override
            public void onNext(DouBookBean bookBean) {

            }
        });

針對Retrofit,需要注意

Retrofit 是一個 RESTful 的 HTTP 網(wǎng)絡(luò)請求框架的封裝。網(wǎng)絡(luò)請求的工作本質(zhì)上是 OkHttp 完成,而 Retrofit 僅負(fù)責(zé) 網(wǎng)絡(luò)請求接口的封裝??聪聢D所示,摘自網(wǎng)絡(luò)

2.Retrofit的創(chuàng)建流程源碼分析

Retrofit實例是使用建造者模式通過Builder類進行創(chuàng)建的。Builder模式的優(yōu)勢:將一個復(fù)雜對象的構(gòu)建與表示分離,使得用戶在不知道對象的創(chuàng)建細(xì)節(jié)情況下就可以直接創(chuàng)建復(fù)雜的對象。

針對builder設(shè)計模式,可以參考我的這篇博客:https://blog.csdn.net/m0_3770...

關(guān)于使用builder模式寫代碼的案例,可以參考我的彈窗封裝lib:https://github.com/yangchong2...

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

首先看看里面的源代碼,如下所示

可以看到Platform.get()獲取的是單利對象。那么也許你會問,這個方法的作用主要是什么呢?通過Class.forName獲取類名的方式,來判斷當(dāng)前的環(huán)境是否在Android中,這在之后獲取默認(rèn)的CallAdapterFactory時候?qū)玫健O旅嫖視治龅健?/p>

關(guān)于單利設(shè)計模式,如果還有疑問,或者想知道所有的獲取單利的方法,可以參考我的這篇博客:設(shè)計模式之一:單例模式

//第一步
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平臺,就創(chuàng)建并返回一個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() {
        // 返回一個默認(rèn)的回調(diào)方法執(zhí)行器
        // 該執(zhí)行器作用:切換線程(子->>主線程),并在主線程(UI線程)中執(zhí)行回調(diào)方法
      return new MainThreadExecutor();
    }
    
    @Override CallAdapter.Factory defaultCallAdapterFactory(@Nullable Executor callbackExecutor) {
      if (callbackExecutor == null) throw new AssertionError();
       // 創(chuàng)建默認(rèn)的網(wǎng)絡(luò)請求適配器工廠 
       // 該默認(rèn)工廠生產(chǎn)的 adapter 會使得Call在異步調(diào)用時在指定的 Executor 上執(zhí)行回調(diào) 
       // 采用了策略模式
      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線程進行對網(wǎng)絡(luò)請求返回數(shù)據(jù)處理等操作。
        handler.post(r);
      }
    }
}

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

都知道這個方法主要是設(shè)置baseUrl。源碼如下所示

首先先對baseUrl進行非空判斷。然后再解析baseUrl,如果解析的httpUrl為null,則會拋出IllegalArgumentException非法參數(shù)異常。那么思考一下,什么情況下解析baseUrl會導(dǎo)致解析內(nèi)容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或者h(yuǎn)ttps才可以。如果隨便寫一個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地址必須是以/結(jié)尾。所以如果是沒有加上/,則會出現(xiàn)異常

public Builder baseUrl(HttpUrl baseUrl) {
  checkNotNull(baseUrl, "baseUrl == null");
  List pathSegments = baseUrl.pathSegments();
  if (!"".equals(pathSegments.get(pathSegments.size() - 1))) {
    throw new IllegalArgumentException("baseUrl must end in /: " + baseUrl);
  }
  this.baseUrl = baseUrl;
  return this;
}

其實個人感覺這塊工作并不難,只要有點英文基礎(chǔ)的,就可以完全看的明白。接著往下分析,如果想了解更多,歡迎看我的博客匯總:https://github.com/yangchong2...

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

在創(chuàng)建的時候會調(diào)用addConverterFactory(GsonConverterFactory.create(JsonUtils.getJson()))添加Gson轉(zhuǎn)換器

這個方法主要是添加用于對象序列化和反序列化的轉(zhuǎn)換器工廠

將上面創(chuàng)建的GsonConverterFactory放入到 converterFactories數(shù)組

.addConverterFactory(GsonConverterFactory.create(JsonUtils.getGson()))

//看這行代碼
public Builder addConverterFactory(Converter.Factory factory) {
    //將上面創(chuàng)建的GsonConverterFactory放入到 converterFactories數(shù)組
  converterFactories.add(checkNotNull(factory, "factory == null"));
  return this;
}

然后看看GsonConverterFactory.creat()方法源碼

使用{@code gson}創(chuàng)建一個實例以進行轉(zhuǎn)換。編碼到JSON并從JSON解碼(當(dāng)沒有由頭指定字符集時)將使用UTF-8。

創(chuàng)建了一個含有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())源碼分析

添加一個調(diào)用適配器工廠,用于支持服務(wù)方法返回類型

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

CallAdapterFactory:注意Factory生產(chǎn)的是CallAdapter,那么CallAdapter又是什么呢?

可以看到CallAdapter源代碼如下所示,它是一個接口。主要作用是:將響應(yīng)類型{@代碼R}的{@鏈接調(diào)用}改編為{@代碼T}的類型。實例由{@LinkplanFactory(一個工廠)創(chuàng)建,該工廠}是{@Link平原Retrofit.Builder#addCallAdapterFactory(Factory)已安裝}到{@LinkRetroflit}實例中。

網(wǎng)絡(luò)請求執(zhí)行器(Call)的適配器,并且在Retrofit中提供了三種CallAdapterFactory: ExecutorCallAdapterFactory(默認(rèn))、DefaultCallAdapterFactory、RxJava2CallAdapterFactory

public interface CallAdapter {
  Type responseType();
  T adapt(Call 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);
    }
  }
}

接著,有伙伴可能會問它的作用是什么呢?

將默認(rèn)的網(wǎng)絡(luò)請求執(zhí)行器(OkHttpCall)轉(zhuǎn)換成適合被不同平臺來調(diào)用的網(wǎng)絡(luò)請求執(zhí)行器形式

一開始Retrofit只打算利用OkHttpCall通過ExecutorCallbackCall切換線程;但后來發(fā)現(xiàn)使用Rxjava更加方便(不需要Handler來切換線程)。想要實現(xiàn)Rxjava的情況,那就得使用RxJavaCallAdapterFactoryCallAdapter將OkHttpCall轉(zhuǎn)換成Rxjava(Scheduler)

.addCallAdapterFactory(RxJava2CallAdapterFactory.create())

2.5 client(okHttpClient)源碼分析

用于請求的HTTP客戶端

指定用于創(chuàng)建{@link Call}實例的自定義調(diào)用工廠。

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對象調(diào)用build()源碼解析

看看源碼

大概的流程就是創(chuàng)建適配器的防御性副本,并添加默認(rèn)調(diào)用適配器。然后復(fù)制轉(zhuǎn)換器的防御性副本,在然后添加內(nèi)置的轉(zhuǎn)化工廠.這可以防止重寫其行為,但也可以確保在使用消耗所有類型的轉(zhuǎn)換器時的正確行為。

通過前面步驟設(shè)置的變量,將Retrofit類的所有成員變量都配置完畢。就成功創(chuàng)建了對象!

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 callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
  callAdapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));

  // Make a defensive copy of the converters.
  List 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的構(gòu)造方法

成功建立一個Retrofit對象的標(biāo)準(zhǔn):配置好Retrofit類里的成員變量,即配置好:

serviceMethod:包含所有網(wǎng)絡(luò)請求信息的對象

baseUrl:網(wǎng)絡(luò)請求的url地址

callFactory:網(wǎng)絡(luò)請求工廠

adapterFactories:網(wǎng)絡(luò)請求適配器工廠的集合

converterFactories:數(shù)據(jù)轉(zhuǎn)換器工廠的集合

callbackExecutor:回調(diào)方法執(zhí)行器

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

然后總結(jié)一下創(chuàng)建的過程

平臺類型對象(Platform - Android)

網(wǎng)絡(luò)請求的url地址(baseUrl)

網(wǎng)絡(luò)請求工廠(callFactory) 默認(rèn)使用OkHttpCall

網(wǎng)絡(luò)請求適配器工廠的集合(adapterFactories) 本質(zhì)是配置了網(wǎng)絡(luò)請求適配器工廠- 默認(rèn)是ExecutorCallAdapterFactory

數(shù)據(jù)轉(zhuǎn)換器工廠的集合(converterFactories) 本質(zhì)是配置了數(shù)據(jù)轉(zhuǎn)換器工廠

回調(diào)方法執(zhí)行器(callbackExecutor) 默認(rèn)回調(diào)方法執(zhí)行器作用是:切換線程(子線程 - 主線程)

3.創(chuàng)建ServiceMethod流程源碼分析 3.1 首先看看請求網(wǎng)絡(luò)代碼過程

大概的流程如下代碼所示

定義網(wǎng)絡(luò)請求的接口類 ApiService

public interface ApiService {
    @POST("api/v1/user/old")
    Call> isUserOld();
}

//創(chuàng)建接口類實例
ApiService apiService = retrofit.create(ApiService.class);
//生成最終的網(wǎng)絡(luò)請求對象
Call> userOld = apiService.isUserOld();
//異步機制
userOld.enqueue(new Callback>() {
    @Override
    public void onResponse(Call> call, retrofit2.Response> response) {
        
    }
    @Override
    public void onFailure(Call> call, Throwable t) {

    }
});

3.2 分析create(final Class service)源碼

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

創(chuàng)建接口定義的API端點的實現(xiàn)。給定方法的相對路徑是從描述請求類型的方法的注釋中獲得的。

先對service類進行判斷是否是接口。這個時候你就知道為何只能定義service為接口呢……

接著就創(chuàng)建了ServiceMethod對象,并且把這個對象以鍵的形式存儲到ConcurrentHashMap集合中

最后創(chuàng)建了網(wǎng)絡(luò)請求接口的動態(tài)代理對象,通過代理模式中的動態(tài)代理模式,動態(tài)生成網(wǎng)絡(luò)請求接口的代理類,并將代理類的實例創(chuàng)建交給InvocationHandler類 作為具體的實現(xiàn),并最終返回一個動態(tài)代理對象。

service.getClassLoader()作用是動態(tài)生成接口的實現(xiàn)類

new Class[] { service }作用是動態(tài)創(chuàng)建實例

new InvocationHandler()作用是將代理類的實現(xiàn)交給 InvocationHandler類作為具體的實現(xiàn)

即通過動態(tài)生成的代理類,調(diào)用interfaces接口的方法實際上是通過調(diào)用InvocationHandler對象的invoke方法來完成指定的功能

public  T create(final Class 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);
        }
        
        //讀取網(wǎng)絡(luò)請求接口里的方法,并根據(jù)前面配置好的屬性配置serviceMethod對象
        ServiceMethod serviceMethod =
            (ServiceMethod) loadServiceMethod(method);
        //根據(jù)配置好的serviceMethod對象創(chuàng)建okHttpCall對象 
        OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
        //調(diào)用OkHttp,并根據(jù)okHttpCall返回rejava的Observe對象或者返回Call
        return serviceMethod.adapt(okHttpCall);
      }
    });
}

接著看一下validateServiceInterface方法操作了什么?

通過這個方法可知,如果service類不是接口則會拋異常。同時需要注意API接口不能擴展其他接口

static  void validateServiceInterface(Class 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集合。

關(guān)于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)通過代理模式中的動態(tài)代理模式,在面試中也經(jīng)常會問到該模式,那么該模式有什么特點呢?

當(dāng)NetService對象調(diào)用getCall()接口中方法時會進行攔截,調(diào)用都會集中轉(zhuǎn)發(fā)到 InvocationHandler#invoke (),可集中進行處理

獲得網(wǎng)絡(luò)請求接口實例上的所有注解

接著看看loadServiceMethod(Method method)方法源碼

可以看到先從serviceMethodCache集合中獲取result對象,然后對result進行非空判斷

并且通過synchronized關(guān)鍵字設(shè)置了線程同步鎖,創(chuàng)建ServiceMethod對象前,先看serviceMethodCache有沒有緩存之前創(chuàng)建過的網(wǎng)絡(luò)請求實例,若沒緩存,則通過建造者模式創(chuàng)建 serviceMethod 對象。創(chuàng)建實例的緩存機制:采用單例模式從而實現(xiàn)一個 ServiceMethod 對象對應(yīng)于網(wǎng)絡(luò)請求接口里的一個方法

針對synchronized關(guān)鍵字的作用可以參考我的這篇博客:https://blog.csdn.net/m0_3770...

針對單利設(shè)計模式總結(jié)筆記:https://blog.csdn.net/m0_3770...

3.3 serviceMethod對象的創(chuàng)建過程

創(chuàng)建之前,首先會嘗試根據(jù)方法從一個緩存列表中取出ServiceMethod實例,如果沒有,在鎖保護之后,還有再嘗試一次,還是沒有的情況下,才會去創(chuàng)建ServiceMethod。

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

除了傳遞了兩個參數(shù)外,還獲取網(wǎng)絡(luò)請求接口方法里的注釋,獲取網(wǎng)絡(luò)請求接口方法里的參數(shù)類型,獲取網(wǎng)絡(luò)請求接口方法里的注解內(nèi)容

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()方法

根據(jù)網(wǎng)絡(luò)請求接口方法的返回值和注解類型,從Retrofit對象中獲取對應(yīng)的網(wǎng)絡(luò)請求適配器callAdapter對象

網(wǎng)絡(luò)請求接口方法的返回值和注解類型,從Retrofit對象中獲取該網(wǎng)絡(luò)適配器返回的數(shù)據(jù)類型responseType

然后對responseType類型進行判斷,如果是Response類型或者okhttp3.Response類型,則拋出異常

根據(jù)網(wǎng)絡(luò)請求接口方法的返回值和注解類型,從Retrofit對象中獲取對應(yīng)的數(shù)據(jù)轉(zhuǎn)換器responseConverter對象

然后采用for循環(huán)解析網(wǎng)絡(luò)請求接口中方法的注解,注解包括: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 builder) 構(gòu)造方法

可以看到這里都是參數(shù)賦值操作

ServiceMethod(Builder 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的創(chuàng)建源碼分析

首先獲取method的對象表示的方法的形式類型。然后獲取method的注解。重點看看retrofit.callAdapter(returnType, annotations)主要做了什么?

private CallAdapter 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) 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(".
");
if (skipPast != null) {
  builder.append("  Skipped:");
  for (int i = 0; i < start; i++) {
    builder.append("
   * ").append(callAdapterFactories.get(i).getClass().getName());
  }
  builder.append("
");
}
builder.append("  Tried:");
for (int i = start, count = callAdapterFactories.size(); i < count; i++) {
  builder.append("
   * ").append(callAdapterFactories.get(i).getClass().getName());
}
throw new IllegalArgumentException(builder.toString());
}

4.2 responseConverter的創(chuàng)建源碼分析 5.OkHttpCall的創(chuàng)建源碼分析 5.1 new OkHttpCall<>(serviceMethod, args)源碼分析

可以看到創(chuàng)建OkHttpCall對象需要兩個參數(shù),參數(shù)分別是配置好的ServiceMethod對象和輸入的請求參數(shù)

源碼如下所示

final class OkHttpCall implements Call {
  private final ServiceMethod serviceMethod;
  private final @Nullable Object[] args;

  OkHttpCall(ServiceMethod serviceMethod, @Nullable Object[] args) {
    //含有所有網(wǎng)絡(luò)請求參數(shù)信息的對象
    this.serviceMethod = serviceMethod;
    //網(wǎng)絡(luò)請求接口的參數(shù) 
    this.args = args;
  }
}

接著看看return serviceMethod.adapt(okHttpCall)源碼分析

創(chuàng)建的OkHttpCall對象傳給第一步創(chuàng)建的serviceMethod對象中對應(yīng)的網(wǎng)絡(luò)請求適配器工廠的adapt()

返回對象類型:Android默認(rèn)的是Call<>;若設(shè)置了RxJavaCallAdapterFactory,返回的則是Observable<>。如果這個地方不理解,可以繼續(xù)往下看

T adapt(Call call) {
    return callAdapter.adapt(call);
}

接著看看實際的調(diào)用

ApiService對象實際上是動態(tài)代理對象Proxy.newProxyInstance(),并不是真正的網(wǎng)絡(luò)請求接口創(chuàng)建的對象

當(dāng)ApiService對象調(diào)用isUserOld()時會被動態(tài)代理對象Proxy.newProxyInstance()攔截,然后調(diào)用自身的InvocationHandler # invoke()

invoke(Object proxy, Method method, Object... args)會傳入3個參數(shù):Object proxy:(代理對象)、Method method(調(diào)用的isUserOld()),Object... args(方法的參數(shù),即getCall(*)中的)

接下來利用Java反射獲取到isUserOld()的注解信息,配合args參數(shù)創(chuàng)建ServiceMethod對象。

最終創(chuàng)建并返回一個OkHttpCall類型的Call對象或者Observable

OkHttpCall類是OkHttp的包裝類

創(chuàng)建了OkHttpCall類型的Call對象還不能發(fā)送網(wǎng)絡(luò)請求,需要創(chuàng)建Request對象【也就是異步請求方法】才能發(fā)送網(wǎng)絡(luò)請求

ApiService apiService = retrofit.create(ApiService.class);
//返回Android默認(rèn)的Call
Call> userOld = apiService.isUserOld();


//返回的則是Observable
Observable advert = mApiService.getSplashImage(method)
6.OkHttpCall的網(wǎng)絡(luò)請求 6.1 OkHttpCall.execute()同步請求

使用方法Response response = call.execute();

實際開發(fā)中這種我也沒有用過……哈哈

首先添加一個synchronized同步鎖。先創(chuàng)建一個OkHttp的Request對象請求,然后調(diào)用OkHttpCall的execute()發(fā)送網(wǎng)絡(luò)請求,再然后解析網(wǎng)絡(luò)請求返回的數(shù)據(jù)。

需要注意:

發(fā)送網(wǎng)絡(luò)請求時,OkHttpCall需要從ServiceMethod中獲得一個Request對象

  @Override public Response 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()異步請求

關(guān)于異步操作封裝庫,可以看我的開源線程池封裝庫:https://github.com/yangchong2...

如何調(diào)用可以看前面的代碼介紹。這里就不介紹呢!

首先添加一個synchronized同步鎖。創(chuàng)建OkHttp的Request對象,然后發(fā)送網(wǎng)絡(luò)請求,然后解析返回數(shù)據(jù)。在這里,可能會想到,究竟是如何做到異步操作的呢?

  @Override public void enqueue(final Callback 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 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這個類,關(guān)于CallAdapterFactory是做什么用的?前面已經(jīng)介紹呢!

如果你對異步線程還不是很熟悉,可以參考我的線程池封裝庫,里面已經(jīng)很詳細(xì)實現(xiàn)了異步線程操作,參考鏈接:https://github.com/yangchong2...

線程切換,即將子線程切換到主線程,從而在主線程對返回的數(shù)據(jù)結(jié)果進行處理

static final class ExecutorCallbackCall implements Call {
final Executor callbackExecutor;
final Call delegate;

ExecutorCallbackCall(Executor callbackExecutor, Call delegate) {
  this.callbackExecutor = callbackExecutor;
  this.delegate = delegate;
}

@Override public void enqueue(final Callback callback) {
  checkNotNull(callback, "callback == null");

  delegate.enqueue(new Callback() {
    @Override public void onResponse(Call call, final Response 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 call, final Throwable t) {
      callbackExecutor.execute(new Runnable() {
        @Override public void run() {
          callback.onFailure(ExecutorCallbackCall.this, t);
        }
      });
    }
  });
}

6.3 parseResponse解析網(wǎng)絡(luò)數(shù)據(jù)源碼解析

解析網(wǎng)絡(luò)數(shù)據(jù)

關(guān)于網(wǎng)絡(luò)狀態(tài)欄,我已經(jīng)整理了一篇十分詳細(xì)的博客,可以看我的這篇文章:07.Http狀態(tài)碼詳解

調(diào)用serviceMethod.toResponse(catchingBody),解析數(shù)據(jù)時,還需要通過ServiceMethod使用Converter(數(shù)據(jù)轉(zhuǎn)換器)轉(zhuǎn)換成Java對象進行數(shù)據(jù)解析

關(guān)于網(wǎng)絡(luò)請求的基礎(chǔ)介紹,可以參考我的這篇博客:https://blog.csdn.net/m0_3770...

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

關(guān)于其他內(nèi)容介紹 01.關(guān)于博客匯總鏈接

1.技術(shù)博客匯總

2.開源項目匯總

3.生活博客匯總

4.喜馬拉雅音頻匯總

5.其他匯總

02.關(guān)于我的博客

我的個人站點:www.yczbj.org,www.ycbjie.cn

github:https://github.com/yangchong211

知乎:https://www.zhihu.com/people/...

簡書:http://www.jianshu.com/u/b7b2...

csdn:http://my.csdn.net/m0_37700275

喜馬拉雅聽書:http://www.ximalaya.com/zhubo...

開源中國:https://my.oschina.net/zbj161...

泡在網(wǎng)上的日子:http://www.jcodecraeer.com/me...

郵箱:[email protected]

阿里云博客:https://yq.aliyun.com/users/a... 239.headeruserinfo.3.dT4bcV

segmentfault頭條:https://segmentfault.com/u/xi...

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/71911.html

相關(guān)文章

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<