OkHttp+Retrofit+Dagger2+RxJava+MVP架構 學習筆記
一口吃不成一個大胖子,一步一步地講解各個框架特性及使用,再連線起來。
文章目錄
- OkHttp
- Retrofit
- Retrofit註解
- 請求姿勢
- 1. 建立 接收伺服器返回資料 的類
- 2. 建立 用於描述網路請求 的介面
- 3. 建立 Retrofit的例項 並 發起請求
- 4. 關於資料解析器(Converter)
- 5. 關於網路請求介面卡(CallAdapter)
- 更多
- RxJava
- Dagger2
- MVP架構
OkHttp
implementation 'com.squareup.okhttp3:okhttp:3.11.0'
Header的設定
- 使用header(name,value)來設定HTTP頭的唯一值,如果請求中已經存在響應的資訊那麼直接替換掉。
- 使用addHeader(name,value)來補充新值,如果請求頭中已經存在name的name-value,那麼還會繼續新增,請求頭中便會存在多個name相同而value不同的“鍵值對”。
- 使用header(name)讀取唯一值或多個值的最後一個值
- 使用headers(name)獲取所有值
GET & POST請求
private String post() throws IOException {
MediaType mediaType = MediaType.parse("text/x-markdown; charset=utf-8");
String requestBody = "I am Renly.";
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(NetConfig.REQUEST_URL)
.header("Token","TokenValue")
.addHeader("HeaderName","HeaderValue")
.post(RequestBody.create(mediaType, requestBody)) // 預設是GET請求,GET請求可以不寫
.build();
// 同步請求
Response response = client.newCall(request).execute();
// 非同步請求,此方法不適合該方法
// Response response = client.newCall(request).enqueue(new Callback() {
// @Override
// public void onFailure(Call call, IOException e) {
// Log.e(TAG,"getResponse fail " + e.getMessage());
// }
//
// @Override
// public void onResponse(Call call, Response response) throws IOException {
// Log.e(TAG,"getResponse success " + response.body().string());
// }
// });
return response.body().string();
}
攔截器-interceptor
OkHttp的攔截器鏈可謂是其整個框架的精髓,使用者可傳入的 interceptor 分為兩類:
- 一類是全域性的 interceptor,該類 interceptor 在整個攔截器鏈中最早被呼叫,通過 OkHttpClient.Builder#addInterceptor(Interceptor) 傳入;
- 另外一類是非網頁請求的 interceptor ,這類攔截器只會在非網頁請求中被呼叫,並且是在組裝完請求之後,真正發起網路請求前被呼叫,所有的 interceptor 被儲存在 List<Interceptor> interceptors 集合中,按照新增順序來逐個呼叫,具體可參考 RealCall#getResponseWithInterceptorChain() 方法。通過 OkHttpClient.Builder#addNetworkInterceptor(Interceptor) 傳入;
這裡舉一個簡單的例子,例如有這樣一個需求,我要監控App通過 OkHttp 發出的所有原始請求,以及整個請求所耗費的時間,針對這樣的需求就可以使用第一類全域性的 interceptor 在攔截器鏈頭去做。
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.addInterceptor(new LoggingInterceptor())
.build();
Request request = new Request.Builder()
.url("http://www.publicobject.com/helloworld.txt")
.header("User-Agent", "OkHttp Example")
.build();
okHttpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.d(TAG, "onFailure: " + e.getMessage());
}
@Override
public void onResponse(Call call, Response response) throws IOException {
ResponseBody body = response.body();
if (body != null) {
Log.d(TAG, "onResponse: " + response.body().string());
body.close();
}
}
});
public class LoggingInterceptor implements Interceptor {
private static final String TAG = "LoggingInterceptor";
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
long startTime = System.nanoTime();
Log.d(TAG, String.format("Sending request %s on %s%n%s",
request.url(), chain.connection(), request.headers()));
Response response = chain.proceed(request);
long endTime = System.nanoTime();
Log.d(TAG, String.format("Received response for %s in %.1fms%n%s",
response.request().url(), (endTime - startTime) / 1e6d, response.headers()));
return response;
}
}
Retrofit
implementation 'com.squareup.retrofit2:retrofit:2.4.0'
Retrofit註解
-
請求方法
-
請求引數
-
請求標記
請求姿勢
1. 建立 接收伺服器返回資料 的類
public class Comment {
private long id;
private String body;
private String user_id;
......
}
2. 建立 用於描述網路請求 的介面
Retrofit將 Http請求 抽象成 Java介面:採用 註解 描述網路請求引數和配置網路請求引數
用 動態代理 動態 將該介面的註解“翻譯”成一個 Http 請求,最後再執行 Http 請求
注:介面中的每個方法的引數都需要使用註解標註,否則會報錯
public interface APi {
// @GET註解的作用:採用Get方法傳送網路請求
// "comments"用於在建立Retrofit例項時拼接網路請求的URL
@GET("comments")
Call<List<Comment>> getComments(@Header("Authorization") String authorization, @Query("page")int page);
// getComments() = 接收網路請求資料的方法
// 其中返回型別為Call<*>,*是接收資料的類(即上面定義的Comment類)
@Headers("Authorization: authorization")
@POST("login")
Call<ResponseBody> doLogin(@Query("email") String email, @Query("password") String password);
// 如果想直接獲得Responsebody中的內容,可以定義網路請求返回值為Call<ResponseBody>
// Header:
// 以上的效果是一致的。
// 區別在於使用場景和使用方式
// 1. 使用場景:@Header用於新增不固定的請求頭,@Headers用於新增固定的請求頭
// 2. 使用方式:@Header作用於方法的引數;@Headers作用於方法
}
3. 建立 Retrofit的例項 並 發起請求
只演示post請求,get請求同理
private void TestRetrofit() {
OkHttpClient client = new OkHttpClient.Builder().build();
// 建立Retrofit例項
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(NetConfig.BASE_URL) //網路請求完整的URL = 通過例項化時.baseUrl()設定+網路請求介面的註解設定
.addConverterFactory(GsonConverterFactory.create()) // 設定資料解析器
.addCallAdapterFactory(RxJavaCallAdapterFactory.create()) // 支援RxJava平臺
.client(client)
.build();
APi mApi = retrofit.create(APi.class);
//傳送網路請求(非同步)
retrofit2.Call<ResponseBody> call = mApi.doLogin("[email protected]", "testPwd");
call.enqueue(new retrofit2.Callback<ResponseBody>() {
@Override
public void onResponse(retrofit2.Call<ResponseBody> call, retrofit2.Response<ResponseBody> response) {
//請求成功時回撥
Log.e("print","onResponse");
try {
tv.setText(response.body().string());
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void onFailure(retrofit2.Call<ResponseBody> call, Throwable t) {
//請求失敗時候的回撥
Log.e("print","onFailure " + t.getMessage());
}
});
// 傳送網路請求(同步)
Response<ResponseBody> response = call.execute();
}
4. 關於資料解析器(Converter)
//設定資料解析器
.addConverterFactory(GsonConverterFactory.create())
這個有啥用?這句話的作用就是使得來自介面的json結果會自動解析成定義好了的欄位和型別都相符的json物件接受類。在Retrofit 2.0中,Package 中已經沒有Converter了,所以,你需要自己建立一個Converter, 不然的話Retrofit只能接收字串結果,你也只能拿到一串字元,剩下的json轉換的活還得你自己來幹。所以,如果你想接收json結果並自動轉換成解析好的接收類,必須自己建立Converter物件,然後使用addConverterFactory把它新增進來!
Retrofit支援多種資料解析方式,在使用時注意需要在Gradle新增依賴:
資料解析器 | Gradle依賴 |
---|---|
Gson | com.squareup.retrofit2:converter-gson:2.0.2 |
Jackson | com.squareup.retrofit2:converter-jackson:2.0.2 |
Simple XML | com.squareup.retrofit2:converter-simplexml:2.0.2 |
Protobuf | com.squareup.retrofit2:converter-protobuf:2.0.2 |
Moshi | com.squareup.retrofit2:converter-moshi:2.0.2 |
Wire | com.squareup.retrofit2:converter-wire:2.0.2 |
Scalars | com.squareup.retrofit2:converter-scalars:2.0.2 |
5. 關於網路請求介面卡(CallAdapter)
//設定網路請求介面卡
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
網路請求介面卡 | Gradle依賴 |
---|---|
Guava | com.squareup.retrofit2:adapter-guava:2.0.2 |
Java8 | com.squareup.retrofit2:adapter-java8:2.0.2 |
RXJava | com.squareup.retrofit2:adapter-rxjava:2.0.2 |
更多
注:addConverterFactory是有先後順序的,如果有多個ConverterFactory都支援(就是大神所謂的我可以處理)同一種類型,那麼就是隻有第一個才會被使用,而GsonConverterFactory是不判斷是否支援的(直接返回Converter)
附時序圖
推薦閱讀:
這是一份很詳細的 Retrofit 2.0 使用教程(含例項講解)
Retrofit分析-漂亮的解耦套路
Retrofit Javadoc:Retrofit官方 API
RxJava
implementation 'io.reactivex.rxjava2:rxandroid:2.1.0'
implementation 'io.reactivex.rxjava2:rxjava:2.2.3'
由於這部分知識過於龐大,自知實力沒法提煉出精髓就不班門弄斧了,這裡推薦幾篇文章,這些文章對我理解RxJava有很大的幫助,推薦結合看才能深入瞭解。
優美的非同步 — RxAndroid
給 Android 開發者的 RxJava 詳解
RxJava原始碼分析與實戰 關於RxJava的專欄,適於深入瞭解(像是flatMap,retryWhen操作符)
接下來會開一個專案來寫OkHttp+Retrofit+RxJava
Dagger2
implementation 'com.google.dagger:dagger:2.19'
annotationProcessor 'com.google.dagger:dagger-compiler:2.19'
關鍵的註解
@Inject
這個註解是用來說明該註解下方的屬性或方法需要依賴注入。(如果使用在類構造方法上,則該類也會被註冊在DI容器中作為注入物件。很重要,理解這個,就能理解Presenter注入到Activity的步驟!)
@Provider
在@Module註解的類中,使用@Provider註解,說明提供依賴注入的具體物件
@Component
簡單說就是,可以通過Component訪問到Module中提供的依賴注入物件。假設,如果有兩個Module,AModule、BModule,如果Component只註冊了AModule,而沒有註冊BModule,那麼BModule中提供的物件,無法進行依賴注入!
@SubComponent
該註解從名字上就能知道,它是子Component,會完全繼承父Component的所有依賴注入物件!
@Sigleton
被註解的物件,在App中是單例存在的!
@Scope
用來標註依賴注入物件的適用範圍。
@Named(@)
因為Dagger2 的以來注入是使用型別推斷的,所以同一型別的物件就無法區分,可以使用@Named註解區分同一型別物件,可以理解為物件的別名!
MVP架構
MVP是什麼
我們所說的mvp架構,是google開源的一個設計模式,目的是為了將程式碼更加優雅清晰的呈現出來,廢話也不多說,直接分析
M:M層,也就是我們在程式中經常出現的model層,他的功能就是處理資料,其他任務不由他來接手。
V:V層,我們的view層,也就是顯示資料的地方,我們在得到資料之後,把資料傳遞給view層,通過他來顯示資料。同時,view層的點選事件等處理會在這裡出現,但真正的資料處理不是在這裡,而是在model層中處理。
P:P層,也就是Presenter層,他是我們mvp架構中的中間人,通過p層的連線,讓我們可以是M層和V層進行通訊。M層在獲取到資料之後,把它交給P層,P層在交給View層,同樣,View層的點選事件等處理通過P層去通知M層,讓他去進行資料處理。