前言:
哈哈,其實寫的還是很水,各位原諒我O(∩_∩)O。
介於自己的網路方面知識爛的一塌糊塗,所以準備寫相關網路的文章,但是考慮全部寫在一篇太長了,所以分開寫,希望大家能仔細看,最好可以指出我的錯誤,讓我也能糾正。
1.講解相關的整個網路體系結構:
2.講解相關網路的重要知識點,比如很多人都聽過相關網路方面的名詞,但是僅限於聽過而已,什麼tcp ,udp ,socket ,websocket, http ,https ,然後webservice是啥,跟websocket很像,socket和websocket啥關係長的也很像,session,token,cookie又是啥。
Android技能樹 — 網路小結(3)之HTTP/HTTPS
Android技能樹 — 網路小結(4)之socket/websocket/webservice
相關網路知識點小結- cookie/session/token(待寫)
3.相關的第三方框架的原始碼解析,畢竟現在面試個大點的公司,okhttp和retrofit原始碼是必問的。
Android技能樹 — 網路小結(6)之 OkHttp超超超超超超超詳細解析
Android技能樹 — 網路小結(7)之 Retrofit原始碼詳細解析
正文
因為我平時使用的都是Rxjava2 + Retrofit ,所以我相關的原始碼解析都是配合RxJava來的,而不是Call返回物件。
讀本文的我推薦大家最好對OKHttp原始碼有所瞭解,再來看本文,因為Retrofit內部還是通過OkHttp發出網路請求。大家也可以看我前面寫的:Android技能樹 — 網路小結之 OkHttp超超超超超超超詳細解析, 同時本文不會再去教大家Retrofit的基礎使用,如果要看一些簡單使用,可以看下面的一些推薦部落格:
Android Retrofit 2.0 的詳細 使用攻略(含例項講解)
Android:Retrofit 結合 RxJava的優雅使用(含例項教程)
我們先上一張別的大佬部落格中的一張圖:
這個圖畫的很好,但是這個圖更多的是從大局觀來看,所以如果對於原始碼不是有一些基礎瞭解的話,看這個圖很容易就忘記。
看過我的Okhttp原始碼分析的文章:Android技能樹 — 網路小結之 OkHttp超超超超超超超詳細解析,我們文中的Okhttp流程圖就是跟著原始碼一步步來畫的。我更喜歡是跟著原始碼一步步來畫流程圖(PS:其實是我水平太差了,無法一下子總結處第三方庫的各種設計模式的使用),所以Retrofit我也畫了下面這個圖:
而等會我們分析完這個跟著原始碼分析的流程圖後,再回頭看上面的別人部落格中的總結的Retrofit結構圖,就會很簡單了。
首先我們來確定總體大綱:
我們知道我們的目標是要發起一次網路請求,他有這麼幾步:
- 告訴它一些基本資訊,比如url地址,網路請求方式(get、post、...等),請求引數值。然後拼裝成一個標準的網路Request請求的格式發出去。所以這裡有二步動作:1.先解析我們寫的引數,2.再解析完後拼裝成標準的網路Request請求格式
- 發出請求後,接收到了後臺的Response返回結果,我們要把Resonse轉換成我們想要的返回結果。但是我們寫的想要的返回結果又有二大關鍵地方,我們平常的返回結果可能是
X <Y>
,我們先來看外面的X的型別
,比如我們常見的返回結果是Call<Y> 和 Observable<Y>
,所以我們在轉換的時候一是要考慮最外面的那個返回型別的轉換。另外一個是Y的型別
,也就是裡面我們具體寫的Bean物件,比如我們直接返回字串,那可能就是Observable<String>
,又或者是自己定義的xxxBean物件,那就是Observable<xxxBean>
。所以我們要有二類轉換:1.外層的結果型別,比如Call或者Observable等,2.是泛型裡面填的具體的Bean物件型別
所以我們總結起來就需要四步:
- 解析並拿到我們寫的一些引數(url,請求方式(post/get),請求引數......)
- 根據我們寫的引數,拼成一個網路請求Request,去幫我們發起請求。
- Response如何轉換成Call或者Observable等返回型別,和第4步中的Bean物件拼成了Call《Bean》或者Observable《Bean》
- Response如何轉換成我們所需要的具體的Bean物件。
沒錯,下次別人問你,你就心裡有數了,到底Retrofit做了什麼內容,你就跟別人說很簡單啦,大致做了上面四步,逼格一下子提高了。。
1. 建立Retrofit物件
我這裡直接先把建立Retrofit的物件的程式碼寫上:
Retrofit retrofit = new Retrofit.Builder()
.client(new OkHttpClient())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.baseUrl("https://xxxx.com/")
.build();
複製程式碼
不要問建立Retrofit的各自的方法是幹嘛的,我們後面會一步步講解。
2. 如何解析並拿到我們寫的引數
我們知道我們平常是這樣寫的:
我們隨便寫一個常見的獲取某個使用者的個人資訊介面來說明:
InfoApi.java:
interface InfoApi{
@GET("userinfo.do")
Observable<UserBean> getInfo(@Query("name") String nameStr);
}
複製程式碼
那我們要拿到:
- path值:上面建立Retrofit時候傳入的baseUrl +
userinfo.do
="https://xxxx.com/userinfo.do"
- 網路請求的方式:
GET請求
- 傳送的引數query :
name=nameStr
最終我們發現是GET請求,所以這麼拼在一起:path + "?" + query = http://xxxx/userinfo.do?name=nameStr
所以我們來看如何一步步拿到相關引數:
我們知道上面寫的InfoApi.java
是要被retrofit載入進去的:
retrofit.create(InfoApi.class);
複製程式碼
所以我們要來看create
方法的具體操作前,我們先來了解一下基礎知識,那就是代理模式,如果知道代理模式的,直接可以忽略此處,直接往下看。
2.1 create方法:
在看create程式碼之間,我們要先學會代理模式相關知識
本來也想一步步長篇大論的寫下,但是後來看到一篇不錯的文章,寫的挺仔細的:java動態代理實現與原理詳細分析 ,希望大家能仔細看完,在看下面的內容。
我們點進去檢視具體的程式碼:
public <T> T create(final Class<T> service) {
Utils.validateServiceInterface(service);
if (this.validateEagerly) {
this.eagerlyValidateMethods(service);
}
//'使用了代理模式'
return Proxy.newProxyInstance(service.getClassLoader(), new Class[]{service}, new InvocationHandler() {
private final Platform platform = Platform.get();
public Object invoke(Object proxy, Method method, @Nullable Object[] args) throws Throwable {
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
} else if (this.platform.isDefaultMethod(method)) {
return this.platform.invokeDefaultMethod(method, service, proxy, args);
} else {
//'我們可以看到我們寫的介面裡面的的method傳入到了loadServiceMethod方法裡面,從而得到了我們定義在method上面的相關引數資訊。'
ServiceMethod<Object, Object> serviceMethod = Retrofit.this.loadServiceMethod(method);
//'我們傳入的方法的引數args和上面獲得的ServiceMethod,一起傳入OkHttpCall建構函式中,得到OkHttpCall物件'
OkHttpCall<Object> okHttpCall = new OkHttpCall(serviceMethod, args);
return serviceMethod.adapt(okHttpCall);
}
}
});
}
複製程式碼
我們可以看到我們呼叫的getInfo
這個method方法傳入了:
ServiceMethod<Object, Object> serviceMethod = Retrofit.this.loadServiceMethod(method);
複製程式碼
我們進去檢視:
ServiceMethod<?, ?> loadServiceMethod(Method method) {
//'從快取中去讀'
ServiceMethod<?, ?> result = (ServiceMethod)this.serviceMethodCache.get(method);
if (result != null) {
return result;
} else {
Map var3 = this.serviceMethodCache;
synchronized(this.serviceMethodCache) {
result = (ServiceMethod)this.serviceMethodCache.get(method);
if (result == null) {
//'如果快取中沒有,則新建'
result = (new retrofit2.ServiceMethod.Builder(this, method)).build();
//'新建完後再放入快取中'
this.serviceMethodCache.put(method, result);
}
return result;
}
}
}
複製程式碼
我們可以看到新建的方法:
(new retrofit2.ServiceMethod.Builder(this, method)).build();
複製程式碼
我們來看ServiceMethod類下的Builder的建構函式:
Builder(Retrofit retrofit, Method method) {
this.retrofit = retrofit;
this.method = method;
//'Java特有的方法,可以獲取Java方法上面的註解標識,比如:@POST,@GET'
this.methodAnnotations = method.getAnnotations();
//'獲取方法引數裡面定義的引數型別,比如:String,boolean'
this.parameterTypes = method.getGenericParameterTypes();
//'獲取方法裡面的註解標識,比如:@Query,@Path'
this.parameterAnnotationsArray = method.getParameterAnnotations();
}
複製程式碼
是不是一下子就知道了,原來是通過這樣的方式拿到了我們寫在方法上面的一些引數值,如果還不清楚Method的這幾個方法,可以看下面的相關連結:
Java獲取類、方法、屬性上的註解
java.lang.reflect.Method.getGenericParameterTypes()方法示例.
使用反射獲得引數列表裡的註解getParameterAnnotations.
我們建立ServiceMethod因為是使用的Builder模式,所以最終要呼叫build()方法來建立例項:
public ServiceMethod build() {
//'建立了CallAdapter物件,具體幹嘛用的,具體後面會講解'
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物件,具體後面會講解'
responseConverter = createResponseConverter();
//'對於我們寫的介面請求方法的方法上面的註解進行相關判斷,'
for (Annotation annotation : methodAnnotations) {
parseMethodAnnotation(annotation);
}
//'因為進行方法上面註解的解析了,所以httpMethod的也就相應的被賦值了,
如果為空,就說明你寫的請求介面方法沒有寫@GET等,就會丟擲異常'
if (httpMethod == null) {
throw methodError(“HTTP method annotation is required (e.g., @GET, @POST, etc.).”);
}
//'因為上面解析了,所以比如我們發現是@GET請求,這時候hasBody會是false,如果你還用了Multipart註解,就會報錯了,他要求是要有request body的,@GET請求是不能使用Multipart的'
if (!hasBody) {
if (isMultipart) {
throw methodError(
“Multipart can only be specified on HTTP methods with request body (e.g., @POST).”);
}
//'同上,表單提交是一定要求有request body的'
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];
//'遍歷我們獲取的方法裡面的註解集合,比如@Query,@Path等'
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.”);
}
//'然後對我們寫的方法內部引數註解進行判斷,看寫的是否正確等
這裡的判斷很長,比如如果你用的是註解@Body,那麼先判斷你是否用了方法上面的@FormEncode註解或者@Multipart註解,
不然就報錯,然後因為我們填的引數是物件了,所以內部需要通過RequestBodyConverter來進行轉換,把我們傳的物件,變成了RequestBody物件。
具體很多很多判斷,各種註解的判斷我都不一一講了,大家只要進去看方法詳細程式碼就可以了。'
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);
}
複製程式碼
好,我們已經成功拿到了我們的方法中的紅色框出來的部分,綠色的部分我們還沒有獲取。
而代理模式的invoke方法裡面的引數 @Nullable Object[] args,就是我們具體傳入的引數,比如我這麼寫:
getInfo("青蛙要fly");
複製程式碼
args裡面就有了我們傳入的"青蛙要fly"
字串。這樣我們是不是就獲取了上面的其中一個綠色框nameStr的內容了。
我們拿到包含了這些紅色框引數的ServiceMethod物件後,加上我們傳入的綠色的框的nameStr的具體的值
,我們已經可以進行網路Request請求的所必要的引數了 (另外一個綠色的框只是用來最後網路請求成功後拿到的Response進行轉換,所以這時候不知道都不影響Request請求)
我們可以看到我們獲得到的資訊,又用來生成了OkHttpCall物件,然後呼叫了serviceMethod.adapt(okHttpCall);
方法。
那我們可以看到create接下去已經沒有其他程式碼了,所以serviceMethod.adapt(okHttpCall);
肯定會幫我們用剛才拿到的已知引數,幫我們拼成Request,完成一次網路請求。
3.根據我們寫的引數,拼成Request請求
我們上面已經說到了進入了serviceMethod.adapt(okHttpCall);
方法了,我們點進去檢視:
T adapt(Call<R> call) {
return callAdapter.adapt(call);
}
複製程式碼
我們可以看到是呼叫了callAdapter類的adapt方法。那這個callAdapter物件又是什麼呢?
還記不記得我們第一大步:建立Retrofit物件時候的程式碼:
Retrofit retrofit = new Retrofit.Builder()
.client(new OkHttpClient())
//'這裡傳入了CallAdapterFactory,而Factory類是用來建立具體的CallAdapter物件的工廠類'
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.baseUrl("https://xxxx.com/")
.build();
複製程式碼
所以本文中我們使用的CallAdapter是RxJava2CallAdapterFactory建立的,我們先來看ServiceMethod裡面建立CallAdapter的方法:
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);
}
//'如果方法的返回結果是void,則丟擲異常'
if (returnType == void.class) {
throw methodError(“Service methods cannot return void.”);
}
//'我們前面提過的,獲取方法上的註解,比如@GET等'
Annotation[] annotations = method.getAnnotations();
try {
//'拿著我們的介面請求方法的返回物件及方法上的註解資訊,'
//'去通過Retrofit類的callAdapter類去生成一個CallAdapter物件'
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方法,我們不看都知道,通過我們前面建立Retrofit物件時候傳入的addCallAdapterFactory的工廠類來建立具體的CallAdapter,當然我們具體還是要具體程式碼一步步來看過程。
我們在呼叫addCallAdapterFactory加入我們的RxJava2CallAdapterFactory.create()
,所以先來看下addCallAdapterFactory方法做了什麼:
public Builder addCallAdapterFactory(CallAdapter.Factory factory) {
callAdapterFactories.add(checkNotNull(factory, "factory == null"));
return this;
}
複製程式碼
我們可以簡單的看到,就是把我們的Factory工廠類物件加入到private final List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>();
這個List佇列中而已。
那這個佇列到底都加了哪些工廠類的,如果我在建立Retrofit物件時候不呼叫addCallAdapterFactory
方法,難道這個佇列就是空的????那又怎麼去生成CallAdapter物件?
首先肯定要加入我們自己傳入的Factory,有可能一個,也可能傳入多個:
Retrofit retrofit = new Retrofit.Builder()
........
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addCallAdapterFactory(xxxxxxCallAdapterFactory.create())
.addCallAdapterFactory(yyyyyyCallAdapterFactory.create())
........
........
.build();
複製程式碼
但是為了防止我們建立Retrofit物件時候不呼叫addCallAdapterFactory傳入自己的Factory,所以本身這個佇列還會加入預設的Factory:
//'看名字就知道,加入平臺的預設的CallAdapterFactory(有java8 和 Android Platform,我們這裡肯定是Android)'
callAdapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));
複製程式碼
當然這個ExecutorCallAdapterFactory肯定是繼承了CallAdapter.Factory:
我們已經知道了我們的CallAdapterFactory佇列裡面包含了哪些工廠類了。接下來我們再來具體的Retrofit的callAdapter的方法:
public CallAdapter<?, ?> callAdapter(Type returnType, Annotation[] annotations) {
//'因為可能有多個CallAdapterFactory工廠類,所以要每個工廠類都去試一下,有一個成功就直接返回了'
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;
//'迴圈遍歷所有的CallAdapterFactory,然後哪個能成功生成CallAdapter,就直接返回'
for (int i = start, count = callAdapterFactories.size(); i < count; i++) {
CallAdapter<?, ?> adapter = callAdapterFactories.get(i).get(returnType, annotations, this);
if (adapter != null) {
return adapter;
}
}
//'如果所有的CallAdapterFctory都不能使用,就拼接字串,丟擲異常'
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());
}
複製程式碼
有可能有人會問,為什麼CallAdapterFactory有可能生成CallAdapter不成功??還要一個個去遍歷?
因為我們同時傳入了我們需要返回的物件的型別傳入到了CallAdapterFactory中,你說如果你是預設的ExecutorCallAdapterFactory
工廠類,你卻傳入了Rxjava的返回相關引數,比如我們例子中的Observable<UserBean>
,它的程式碼裡面都不認識這種返回型別,怎麼幫你去生成物件,而且程式碼也是加了判斷,如果返回型別不是Call型別,直接就退出了。
@Override
public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
//'如果不是Call.class,直接退出生成CallAdapter物件'
if (getRawType(returnType) != Call.class) {
return null;
}
.......
.......
}
複製程式碼
所以我們來看下RxJava2CallAdapterFactory裡面怎麼生成相應的CallAdapter的:
@Override
public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
Class<?> rawType = getRawType(returnType);
//'如果我們的返回型別是Completable,就直接返回RxJava2CallAdapter物件,裡面的responseType是void'
if (rawType == Completable.class) {
// Completable is not parameterized (which is what the rest of this method deals with) so it
// can only be created with a single configuration.
return new RxJava2CallAdapter(Void.class, scheduler, isAsync, false, true, false, false,
false, true);
}
//'判斷是否是Flowable或者Single或者Maybe'
boolean isFlowable = rawType == Flowable.class;
boolean isSingle = rawType == Single.class;
boolean isMaybe = rawType == Maybe.class;
//'如果既不是上面三種又不是Observable型別,直接返回null'
if (rawType != Observable.class && !isFlowable && !isSingle && !isMaybe) {
return null;
}
boolean isResult = false;
boolean isBody = false;
Type responseType;
//'如果不是泛型類的,比如Observable<XXXX> ,則拋異常'
if (!(returnType instanceof ParameterizedType)) {
String name = isFlowable ? "Flowable"
: isSingle ? "Single"
: isMaybe ? "Maybe" : "Observable";
throw new IllegalStateException(name + " return type must be parameterized"
+ " as " + name + "<Foo> or " + name + "<? extends Foo>");
}
//'獲取泛型中的具體引數,比如Observable<xxxBean>中的xxxBean的type'
Type observableType = getParameterUpperBound(0, (ParameterizedType) returnType);
//'獲取xxxBean具體的Class物件'
Class<?> rawObservableType = getRawType(observableType);
//'判斷我們上面獲取的泛型內容(xxxBean)是不是Response'
if (rawObservableType == Response.class) {
if (!(observableType instanceof ParameterizedType)) {
throw new IllegalStateException("Response must be parameterized"
+ " as Response<Foo> or Response<? extends Foo>");
}
responseType = getParameterUpperBound(0, (ParameterizedType) observableType);
//'判斷我們上面獲取的泛型內容(xxxBean)是不是Result'
} else if (rawObservableType == Result.class) {
if (!(observableType instanceof ParameterizedType)) {
throw new IllegalStateException("Result must be parameterized"
+ " as Result<Foo> or Result<? extends Foo>");
}
responseType = getParameterUpperBound(0, (ParameterizedType) observableType);
isResult = true;
} else {
//'我們平常開發泛型裡面填的肯定是自己的Bean物件
//所以最後走的是這裡的程式碼'
responseType = observableType;
//'同時isBody設定為true'
isBody = true;
}
//'生成具體的Rxjava2CallAdapter物件'
return new RxJava2CallAdapter(responseType, scheduler, isAsync, isResult, isBody, isFlowable,
isSingle, isMaybe, false);
}
複製程式碼
既然我們CallAdapter物件也建立完了,我們回到最剛開始的地方,還記得我們前面分析的程式碼是到了callAdapter.adapt(okHttpCall)
(如果忘記的同學,可以重新回頭看下)。
所以我們現在已經建立的Rxjava2CallAdapter物件了,我們來看下它的adapt方法:
@Override public Object adapt(Call<R> call) {
/**
'很多人會說這個isAsync,是否非同步是哪裡設定的,
其實就是再我們傳入Factory物件時候建立的。
Retrofit retrofit = new Retrofit.Builder()
.client(new OkHttpClient())
我們看見建立Factory物件,可以是createAsync()或者create()方法二種來建立,從而決定是同步還是非同步操作
.addCallAdapterFactory(RxJava2CallAdapterFactory.createAsync())
或者是
.addCallAdapterFactory(RxJava2CallAdapterFactory.create()
.build();'
*/
//'我們可以看到上面根據是否非同步,建立不同的Observable物件,我們用複雜點的來講解吧,
就當我們建立的時候使用的是RxJava2CallAdapterFactory.createAsync()方法,所以拿到的物件是CallEnqueueObservable'
Observable<Response<R>> responseObservable = isAsync
? new CallEnqueueObservable<>(call)
: new CallExecuteObservable<>(call);
//'因為我們Observable<xxxBean>裡面包含的是自己Bean,所以建立的時候isBody = true;'
Observable<?> observable;
if (isResult) {
observable = new ResultObservable<>(responseObservable);
} else if (isBody) {
//'所以我們的Observable為BodyObservable'
observable = new BodyObservable<>(responseObservable);
} else {
observable = responseObservable;
}
if (scheduler != null) {
observable = observable.subscribeOn(scheduler);
}
if (isFlowable) {
return observable.toFlowable(BackpressureStrategy.LATEST);
}
if (isSingle) {
return observable.singleOrError();
}
if (isMaybe) {
return observable.singleElement();
}
if (isCompletable) {
return observable.ignoreElements();
}
//'所以最終返回了BodyObservable<CallEnqueueObservable>'
return observable;
}
複製程式碼
BodyObservable看名字就知道是一個自定義Observable:
final class BodyObservable<T> extends Observable<T> {
private final Observable<Response<T>> upstream;
BodyObservable(Observable<Response<T>> upstream) {
this.upstream = upstream;
}
@Override protected void subscribeActual(Observer<? super T> observer) {
//'當有Observer註冊我們的Observable的時候,
其實是我們前面的傳入的CallEnqueueObservable去註冊了
一個BodyObserver<我們自己寫的Observer>'
upstream.subscribe(new BodyObserver<T>(observer));
}
}
複製程式碼
所以核心還是我們傳入的CallEnqueueObservable
這個Observable,所以最後還是要看這個類的原始碼:
final class CallEnqueueObservable<T> extends Observable<Response<T>> {
private final Call<T> originalCall;
CallEnqueueObservable(Call<T> originalCall) {
this.originalCall = originalCall;
}
@Override protected void subscribeActual(Observer<? super Response<T>> observer) {
// Since Call is a one-shot type, clone it for each new observer.
Call<T> call = originalCall.clone();
//'我們可以看到簡歷了一個CallCallback物件,傳入了使用者寫的我們前面建立的OkHttpCall物件和使用者寫的observer物件'
CallCallback<T> callback = new CallCallback<>(call, observer);
observer.onSubscribe(callback);
//'然後呼叫了call的enqueue方法,
因為是OkHttpCall物件,所以我們直接看OkHttpCall物件的enqueue方法即可'
call.enqueue(callback);
}
private static final class CallCallback<T> implements Disposable, Callback<T> {
.......
.......
.......
}
}
複製程式碼
OkHttpCall的enqueue方法:
@Override public void enqueue(final Callback<T> callback) {
checkNotNull(callback, "callback == null");
okhttp3.Call call;
Throwable failure;
synchronized (this) {
if (executed) throw new IllegalStateException("Already executed.");
executed = true;
call = rawCall;
failure = creationFailure;
if (call == null && failure == null) {
try {
//'建立Okhttp3的Call物件(畢竟最後發起網路請求是Okhttp,也要使用它的Call物件)'
call = rawCall = createRawCall();
} catch (Throwable t) {
throwIfFatal(t);
failure = creationFailure = t;
}
}
}
if (failure != null) {
callback.onFailure(this, failure);
return;
}
if (canceled) {
call.cancel();
}
//'常規Okhttp的操作,call.enqueue方法發起非同步請求,估計大家都看得懂,我就不多介紹了,我們直接看拿到返回的資料處理'
call.enqueue(new okhttp3.Callback() {
@Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
Response<T> response;
try {
//'我們這裡成功的拿到了Okhttp3.Response物件,
所以使用parseResponse方法將rawResponse物件轉換成Retrofit的Response物件'
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();
}
}
});
}
複製程式碼
到這裡,我們已經成功的傳送了網路請求,並且拿到了主句
4. 如何將Resonse轉換成最終我們想要的結果物件
我們上面可以看到我們是講OkHttp3.Response物件轉換成了Retrofit.Response物件,我們具體來看下:
Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
//'把Okhttp3.Response中的body部分取出來'
ResponseBody rawBody = rawResponse.body();
rawResponse = rawResponse.newBuilder()
.body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength()))
.build();
int code = rawResponse.code();
//'我們就當成功請求回來的,所以code是200'
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 {
//'核心程式碼:
把body部分,通過toResponse方法,變成我們寫入的泛型(也就是Observable<xxxBean>這個xxxBean物件'
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;
}
}
複製程式碼
所以最終我們發現又回到了serviceMethod裡面了,相關的方法呼叫都在這裡面:
R toResponse(ResponseBody body) throws IOException {
//'可以看到我們通過responseConverter轉換器來對body部分進行了轉換'
return responseConverter.convert(body);
}
複製程式碼
這個responseConverter又是怎麼來的呢?我們再回到建立Retrofit物件的地方:
Retrofit retrofit = new Retrofit.Builder()
.client(new OkHttpClient())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
//'看見了ConverterFactory沒有,就是這裡傳入了我們的轉換器物件'
.addConverterFactory(GsonConverterFactory.create())
.baseUrl("https://xxxx.com/")
.build();
複製程式碼
我們來看下具體的程式碼:
public final class GsonConverterFactory extends Converter.Factory {
//'可以看到預設內部使用的是GSON來進行轉換'
public static GsonConverterFactory create() {
return create(new Gson());
}
public static GsonConverterFactory create(Gson gson) {
return new GsonConverterFactory(gson);
}
private final Gson gson;
private GsonConverterFactory(Gson gson) {
if (gson == null) throw new NullPointerException("gson == null");
this.gson = gson;
}
//'這個方法從名字就可以看出來,是用用來給ResponseBody轉換成我們要的物件'
@Override
public Converter<ResponseBody, ?> responseBodyConverter(final Type type, Annotation[] annotations, Retrofit retrofit) {
Type newType = new ParameterizedType() {
@Override
public Type[] getActualTypeArguments() {
return new Type[] { type };
}
@Override
public Type getOwnerType() {
return null;
}
@Override
public Type getRawType() {
return HttpResult.class;
}
};
TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(newType));
//'可以看到,用在Response的轉換器是叫GsonResponseBodyConverter物件'
return new GsonResponseBodyConverter<>(adapter);
}
//'這個名字也可以看出來是把我們傳入的物件轉換成RequestBody,從而發起請求'
@Override
public Converter<?, RequestBody> requestBodyConverter(Type type, Annotation[] parameterAnnotations,
Annotation[] methodAnnotations, Retrofit retrofit) {
TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
//'可以看到,用在Request的轉換器是叫GsonRequestBodyConverter物件'
return new GsonRequestBodyConverter<>(gson, adapter);
}
}
複製程式碼
我們具體來看看GsonResponseBodyConverter
:
final class GsonResponseBodyConverter<T> implements Converter<ResponseBody, T> {
private final Gson gson;
private final TypeAdapter<T> adapter;
GsonResponseBodyConverter(Gson gson, TypeAdapter<T> adapter) {
this.gson = gson;
this.adapter = adapter;
}
@Override public T convert(ResponseBody value) throws IOException {
//'根據傳入的ResponseBody得到JsonReader'
JsonReader jsonReader = gson.newJsonReader(value.charStream());
try {
//'很簡單,就是GSON進行相關的JSON解析'
T result = adapter.read(jsonReader);
if (jsonReader.peek() != JsonToken.END_DOCUMENT) {
throw new JsonIOException("JSON document was not fully consumed.");
}
return result;
} finally {
value.close();
}
}
}
複製程式碼
這裡我們看到了既然解析部分是在這裡,是不是我們可以做很多定製化操作,答案當然是Yes,比如我寫了個自定義的GsonResponseBodyConverter來進行替換(下面的類就隨便寫寫,大家可以根據自己的需求寫自己的自定義轉換器):
final class GsonResponseBodyConverter<T> implements Converter<ResponseBody, Object> {
private final TypeAdapter<T> adapter;
GsonResponseBodyConverter(TypeAdapter<T> adapter) {
this.adapter = adapter;
}
@Override
public Object convert(ResponseBody value) throws IOException {
try {
//'因為我統一的外層物件都是使用的HttpResult,我的程式碼是這麼寫的Observable<HttpResult<xxxBean>>'
HttpResult apiModel = (HttpResult) adapter.fromJson(value.charStream());
//'直接在這裡就對統一處理操作'
if (apiModel.getCode().equals(CompanyHttpCode.TOKEN_NOT_EXIST)) {
throw new TokenNotExistException();
} else if (apiModel.getCode().equals(CompanyHttpCode.TOKEN_INVALID)) {
throw new TokenInvalidException();
} else if (!apiModel.getCode().equals(CompanyHttpCode.SUCCESS_CODE)) {
// 特定 API 的錯誤,在相應的 Subscriber 的 onError 的方法中進行處理
throw new ApiException();
} else if (apiModel.getCode().equals(CompanyHttpCode.SUCCESS_CODE) || apiModel.getCode().equals(CompanyHttpCode.SUCCESS_CODE_STR)) {
return apiModel;
}
} finally {
value.close();
}
return null;
}
}
複製程式碼
好了,我們已經拿到了相應的Observable<xxxBean>
裡面的xxxBean物件了,我們可以看到:
try {
//'我們前面講過,通過這個方法轉換的parseResponse(rawResponse);
把OkHttp3.Response轉換成了Retrofit.Response<我們的bean> '
} catch (Throwable e) {
callFailure(e);
return;
}
try {
//'在轉換成功後,我們就把具體的response重新通過回撥函傳回去給CallEnqueueObservable'
callback.onResponse(OkHttpCall.this, response);
} catch (Throwable t) {
t.printStackTrace();
}
複製程式碼
CallEnqueueObservable的onResponse方法:
@Override public void onResponse(Call<T> call, Response<T> response) {
if (disposed) return;
try {
//'我們可以看到,Observable呼叫了observerde的onNext方法把Retrofit.Reponse物件傳送了出去'
observer.onNext(response);
......
......
......
}
複製程式碼
可能有些人就會奇怪了,我們平常使用,明明拿到的就是具體的裡面的xxxBean物件,而不是Response<xxxBean>
,那是因為上面我們提過的,我們的Observer被BodyObserver包了一層:
private static class BodyObserver<R> implements Observer<Response<R>> {
@Override public void onNext(Response<R> response) {
if (response.isSuccessful()) {
//'最終到我們的Observer的時候,就是Response裡面包含了的我們寫的xxxBean物件了。'
observer.onNext(response.body());
} else {
.....
.....
}
}
......
......
}
複製程式碼
結語:
所以現在我們再來看程式碼,是不是已經就能懂中間到底做了什麼操作。哈哈:
interface InfoApi{
@GET("userinfo.do")
Observable<UserBean> getInfo(@Query("name") String nameStr);
}
Retrofit retrofit = new Retrofit.Builder()
.client(new OkHttpClient())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.baseUrl("https://xxxx.com/")
.build();
retrofit.create(InfoApi.class)
.getInfo("青蛙要fly")
.subscribe(new ResourceObserver<UserBean>() {
@Override
public void onNext(UserBean userBean) {
}
@Override
public void onError(Throwable e) {
}
@Override
public void onComplete() {
}
});
複製程式碼
然後再回頭看這個圖片,是不是也看得懂了:
不知不覺就寫完了,哈哈,可能有些地方不詳細或者是寫的不好又或者是寫錯了。可以留言,我更希望的是能指出我哪裡寫錯了,哈哈,這樣我也可以糾正錯誤的知識。