前言
噢!親愛的朋友們,快來看看這優秀的Retrofit
,它實在太美妙了,如果你不看的話,我保證會用我的靴子狠狠地踢你的屁股!(狗頭保命)
正文
1. 什麼是Retrofit?
在 官網 中對它的描述:
A type-safe HTTP client for Android and Java
大概意思也就是針對 Java
和 Android
的 一種型別安全的HTTP
庫.
Retrofit
是Square開源的一款優秀的網路框架,它不會自己去請求網路,而是對OkHttp
進行了封裝。
僅僅會使用還不夠,學習原始碼有助於我們更好的成長。
2. Retrofit的使用
我們先來看看官網上的案例:
- 先定義你的網路介面
public interface GitHubService {
@GET("users/{user}/repos")
Call<List<Repo>> listRepos(@Path("user") String user);
}
複製程式碼
- 建立Retrofit物件
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.build();
複製程式碼
- 獲得網路請求API的例項
GitHubService service = retrofit.create(GitHubService.class);
複製程式碼
- 呼叫API方法
Call<List<Repo>> call = service.listRepos("octocat");
複製程式碼
- 執行網路請求
// 同步
try {
List<Repo> repos = call.execute().body();
} catch (IOException e) {
e.printStackTrace();
}
// 非同步
call.enqueue(new Callback<List<Repo>>() {
@Override
public void onResponse(Call<List<Repo>> call, Response<List<Repo>> response) {
// 資料返回成功
}
@Override
public void onFailure(Call<List<Repo>> call, Throwable t) {
// 資料返回失敗
}
});
複製程式碼
至此,Retrofit
的一次網路請求就結束了,是不是很簡單。
Retrofit
大大減輕了開發者對於網路請求的操作。
3.Retrofit的URL和引數型別
3.1 Url規則和配置
支援的協議:GET / POST / PUT / DELETE / HEAD / PATCH
比如:
@GET("users/{user}/repos")
GET中的value 要和baseUrl整合一起
我們來看看baseUrl
和value
的整合規則:
第一種:
path 是 絕對路徑形式:
path = "/apath", baseUrl = "http://host:port/a/b"
Url = "http://host:port/apath"
複製程式碼
第二種:
path 是相對路徑,baseUrl 目錄形式:
path = "apath", baseUrl = "http://host:port/a/b/"
Url = "http://host:port/a/b/apath"
複製程式碼
第三種:
path 是相對路徑,baseUrl 是檔案形式:
path = "apath", baseUrl = "http://host:port/a/b"
Url = "http://host:port/a/apath"
複製程式碼
第四種:
path 是完整Url:
path = "http://host:port/aa/apath", baseUrl = "http://host:port/a/b"
Url = "http://host:port/aa/apath"
複製程式碼
建議整個專案都統一使用一種路徑方式,一般選擇第二種方式。
3.2 引數型別
通過註解的形式令Http
請求的引數更加直接。
3.2.1 Query & QueryMap
Query
其實就是Url
中 ?
之後的 key-value
。
比如:
url :"www.println.net/?cate=andro…"
其中 cate=android 就是 Query。
interface PrintlnServer{
@GET("/")
Call<String> cate(@Query("cate") String cate);
}
//引數 cate 就是 它的 key,傳入的值 就是它的 value
複製程式碼
如果擁有多個引數的話 , 可以使用 QueryMap
。
3.2.2 Field & FieldMap
在專案中,大部分情況下我們是使用POST
。
@FormUrlEncoded
@POST("/")
Call<ResponseBody> example(@Field("name") String name,
@Field("occupation") String occupation);
// 需要注意的是 使用Field 的時候,要加 @FormUrlEncoded 用來格式化。
複製程式碼
如果 你有多個引數需要填寫, 當然可以使用 FieldMap
。
3.2.3 Part & PartMap
用於上傳檔案
案例:
public interface FileUploadService {
@Multipart
@POST("upload")
Call<ResponseBody> upload(@Part("description") RequestBody description,
@Part MultipartBody.Part file);
}
// 注意使用 註解 @Multipart
************************ 使用案例 ***********************
//先建立 service
FileUploadService service = retrofit.create(FileUploadService.class);
//構建要上傳的檔案
File file = new File(filename);
RequestBody requestFile =
RequestBody.create(MediaType.parse("application/otcet-stream"), file);
MultipartBody.Part body =
MultipartBody.Part.createFormData("aFile", file.getName(), requestFile);
String descriptionString = "This is a description";
RequestBody description =
RequestBody.create(MediaType.parse("multipart/form-data"), descriptionString);
Call<ResponseBody> call = service.upload(description, body);
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call,
Response<ResponseBody> response) {
System.out.println("success");
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
t.printStackTrace();
}
});
複製程式碼
當然如果需要多個Part
引數,可以使用PartMap
。
接下來我們來看下Retrofit
的原理是如何完成的。
4. Retrofit的原理
4.1 Retrofit的create方法
我們首先進入到create
方法中:
public <T> T create(final Class<T> service) {
// 在這裡驗證 介面是否合理
validateServiceInterface(service);
// 用到了Java的動態代理技術
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
new InvocationHandler() {
private final Platform platform = Platform.get();
private final Object[] emptyArgs = new Object[0];
@Override public @Nullable Object invoke(Object proxy, Method method,
@Nullable Object[] args) throws Throwable {
// 如果物件是Object,不用管它
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
// 是否是Java8
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, proxy, args);
}
return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
}
});
}
複製程式碼
在這裡用到了Java
的動態代理,
可以看到我們在invoke
中,我們進入到了loadServiceMethod
中:
ServiceMethod<?> loadServiceMethod(Method method) {
ServiceMethod<?> result = serviceMethodCache.get(method);
if (result != null) return result;
synchronized (serviceMethodCache) {
result = serviceMethodCache.get(method);
if (result == null) {
result = ServiceMethod.parseAnnotations(this, method);
serviceMethodCache.put(method, result);
}
}
return result;
}
複製程式碼
在這裡運用了快取的技術,因為動態代理和建立一個ServiceMethod
是比較耗時間的,而且一個api
可能伴隨著頻繁的呼叫,所以在這裡使用快取技術可以有效的減少時間的消耗。
當快取中不存在的時候,就需要我們去建立一個新的ServiceMethod
,於是呼叫 ServiceMethod.parseAnnotations(this, method)
:
static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);
Type returnType = method.getGenericReturnType();
if (Utils.hasUnresolvableType(returnType)) {
throw methodError(method,
"Method return type must not include a type variable or wildcard: %s", returnType);
}
if (returnType == void.class) {
throw methodError(method, "Service methods cannot return void.");
}
return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
}
複製程式碼
讓我們再進入到 HttpServiceMethod
中看看:
static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
Retrofit retrofit, Method method, RequestFactory requestFactory) {
//....省略部分程式碼
CallAdapter<ResponseT, ReturnT> callAdapter =
createCallAdapter(retrofit, method, adapterType, annotations);
//.....
Converter<ResponseBody, ResponseT> responseConverter =
createResponseConverter(retrofit, method, responseType);
//. . . .
return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
//....
}
複製程式碼
先來看看 callAdapter
是怎麼建立的?
進入到 createCallAdapter
中:
private static <ResponseT, ReturnT> CallAdapter<ResponseT, ReturnT> createCallAdapter(
Retrofit retrofit, Method method, Type returnType, Annotation[] annotations) {
try {
//noinspection unchecked
return (CallAdapter<ResponseT, ReturnT>) retrofit.callAdapter(returnType, annotations);
} catch (RuntimeException e) { // Wide exception range because factories are user code.
throw methodError(method, 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);
}
複製程式碼
進入到nextCallAdapter()
:
public CallAdapter<?, ?> nextCallAdapter(@Nullable CallAdapter.Factory skipPast, Type returnType,
Annotation[] annotations) {
Objects.requireNonNull(returnType, "returnType == null");
Objects.requireNonNull(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;
}
}
// ...... 省略部分程式碼
}
複製程式碼
可以看到是從 callAdapterFactories
這個list
集合中獲取的,如果我們沒有新增過CallAdapterFactory
即:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
複製程式碼
那麼會自動使用一個預設的CallAdapterFactory:DefaultCallAdapterFactory
這個物件在build()
的時候會新增到集合中去:
public Retrofit build() {
// .....
//在這裡新增 預設的 CallAdapterFactory
List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));
// ....
}
複製程式碼
我們再轉回來,最終會返回一個CallAdapted
的例項,這個類的父類其實就是 HttpServiceMethod
static final class CallAdapted<ResponseT, ReturnT> extends HttpServiceMethod<ResponseT, ReturnT>
複製程式碼
就這樣,在Retrofit
的create
的方法中loadServiceMethod
最終也是 返回了 一個 CallAdapted
例項,
這個例項呼叫了 invoke
方法,但是CallAdapted
類沒有invoke
方法,那麼追溯到它的父類 HttpServiceMethod
:
@Override final @Nullable ReturnT invoke(Object[] args) {
Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
return adapt(call, args);
}
複製程式碼
在這裡建立了 一個 OkHttpCall
,然後呼叫了 adapt
方法,CallAdapted
類實現了這個抽象方法:
@Override protected ReturnT adapt(Call<ResponseT> call, Object[] args) {
return callAdapter.adapt(call);
}
複製程式碼
這個 callAdapter
就是預設的DefaultCallAdapter
:
@Override public Call<Object> adapt(Call<Object> call) {
return executor == null
? call
: new ExecutorCallbackCall<>(executor, call);
}
複製程式碼
當 API
呼叫的時候返回的Call
物件就是這個了。
在這裡 executor
是不為null
的,所以我們得到的 Call
例項 是 ExecutorCallbackCall
。
因為在 Retrofit
類的build()
的方法中:
Executor callbackExecutor = this.callbackExecutor;
if (callbackExecutor == null) {
callbackExecutor = platform.defaultCallbackExecutor();
}
複製程式碼
所以 拿到的是 platform
的 defaultCallbackExecutor()
,
先看看 platform
的建立:
private static Platform findPlatform() {
try {
Class.forName("android.os.Build");
if (Build.VERSION.SDK_INT != 0) {
return new Android();
}
} catch (ClassNotFoundException ignored) {
}
return new Platform(true);
}
複製程式碼
如果是 Android
的話,則建立Andoid
類:
static final class Android extends Platform {
Android() {
super(Build.VERSION.SDK_INT >= 24);
}
@Override public Executor defaultCallbackExecutor() {
return new MainThreadExecutor();
}
static class MainThreadExecutor implements Executor {
private final Handler handler = new Handler(Looper.getMainLooper());
@Override public void execute(Runnable r) {
handler.post(r);
}
}
}
複製程式碼
所以 callbackExecutor
是存在的,且資料會通過handler
傳遞到主執行緒中。
4.2 Retrofit的網路請求
現在我們來看一下 Retrofit
的網路請求:
call.enqueue(new Callback<List<Repo>>() {
@Override
public void onResponse(Call<List<Repo>> call, Response<List<Repo>> response) {
// 資料返回成功
Log.i("zxy", "onResponse: success");
}
@Override
public void onFailure(Call<List<Repo>> call, Throwable t) {
// 資料返回失敗
Log.i("zxy", "onFailure: fail");
}
});
複製程式碼
首先我們知道了 這個call
是 ExecutorCallbackCall
的 例項:
讓我們找到 enqueue
方法:
@Override public void enqueue(final Callback<T> callback) {
Objects.requireNonNull(callback, "callback == null");
// 這個 delegate 就是 OKHttpCall
delegate.enqueue(new Callback<T>() {
@Override public void onResponse(Call<T> call, final Response<T> response) {
// callbackExecutor 會通過handler 將資料回撥到主執行緒
callbackExecutor.execute(() -> {
if (delegate.isCanceled()) {
callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled"));
} else {
callback.onResponse(ExecutorCallbackCall.this, response);
}
});
}
@Override public void onFailure(Call<T> call, final Throwable t) {
callbackExecutor.execute(() -> callback.onFailure(ExecutorCallbackCall.this, t));
}
});
}
複製程式碼
已經知道delegate
就是 OKHttpCall
,現在進入到OKHttpCall
中去找到 enqueue
方法:
@Override public void enqueue(final Callback<T> callback) {
okhttp3.Call call;
Throwable failure;
// .....
try {
// 在這裡建立 OkHttp3的Call
call = rawCall = createRawCall();
} catch (Throwable t) {
throwIfFatal(t);
failure = creationFailure = t;
}
// .....
// 如果存在異常,就回撥出去
if (failure != null) {
callback.onFailure(this, failure);
return;
}
// ......
// OkHttp 執行 請求
call.enqueue(new okhttp3.Callback() {
@Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
Response<T> response;
try {
// 將 response 進行處理
response = parseResponse(rawResponse);
} catch (Throwable e) {
throwIfFatal(e);
callFailure(e);
return;
}
try {
// 成功就回撥出去
callback.onResponse(OkHttpCall.this, response);
} catch (Throwable t) {
throwIfFatal(t);
t.printStackTrace(); // TODO this is not great
}
}
@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) {
throwIfFatal(t);
t.printStackTrace(); // TODO this is not great
}
}
});
}
複製程式碼
其實可以看到最終的網路請求是由OkHttp3
做出的。
我們看這個方法 createRawCall
:
private okhttp3.Call createRawCall() throws IOException {
okhttp3.Call call = callFactory.newCall(requestFactory.create(args));
if (call == null) {
throw new NullPointerException("Call.Factory returned null.");
}
return call;
}
複製程式碼
這裡的callFactory
其實就是 OkHttp3
的 OkHttpClient
的例項,
在Retrofit
的build()
中:
okhttp3.Call.Factory callFactory = this.callFactory;
if (callFactory == null) {
callFactory = new OkHttpClient();
}
複製程式碼
在讓我們看requestFactory
,這個物件 在ServiceMethod
中 就有生成:
static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);
// .......
return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
}
複製程式碼
在requestFactory
的create()
方法中:
okhttp3.Request create(Object[] args) throws IOException {
//......
RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl,
headers, contentType, hasBody, isFormEncoded, isMultipart);
// .....
return requestBuilder.get()
.tag(Invocation.class, new Invocation(method, argumentList))
.build();
}
複製程式碼
在create
最後返回的是OkHttp3
的Request
物件。
所以其實Retrofit
中的網路請求是由OkHttp3
來執行的。
4.3 關於ConverterFactory
在最開始的實現Retrofit
的物件的時候:
.addConverterFactory(GsonConverterFactory.create())
複製程式碼
在HttPServiceMethod
中的parseAnnotations
:
// 這個就是 獲得 responseConverter
Converter<ResponseBody, ResponseT> responseConverter =
createResponseConverter(retrofit, method, responseType);
複製程式碼
讓我們繼續進入到 createResponseConverter
中:
private static <ResponseT> Converter<ResponseBody, ResponseT> createResponseConverter(
Retrofit retrofit, Method method, Type responseType) {
Annotation[] annotations = method.getAnnotations();
try {
return retrofit.responseBodyConverter(responseType, annotations);
} catch (RuntimeException e) { // Wide exception range because factories are user code.
throw methodError(method, e, "Unable to create converter for %s", responseType);
}
}
複製程式碼
繼續進入到retrofit#responseBodyConverter
中:
public <T> Converter<ResponseBody, T> responseBodyConverter(Type type, Annotation[] annotations) {
return nextResponseBodyConverter(null, type, annotations);
}
public <T> Converter<ResponseBody, T> nextResponseBodyConverter(
@Nullable Converter.Factory skipPast, Type type, Annotation[] annotations) {
//......
int start = converterFactories.indexOf(skipPast) + 1;
for (int i = start, count = converterFactories.size(); i < count; i++) {
// 在converter物件
Converter<ResponseBody, ?> converter =
converterFactories.get(i).responseBodyConverter(type, annotations, this);
if (converter != null) {
//noinspection unchecked
return (Converter<ResponseBody, T>) converter;
}
}
//......
}
複製程式碼
拿GsonConverterFactory
為例:
@Override
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
Retrofit retrofit) {
TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
// 在這裡就是返回了Converter物件
return new GsonResponseBodyConverter<>(gson, adapter);
}
複製程式碼
那麼它在哪裡被使用呢?
在call
執行enqueue
的時候,通過回撥onResponse
返回資料,在parseResponse()
方法中會對資料進行處理:
Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
ResponseBody rawBody = rawResponse.body();
//......
ExceptionCatchingResponseBody catchingBody = new ExceptionCatchingResponseBody(rawBody);
try {
// 這裡的responseConverter 就是 GsonResponseBodyConverter
T body = responseConverter.convert(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;
}
}
複製程式碼
在GsonResponseBodyConverter
的重寫方法convert
中就會對資料進行處理:
@Override public T convert(ResponseBody value) throws IOException {
JsonReader jsonReader = gson.newJsonReader(value.charStream());
try {
return adapter.read(jsonReader);
} finally {
value.close();
}
}
複製程式碼
到這裡,Retrofit
的分析就結束了。
總結
Retrofit
的程式碼簡單優雅,值得我們去學習,學習原始碼有助於我們更好的思考。從原始碼中不僅僅是學習優秀的程式碼也是學習大神們的思想。
這裡有一些優秀的博文值得學習: