版權宣告:本文為博主原創文章,未經博主允許不得轉載
Github:github.com/Darkwh
若有錯誤或疑問歡迎小夥伴們留言評論
友情提示!!!
本人英文渣,文章中哪些單詞翻譯的不夠形象的話。。。。那你到是來打我呀O(∩_∩)O
系列回顧
文章目錄
前言
相信大多數Android開發者都聽過並使用過Retrofit,一款由square公司開源的網路請求庫。自己也用了一段時間,retrofit在okhttp的基礎上再一次簡化我們的網路請求操作,並且支援rxjava,用起來更加的得心應手。今天給大家分享一下自己閱讀retrofit原始碼的成果,記錄的同時希望也能將收穫分享給大家,個人水平有限,有錯誤的地方也請大神包涵和指正。
Retrofit的基本使用
Retrofit的使用方法網上資料很多,這裡不多說了,簡單貼一下使用方法,以後的分析原始碼會跟著呼叫順序去走。
建立介面檔案
interface TestService{
@GET("data/Android/10/1")
fun getMsg(): Call<MsgBean>
}
複製程式碼
建立接收網路請求的實體類
class MsgBean {
var error: Boolean = false
}
複製程式碼
建立Retrofit物件
val retrofit: Retrofit = Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.baseUrl("http://gank.io/api/")
.build()
複製程式碼
建立介面的代理物件
val service: TestService = retrofit.create(TestService::class.java)
複製程式碼
獲取Call物件
val call: Call<MsgBean> = service.getMsg()
複製程式碼
執行請求並監聽返回結果
call.enqueue(object : Callback<MsgBean> {
override fun onFailure(call: Call<MsgBean>?, t: Throwable?) {
Log.i("wh", "onFailure")
}
override fun onResponse(call: Call<MsgBean>?, response: Response<MsgBean>?) {
Log.i("wh", "onResponse")
}
})
複製程式碼
原始碼目錄結構及專案組成
原始碼目錄
retrofit的原始碼類比較少,非常適合新手閱讀(比如我),其中http包下面都是一些自定義的註解(@GET、@POST等),這裡就不一一列出了
比較重要的幾個類
介面:Call、CallAdapter、Converter、Callback
類:Retrofit、ServiceMethod、ParameterHandler、OkHttpCall
接下來讓我們瞭解一下這幾個類大致是做什麼的,首先我們從介面開始,因為介面屬於高度抽象,瞭解介面的定義有助於我們理解原始碼。
1.Call
/**
* An invocation of a Retrofit method that sends a request to a webserver and returns a response.
* Each call yields its own HTTP request and response pair. Use {@link #clone} to make multiple
* calls with the same parameters to the same webserver; this may be used to implement polling or
* to retry a failed call.
*
* <p>Calls may be executed synchronously with {@link #execute}, or asynchronously with {@link
* #enqueue}. In either case the call can be canceled at any time with {@link #cancel}. A call that
* is busy writing its request or reading its response may receive a {@link IOException}; this is
* working as designed.
*
* @param <T> Successful response body type.
*/
public interface Call<T> extends Cloneable {
......
}
複製程式碼
一個向伺服器傳送請求並獲得響應的呼叫器,主要的方法為
Response<T> execute() throws IOException;
void enqueue(Callback<T> callback);
複製程式碼
這兩個方法小夥伴們應該很熟悉了,execute是傳送同步請求,enqueue是傳送非同步請求
2.CallAdapter
/**
* Adapts a {@link Call} with response type {@code R} into the type of {@code T}. Instances are
* created by {@linkplain Factory a factory} which is
* {@linkplain Retrofit.Builder#addCallAdapterFactory(Factory) installed} into the {@link Retrofit}
* instance.
*/
public interface CallAdapter<R, T> {
......
}
複製程式碼
中文意思大致就是說CallAdapter接收一個Call物件(帶著返回型別引數R)並轉換為新的型別T。CallAdapter會被
Retrofit.Builder.addCallAdapterFactory(Factory)指定的Factory建立(注意builder可以指定多個Factory的,後面會說明這個問題)
上面的文字可能會讓你覺得比較亂,沒關係我們來看一下這個介面中的其中一個抽象方法:
T adapt(Call<R> call);
複製程式碼
這個方法就是CallAdapter最為核心的一個方法,如同註釋中的描述一樣,接收一個Call物件,Call物件帶著引數R,方法返回T型別。
再看CallAdapter介面中的另一個方法:
Type responseType();
複製程式碼
程式碼中的註解描述為:當把HTTP返回體轉換為JAVA類的時候返回引數型別,例如你得自定義介面中的方法返回值為Call<Foo>,那麼Type型別則為Foo。
總結來說CallAdapter將Call<R> 轉換為T的這麼一個轉換介面卡
CallAdapter介面中還有一個內部工廠類:
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);
}
}
複製程式碼
典型的工廠模式,通過get方法來獲取CallAdapter例項
3.Converter
/**
* Convert objects to and from their representation in HTTP. Instances are created by {@linkplain
* Factory a factory} which is {@linkplain Retrofit.Builder#addConverterFactory(Factory) installed}
* into the {@link Retrofit} instance.
*/
public interface Converter<F, T> {
......
}
複製程式碼
Converter介面的作用是將Java實體類和Http內容相互轉換
同CallAdapter一樣,Converter介面中也有一個內部工廠類:
abstract class Factory {
public @Nullable Converter<ResponseBody, ?> responseBodyConverter(Type type,
Annotation[] annotations, Retrofit retrofit) {
return null;
}
public @Nullable Converter<?, RequestBody> requestBodyConverter(Type type,
Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
return null;
}
public @Nullable Converter<?, String> stringConverter(Type type, Annotation[] annotations,
Retrofit retrofit) {
return null;
}
protected static Type getParameterUpperBound(int index, ParameterizedType type) {
return Utils.getParameterUpperBound(index, type);
}
protected static Class<?> getRawType(Type type) {
return Utils.getRawType(type);
}
}
複製程式碼
- responseBodyConverter方法將Http內容轉換為Java實體
- requestBodyConverter方法將Java實體轉換為Http內容(RequestBody)
4.Callback
public interface Callback<T> {
void onResponse(Call<T> call, Response<T> response);
void onFailure(Call<T> call, Throwable t);
}
複製程式碼
網路請求成功/失敗的回撥介面,不多說了吧就。
以上為Retrofit原始碼中的四個介面及其簡單介紹。接下來讓我們再瞭解一下其他幾個比較重要的類
1.Retrofit
就這個名字,和專案名同款,意味這個類的地位是多麼重要,具體程式碼後面分析。
2.ServiceMethod
/**
* Adapts an invocation of an interface method into an HTTP call.
*/
final class ServiceMethod<R, T> {
......
}
複製程式碼
將一個介面方法轉換為HTTP請求的呼叫器
非常核心的一個類,它的作用同原始碼中的註釋一樣,會將你定義的介面中的方法轉換為Http請求。具體的程式碼我們同樣放到後面分析。
3.ParameterHandler
這個類原始碼沒什麼註釋,故名思議是處理引數的,這裡處理的是請求引數。
ParameterHandler的定義如下
abstract class ParameterHandler<T> {
abstract void apply(RequestBuilder builder, @Nullable T value) throws IOException;
final ParameterHandler<Iterable<T>> iterable() {
return new ParameterHandler<Iterable<T>>() {
@Override void apply(RequestBuilder builder, @Nullable Iterable<T> values)
throws IOException {
if (values == null) return; // Skip null values.
for (T value : values) {
ParameterHandler.this.apply(builder, value);
}
}
};
}
final ParameterHandler<Object> array() {
return new ParameterHandler<Object>() {
@Override void apply(RequestBuilder builder, @Nullable Object values) throws IOException {
if (values == null) return; // Skip null values.
for (int i = 0, size = Array.getLength(values); i < size; i++) {
//noinspection unchecked
ParameterHandler.this.apply(builder, (T) Array.get(values, i));
}
}
};
}
}
複製程式碼
ParameterHandler是一個抽象類,擁有一個抽象方法apply和兩個遍歷方法,兩個遍歷方法分別適用於Iterable
和陣列,遍歷集合(陣列)並執行apply方法。
ParameterHandler有許多的子類,我們來看其中的幾個:
static final class RelativeUrl extends ParameterHandler<Object> {
@Override void apply(RequestBuilder builder, @Nullable Object value) {
checkNotNull(value, "@Url parameter is null.");
builder.setRelativeUrl(value);
}
}
複製程式碼
static final class Header<T> extends ParameterHandler<T> {
private final String name;
private final Converter<T, String> valueConverter;
Header(String name, Converter<T, String> valueConverter) {
this.name = checkNotNull(name, "name == null");
this.valueConverter = valueConverter;
}
@Override void apply(RequestBuilder builder, @Nullable T value) throws IOException {
if (value == null) return; // Skip null values.
String headerValue = valueConverter.convert(value);
if (headerValue == null) return; // Skip converted but null values.
builder.addHeader(name, headerValue);
}
}
複製程式碼
static final class Part<T> extends ParameterHandler<T> {
private final Headers headers;
private final Converter<T, RequestBody> converter;
Part(Headers headers, Converter<T, RequestBody> converter) {
this.headers = headers;
this.converter = converter;
}
@Override void apply(RequestBuilder builder, @Nullable T value) {
if (value == null) return; // Skip null values.
RequestBody body;
try {
body = converter.convert(value);
} catch (IOException e) {
throw new RuntimeException("Unable to convert " + value + " to RequestBody", e);
}
builder.addPart(headers, body);
}
}
複製程式碼
不難看出,ParameterHandler作用就是為RequestBuilder構建引數,RequestBuilder是retrofit原始碼中的一個累,其build方法會返回一個Request物件,Request物件是OkHttp中的類,沒看過,就不分(裝)析(逼)了。
不知道小夥伴們有沒有注意到Part(Headers headers, Converter<T, RequestBody> converter)這行程式碼,這就是前面說的Converter介面其中一個作用,將Java物件轉換為Http內容。
3.OkHttpCall
這是Retrofit中對Call介面內建的預設實現,這個類其實並沒有Retrofit和ServiceMethod重要,考慮是預設實現,所以也會著重分析一下,來讓我們SOLO下程式碼:
嗯。。。程式碼有點多,就不貼了,還是來讓我們看看其中比較重要的幾個變數和方法吧:
private final ServiceMethod<T, ?> serviceMethod;
private @Nullable
okhttp3.Call rawCall;
複製程式碼
OkHttpCall物件持有一個ServiceMethod引用和一個okhttp3.Call引用
再看OkHttpCall的其他方法,這裡我們主要看一下enqueue非同步請求方法,其它程式碼小夥伴們可自行研究:
@Override
public void enqueue(final Callback<T> callback) {
//檢查傳入的Callback是否為空,為空丟擲異常
checkNotNull(callback, "callback == null");
okhttp3.Call call;
Throwable failure;
synchronized (this) {
//此OkHttpCall物件如果執行過,再次呼叫會丟擲異常
//一般不會拋這個異常,因為每次都是建立一個新的OkHttpCall物件
//所以原始碼中並沒有executed=false這種設定,只有executed = true
if (executed) throw new IllegalStateException("Already executed.");
executed = true;
call = rawCall;
failure = creationFailure;
if (call == null && failure == null) {
try {
//此處建立原始的Call物件
call = rawCall = createRawCall();
} catch (Throwable t) {
//如果為指定的三種異常,則丟擲異常(為RxJava支援)
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)
throws IOException {
Response<T> response;
try {
//解析響應引數
response = parseResponse(rawResponse);
} catch (Throwable e) {
//如有解析異常則回撥給callback
callFailure(e);
return;
}
callSuccess(response);
}
@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 void callSuccess(Response<T> response) {
try {
callback.onResponse(OkHttpCall.this, response);
} catch (Throwable t) {
t.printStackTrace();
}
}
});
}
複製程式碼
enqueue方法總結來說是首先建立一個原始的Call物件(注意這裡為okhttp3的Call物件),然後呼叫Call.enqueue方法,並對返回結果進行解析,請求出錯或者解析出錯會回撥到你設定的Callback物件中的onFailure方法,解析成功則會回撥callSuccess方法
來看一下建立原始Call物件的方法定義:
private okhttp3.Call createRawCall() throws IOException {
Request request = serviceMethod.toRequest(args);
okhttp3.Call call = serviceMethod.callFactory.newCall(request);
if (call == null) {
throw new NullPointerException("Call.Factory returned null.");
}
return call;
}
複製程式碼
通過ServiceMethod的toRequest方法構造出一個Request隊形,然後通過指定的callFactory來建立call物件並返回。這裡預設的callFactory前面講解Retrofit.Builder的build方法中有介紹,未指定的時候預設使用OkHttpClient。
看一下toRequest方法的定義:
/**
* Builds an HTTP request from method arguments.
*/
Request toRequest(@Nullable Object... args) throws IOException {
RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl, headers,
contentType, hasBody, isFormEncoded, isMultipart);
@SuppressWarnings("unchecked") // It is an error to invoke a method with the wrong arg types.
ParameterHandler<Object>[] handlers = (ParameterHandler<Object>[]) parameterHandlers;
int argumentCount = args != null ? args.length : 0;
if (argumentCount != handlers.length) {
throw new IllegalArgumentException("Argument count (" + argumentCount
+ ") doesn`t match expected count (" + handlers.length + ")");
}
for (int p = 0; p < argumentCount; p++) {
handlers[p].apply(requestBuilder, args[p]);
}
return requestBuilder.build();
}
複製程式碼
這個方法的作用就是通過遍歷上面介紹過的ParameterHandler陣列並呼叫apply函式將方法中的引數轉換成Http請求引數。
再來看一下解析返回結果的方法定義:
Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
ResponseBody rawBody = rawResponse.body();
......
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;
}
}
複製程式碼
其中會呼叫ServiceMethod.toResponse,稍微跟一下:
R toResponse(ResponseBody body) throws IOException {
return responseConverter.convert(body);
}
複製程式碼
可以看到此處使用你指定的Convert來對響應資料進行轉換(比如我們最常用的json轉實體類)
總結
以上為大家介紹了一下retrofit的基本使用和幾個重要的類,下一篇我將按照retrofit使用的順序來分析一下原始碼,將我說理解的內容分先給大家,如有錯誤的地方還請包涵和指正!勿噴!!!