摘要:看下圖所示,摘自網(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
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對象呢? CallgetSplashImage(@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") ObservablegetBook(@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 ObservablegetHotMovie(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; } publicT 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"); ListpathSegments = 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. ListcallAdapterFactories = 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, ListconverterFactories, 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") Call3.2 分析create(final Class> 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) { } });
源代碼如下所示,這段代碼很重要。
創(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方法來完成指定的功能
publicT 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
接著看一下validateServiceInterface方法操作了什么?
通過這個方法可知,如果service類不是接口則會拋異常。同時需要注意API接口不能擴展其他接口
staticvoid 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
可以看到這里都是參數(shù)賦值操作
ServiceMethod(Builder4.注解的解析 4.1 callAdapter的創(chuàng)建源碼分析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; }
首先獲取method的對象表示的方法的形式類型。然后獲取method的注解。重點看看retrofit.callAdapter(returnType, annotations)主要做了什么?
private CallAdaptercreateCallAdapter() { 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 OkHttpCallimplements 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(Callcall) { 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 Call6.OkHttpCall的網(wǎng)絡(luò)請求 6.1 OkHttpCall.execute()同步請求> userOld = apiService.isUserOld(); //返回的則是Observable Observable advert = mApiService.getSplashImage(method)
使用方法Response
實際開發(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 Response6.2 OkHttpCall.enqueue()異步請求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; }
關(guān)于異步操作封裝庫,可以看我的開源線程池封裝庫:https://github.com/yangchong2...
如何調(diào)用可以看前面的代碼介紹。這里就不介紹呢!
首先添加一個synchronized同步鎖。創(chuàng)建OkHttp的Request對象,然后發(fā)送網(wǎng)絡(luò)請求,然后解析返回數(shù)據(jù)。在這里,可能會想到,究竟是如何做到異步操作的呢?
@Override public void enqueue(final Callbackcallback) { 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 ExecutorCallbackCall6.3 parseResponse解析網(wǎng)絡(luò)數(shù)據(jù)源碼解析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); } }); } }); }
解析網(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關(guān)于其他內(nèi)容介紹 01.關(guān)于博客匯總鏈接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; } }
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...
阿里云博客: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
閱讀 2220·2020-06-12 14:26
閱讀 2513·2019-08-29 16:41
閱讀 1924·2019-08-29 15:28
閱讀 2473·2019-08-26 13:43
閱讀 775·2019-08-26 13:37
閱讀 2801·2019-08-23 18:13
閱讀 2827·2019-08-23 15:31
閱讀 1041·2019-08-23 14:10