Retrofit是一個Restful設計風格的Http網路請求框架,基於OkHttp
功能強大、簡潔易用、可延伸性好
任何網路請求的場景都應該優先選擇,特別是後臺API遵循restful API設計風格或專案中使用到RxJava時
準確來說,Retrofit是一個RESTful的Http網路請求框架的封裝
原因:網路請求的工作本質上是Okhttp完成,而Retrofit僅負責啊網路請求介面的封裝
App應用程式通過Retrofit請求網路,實際上是使用Retrofit介面層封裝請求參數、Header、Url等資訊,之後由OkHttp完成後續的請求操作
在伺服器端返回數據之後,Okhttp將原始的結果交給Retrofit,Retrofit根據使用者的需求對結果進行解析
封裝了數據轉換、執行緒切換的操作
Retrofit把網路請求的Url分成了兩部分設定:
// 第1部分:在網路請求介面的註解設定
@GET("openapi.do?keyfrom=Yanzhikai&key=2032414398&type=data&doctype=json&version=1.1&q=car")
Call<Translation> getCall();
// 第2部分:在建立Retrofit範例時通過.baseUrl()設定
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://fanyi.youdao.com/") //設定網路請求的Url地址
.addConverterFactory(GsonConverterFactory.create()) //設定數據解析器
.build();
// 從上面看出:一個請求的URL可以通過 替換塊 和 請求方法的參數 來進行動態的URL更新。
// 替換塊是由 被{}包裹起來的字串構成
// 即:Retrofit支援動態改變網路請求根目錄
網路請求的完整Url : 在建立Retrofit範例時通過.baseUrl()設定 + 網路請求介面的註解設定
每個鍵值對需要用@Filed來註解鍵名,隨後的物件需要提供值
每個鍵值對需要用@Part來註解鍵名,隨後的物件需要提供值
public interface GetRequest_Interface {
/**
*表明是一個表單格式的請求(Content-Type:application/x-www-form-urlencoded)
* <code>Field("username")</code> 表示將後面的 <code>String name</code> 中name的取值作爲 username 的值
*/
@POST("/form")
@FormUrlEncoded
Call<ResponseBody> testFormUrlEncoded1(@Field("username") String name, @Field("age") int age);
/**
* {@link Part} 後面支援三種類型,{@link RequestBody}、{@link okhttp3.MultipartBody.Part} 、任意型別
* 除 {@link okhttp3.MultipartBody.Part} 以外,其它型別都必須帶上表單欄位({@link okhttp3.MultipartBody.Part} 中已經包含了表單欄位的資訊),
*/
@POST("/form")
@Multipart
Call<ResponseBody> testFileUpload1(@Part("name") RequestBody name, @Part("age") RequestBody age, @Part MultipartBody.Part file);
}
// 具體使用
GetRequest_Interface service = retrofit.create(GetRequest_Interface.class);
// @FormUrlEncoded
Call<ResponseBody> call1 = service.testFormUrlEncoded1("Carson", 24);
// @Multipart
RequestBody name = RequestBody.create(textType, "Carson");
RequestBody age = RequestBody.create(textType, "24");
MultipartBody.Part filePart = MultipartBody.Part.createFormData("file", "test.txt", file);
Call<ResponseBody> call3 = service.testFileUpload1(name, age, filePart);
// @Header
@GET("user")
Call<User> getUser(@Header("Authorization") String authorization)
// @Headers
@Headers("Authorization: authorization")
@GET("user")
Call<User> getUser()
// 以上的效果是一致的。
// 區別在於使用場景和使用方式
// 1. 使用場景:@Header用於新增不固定的請求頭,@Headers用於新增固定的請求頭
// 2. 使用方式:@Header作用於方法的參數;@Headers作用於方法
Map經過FormBody.Builder類處理成符合OkHttp格式的表單,如:
FormBody.Builder builder = new FormBody.Builder();
builder.add("key","value");
public interface GetRequest_Interface {
/**
*表明是一個表單格式的請求(Content-Type:application/x-www-form-urlencoded)
* <code>Field("username")</code> 表示將後面的 <code>String name</code> 中name的取值作爲 username 的值
*/
@POST("/form")
@FormUrlEncoded
Call<ResponseBody> testFormUrlEncoded1(@Field("username") String name, @Field("age") int age);
/**
* Map的key作爲表單的鍵
*/
@POST("/form")
@FormUrlEncoded
Call<ResponseBody> testFormUrlEncoded2(@FieldMap Map<String, Object> map);
}
// 具體使用
// @Field
Call<ResponseBody> call1 = service.testFormUrlEncoded1("Carson", 24);
// @FieldMap
// 實現的效果與上面相同,但要傳入Map
Map<String, Object> map = new HashMap<>();
map.put("username", "Carson");
map.put("age", 24);
Call<ResponseBody> call2 = service.testFormUrlEncoded2(map);
如:url = http://www.println.net/?cate=android 其中,Query = cate
@GET("/")
Call<String> cate(@Query("cate") String cate);
}
// 其使用方式同 @Field與@FieldMap,這裏不作過多描述
作用
URL地址的預設值
具體使用
public interface GetRequest_Interface {
@GET("users/{user}/repos")
Call<ResponseBody> getBlog(@Path("user") String user );
// 存取的API是:https://api.github.com/users/{user}/repos
// 在發起請求時, {user} 會被替換爲方法的第一個參數 user(被@Path註解作用)
}
作用
直接傳入一個請求的Url變數用於URL設定
具體使用
public interface GetRequest_Interface {
@GET
Call<ResponseBody> testUrlAndQuery(@Url String url, @Query("showAll") boolean showAll);
// 當有URL註解時,@GET傳入的URL就可以省略
// 當GET、POST...HTTP等方法中沒有設定Url時,則必須使用 {@link Url}提供
}
1.通過解析 網路請求介面的註解 設定網路請求參數
2.通過 動態代理 生成網路請求物件
3.通過 網路請求適配器 將網路請求物件進行平臺適配
4.通過 網路請求執行器 發送網路請求
5.通過 數據轉換器 解析伺服器返回的數據
6.通過回撥執行器切換執行緒(子執行緒 -> 主執行緒)
7.使用者在主執行緒處理返回結果
<-- Retrofit類 -->
public final class Retrofit {
private final Map<Method, ServiceMethod> serviceMethodCache = new LinkedHashMap<>();
// 網路請求設定物件(對網路請求介面中方法註解進行解析後得到的物件)
// 作用:儲存網路請求相關的設定,如網路請求的方法、數據轉換器、網路請求適配器、網路請求工廠、基地址等
private final HttpUrl baseUrl;
// 網路請求的url地址
private final okhttp3.Call.Factory callFactory;
// 網路請求器的工廠
// 作用:生產網路請求器(Call)
// Retrofit是預設使用okhttp
private final List<CallAdapter.Factory> adapterFactories;
// 網路請求適配器工廠的集合
// 作用:放置網路請求適配器工廠
// 網路請求適配器工廠作用:生產網路請求適配器(CallAdapter)
// 下面 下麪會詳細說明
private final List<Converter.Factory> converterFactories;
// 數據轉換器工廠的集合
// 作用:放置數據轉換器工廠
// 數據轉換器工廠作用:生產數據轉換器(converter)
private final Executor callbackExecutor;
// 回撥方法執行器
private final boolean validateEagerly;
// 標誌位
// 作用:是否提前對業務介面中的註解進行驗證轉換的標誌位
<-- Retrofit類別建構函式 -->
Retrofit(okhttp3.Call.Factory callFactory, HttpUrl baseUrl,
List<Converter.Factory> converterFactories, List<CallAdapter.Factory> adapterFactories,
Executor callbackExecutor, boolean validateEagerly) {
this.callFactory = callFactory;
this.baseUrl = baseUrl;
this.converterFactories = unmodifiableList(converterFactories);
this.adapterFactories = unmodifiableList(adapterFactories);
// unmodifiableList(list)近似於UnmodifiableList<E>(list)
// 作用:建立的新物件能夠對list數據進行存取,但不可通過該物件對list集閤中的元素進行修改
this.callbackExecutor = callbackExecutor;
this.validateEagerly = validateEagerly;
...
// 僅貼出關鍵程式碼
}
1.Call在Retrofit裡預設是OkHttpCall
2.在Retrofit中提供了四種CallAdapterFactory:ExecutorCallAdapterFactory(預設)、GuavaCallAdapterFactory、Java8CallAdapterFactory、RxJavaCallAdapterFactory
1.例如:一開始Retrofit只打算利用OkHttpCall通過ExecutorCallbackCall切換執行緒,但是後來發現使用RxJava更加方便(不需要Handler切換執行緒)。想要實現Rxjava的方式,就要使用RxJavaCallAdapterFactoryCallAdapter將OkHttpCall轉換成RxJava(Scheduler);
2.Retrofit還支援java8、Guava平臺
<-- Builder類-->
public static final class Builder {
private Platform platform;
private okhttp3.Call.Factory callFactory;
private HttpUrl baseUrl;
private List<Converter.Factory> converterFactories = new ArrayList<>();
private List<CallAdapter.Factory> adapterFactories = new ArrayList<>();
private Executor callbackExecutor;
private boolean validateEagerly;
// 從上面可以發現, Builder類的成員變數與Retrofit類的成員變數是對應的
// 所以Retrofit類的成員變數基本上是通過Builder類進行設定
// 開始看步驟1
<-- 步驟1 -->
// Builder的構造方法(無參)
public Builder() {
this(Platform.get());
// 用this呼叫自己的有參構造方法public Builder(Platform platform) ->>步驟5(看完步驟2、3、4再看)
// 並通過呼叫Platform.get()傳入了Platform物件
// 繼續看Platform.get()方法 ->>步驟2
// 記得最後繼續看步驟5的Builder有參構造方法
}
...
}
<-- 步驟2 -->
class Platform {
private static final Platform PLATFORM = findPlatform();
// 將findPlatform()賦給靜態變數
static Platform get() {
return PLATFORM;
// 返回靜態變數PLATFORM,即findPlatform() ->>步驟3
}
<-- 步驟3 -->
private static Platform findPlatform() {
try {
Class.forName("android.os.Build");
// Class.forName(xxx.xx.xx)的作用:要求JVM查詢並載入指定的類(即JVM會執行該類的靜態程式碼段)
if (Build.VERSION.SDK_INT != 0) {
return new Android();
// 此處表示:如果是Android平臺,就建立並返回一個Android物件 ->>步驟4
}
} catch (ClassNotFoundException ignored) {
}
try {
// 支援Java平臺
Class.forName("java.util.Optional");
return new Java8();
} catch (ClassNotFoundException ignored) {
}
try {
// 支援iOS平臺
Class.forName("org.robovm.apple.foundation.NSObject");
return new IOS();
} catch (ClassNotFoundException ignored) {
}
// 從上面看出:Retrofit2.0支援3個平臺:Android平臺、Java平臺、IOS平臺
// 最後返回一個Platform物件(指定了Android平臺)給Builder的有參構造方法public Builder(Platform platform) --> 步驟5
// 說明Builder指定了執行平臺爲Android
return new Platform();
}
...
}
<-- 步驟4 -->
// 用於接收伺服器返回數據後進行執行緒切換在主執行緒顯示結果
static class Android extends Platform {
@Override
CallAdapter.Factory defaultCallAdapterFactory(Executor callbackExecutor) {
return new ExecutorCallAdapterFactory(callbackExecutor);
// 建立預設的網路請求適配器工廠
// 該預設工廠生產的 adapter 會使得Call在非同步呼叫時在指定的 Executor 上執行回撥
// 在Retrofit中提供了四種CallAdapterFactory: ExecutorCallAdapterFactory(預設)、GuavaCallAdapterFactory、Java8CallAdapterFactory、RxJavaCallAdapterFactory
// 採用了策略模式
}
@Override
public Executor defaultCallbackExecutor() {
// 返回一個預設的回撥方法執行器
// 該執行器作用:切換執行緒(子->>主執行緒),並在主執行緒(UI執行緒)中執行回撥方法
return new MainThreadExecutor();
}
static class MainThreadExecutor implements Executor {
private final Handler handler = new Handler(Looper.getMainLooper());
// 獲取與Android 主執行緒系結的Handler
@Override
public void execute(Runnable r) {
handler.post(r);
// 該Handler是上面獲取的與Android 主執行緒系結的Handler
// 在UI執行緒進行對網路請求返回數據處理等操作。
}
}
// 切換執行緒的流程:
// 1. 回撥ExecutorCallAdapterFactory生成了一個ExecutorCallbackCall物件
//2. 通過呼叫ExecutorCallbackCall.enqueue(CallBack)從而呼叫MainThreadExecutor的execute()通過handler切換到主執行緒
}
// 下面 下麪繼續看步驟5的Builder有參構造方法
<-- 步驟5 -->
// Builder類別建構函式2(有參)
public Builder(Platform platform) {
// 接收Platform物件(Android平臺)
this.platform = platform;
// 通過傳入BuiltInConverters()物件設定數據轉換器工廠(converterFactories)
// converterFactories是一個存放數據轉換器Converter.Factory的陣列
// 設定converterFactories即設定裏面的數據轉換器
converterFactories.add(new BuiltInConverters());
// BuiltInConverters是一個內建的數據轉換器工廠(繼承Converter.Factory類)
// new BuiltInConverters()是爲了初始化數據轉換器
}
CallAdapter用於對原始Call進行再次封裝,如Call到Observable
<-- 步驟1 -->
public Builder baseUrl(String baseUrl) {
// 把String型別的url參數轉化爲適合OKhttp的HttpUrl型別
HttpUrl httpUrl = HttpUrl.parse(baseUrl);
// 最終返迴帶httpUrl型別參數的baseUrl()
// 下面 下麪繼續看baseUrl(httpUrl) ->> 步驟2
return baseUrl(httpUrl);
}
<-- 步驟2 -->
public Builder baseUrl(HttpUrl baseUrl) {
//把URL參數分割成幾個路徑碎片
List<String> pathSegments = baseUrl.pathSegments();
// 檢測最後一個碎片來檢查URL參數是不是以"/"結尾
// 不是就拋出異常
if (!"".equals(pathSegments.get(pathSegments.size() - 1))) {
throw new IllegalArgumentException("baseUrl must end in /: " + baseUrl);
}
this.baseUrl = baseUrl;
return this;
}
baseUrl()用於設定Retrofit類的網路請求url地址。它可以將傳入的String型別的url轉換爲Okhttp的HttpUrl型別的url
public final class GsonConverterFactory extends Converter.Factory {
<-- 步驟1 -->
public static GsonConverterFactory create() {
// 建立一個Gson物件
return create(new Gson()); ->>步驟2
}
<-- 步驟2 -->
public static GsonConverterFactory create(Gson gson) {
// 建立了一個含有Gson物件範例的GsonConverterFactory
return new GsonConverterFactory(gson); ->>步驟3
}
private final Gson gson;
<-- 步驟3 -->
private GsonConverterFactory(Gson gson) {
if (gson == null) throw new NullPointerException("gson == null");
this.gson = gson;
}
所以,GsonConverterFactory.create()是建立了一個含有Gson物件範例的GsonConverterFactory,並返回給addConverterFactory
// 將上面建立的GsonConverterFactory放入到 converterFactories陣列
// 在第二步放入一個內建的數據轉換器工廠BuiltInConverters()後又放入了一個GsonConverterFactory
public Builder addConverterFactory(Converter.Factory factory) {
converterFactories.add(checkNotNull(factory, "factory == null"));
return this;
}
1.Retrofit預設使用Gson進行解析
2.若使用其他解析方式(Json、XML、Protobuf),也可以通過自定義數據解析器來實現(必須繼承Converter.Factory)
對網路請求介面的方法中的每個參數利用對應ParameterHandler進行解析,再根據ServiceMethod物件建立一個OkHttp的Request物件
使用OkHttp的Request發送網路請求
對返回的數據使用之前設定的數據轉換器(GsonConverterFactory)解析返回的數據,最終得到一個Response物件
進行執行緒切換從而在主執行緒處理返回的數據結果
若使用了RxJava,則直接回撥到主執行緒
非同步請求的過程跟同步請求類似,唯一不同的是:非同步請求會將回撥方法交給回撥執行器在指定的執行緒中執行(UI執行緒)
Retrofit使用建造者模式通過Builder類建立了一個Retrofit範例,具體建立細節:
平臺型別物件(plateform-Android)
網路請求的url地址(baseUrl)
網路請求工廠(callFactory)
預設使用OkHttpCall
本質是設定了網路請求適配器工廠——預設是ExecutorCallAdapterFactory
本質是設定了數據轉換器工廠
預設回撥方法執行器作用是:切換執行緒(子執行緒 -> 主執行緒)