mvp+dagger2+retrofit2+rxjava 專案框架 最佳實踐
阿新 • • 發佈:2019-02-05
概述
轉載地址:http://www.jianshu.com/p/d7b9efde7e15
原來一直在用Android最原生的框架進行開發,最多也就使用了butterknife,減少了很多的findviewById。前段時間看google的IO大會,偶爾聽到了新型的Android開發框架dagger2等等,然後對此框架產生了濃厚的興趣。
通過一段時間的深入學習,把我的學習分享出來,希望大家能夠喜歡。
mvp+dagger2+retrofit2+rxjava 一套開發模式自我感覺將是以後Android開發的趨勢,儘早的用起來吧。
使用新型框架能給我們帶來什麼好處?
- 解耦,降低模組耦合度。
- 可以更方便的寫單元測試。
- 減少Activity編碼
- 提高團隊協作的效率
- 提高編碼的效率
- 提高程式碼的可讀性
示例:
本文示例功能:
- retrofit2+Rxjava進行Http和Https網路請求封裝
- MVP工程結構
- Rxjava的使用示例
- dagger2的使用示例
說明
閱讀此文首先你要對以下技術有一定的瞭解。對以上技術還不熟悉的朋友可以先去了解一下。
在我閱讀過無數相關技術文章之後,我給大家推薦這些技術學習的文章:
dagger2
mvp:
retrofit2:
rxjava:
對上面的技術有一定的瞭解後,我們開始一個示例:
架構搭建
首先我們要一個示例的方式來詳細說明整體專案的架構與思想
示例功能;
- 登入功能
- 檢查使用者名稱和密碼是否合法
- 登入按鈕如果不合法則不可點選,合法後登入按鈕可以點選
- 呼叫登入介面進行登入
- 將使用者名稱和密碼儲存本地
- 文章列表
- 從網路獲取文章列表並展示
- 將文章列表儲存到資料庫
- 點選列表進入文章詳情
- 網路獲取圖片
- 單元測試
- 整合測試
- 單元測試
整體架構圖
未命名.png
執行webserver json
//安裝json-server
$ npm install -g json-server
//進入工程目錄
$ cd AndroidArchitecture/
//執行服務
json-server --watch login.json
關於工程
下圖為demo目錄介面,檢視demo原始碼可以參考此結構
AndroidArchitecture.png
關鍵程式碼
-
網路請求返回訊息體統一錯誤處理
訊息體結構
//登入
{
"status_msg" : "登入成功",
"status_code" : 200,
"data" : {
"username" : "admin",
"id" : 1,
"password" : "123456",
"gender" : "男"
}
}
模型結構
public class BaseResponse<T> {
private int status_code;
private String status_msg;
private T data;
public int getStatus_code() {
return status_code;
}
public void setStatus_code(int status_code) {
this.status_code = status_code;
}
public String getStatus_msg() {
return status_msg;
}
public void setStatus_msg(String status_msg) {
this.status_msg = status_msg;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
使用Rxjava物件變換
public class BaseResponseFunc<T> implements Func1<BaseResponse<T>, Observable<T>> {
@Override
public Observable<T> call(BaseResponse<T> tBaseResponse) {
//遇到非200錯誤統一處理,將BaseResponse轉換成您想要的物件
if (tBaseResponse.getStatus_code() != 200) {
return Observable.error(new Throwable(tBaseResponse.getStatus_msg()));
}else{
return Observable.just(tBaseResponse.getData());
}
}
}
自定義訂閱者
/**
* 錯誤統一處理
*
* Created by wanglj on 16/7/4.
*/
public class ExceptionSubscriber<T> extends Subscriber<T> {
private SimpleCallback<T> simpleCallback;
private Application application;
public ExceptionSubscriber(SimpleCallback simpleCallback, Application application){
this.simpleCallback = simpleCallback;
this.application = application;
}
@Override
public void onStart() {
super.onStart();
if(simpleCallback != null)
simpleCallback.onStart();
}
@Override
public void onCompleted() {
if(simpleCallback != null)
simpleCallback.onComplete();
}
@Override
public void onError(Throwable e) {
e.printStackTrace();
if (e instanceof SocketTimeoutException) {
Toast.makeText(application, "網路中斷,請檢查您的網路狀態", Toast.LENGTH_SHORT).show();
} else if (e instanceof ConnectException) {
Toast.makeText(application, "網路中斷,請檢查您的網路狀態", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(application, "error:" + e.getMessage(), Toast.LENGTH_SHORT).show();
}
if(simpleCallback != null)
simpleCallback.onComplete();
}
@Override
public void onNext(T t) {
if(simpleCallback != null)
simpleCallback.onNext(t);
}
}
簡單的回撥模型
public interface SimpleCallback<T> {
void onStart();
void onNext(T t);
void onComplete();
}
presenter層呼叫
public void login(String username,String password){
apiManager.login(username, password, new SimpleCallback<User>() {
@Override
public void onStart() {
loginView.showLoading();
}
@Override
public void onNext(User user) {
loginView.showUser(user);
}
@Override
public void onComplete() {
loginView.hideLoading();
}
});
}
- 對外提供ApiManager以及retrofit的封裝
@Module
public class ApiModule {
@Provides
@Singleton
public OkHttpClient provideOkHttpClient() {
final OkHttpClient.Builder builder = new OkHttpClient.Builder();
//新增logo日誌列印網路請求的攔截器
if (BuildConfig.DEBUG) {
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
builder.addInterceptor(logging);
}
builder.connectTimeout(60 * 1000, TimeUnit.MILLISECONDS)
.readTimeout(60 * 1000, TimeUnit.MILLISECONDS);
return builder.build();
}
@Provides
@Singleton
public Retrofit provideRestAdapter(OkHttpClient okHttpClient) {
Retrofit.Builder builder = new Retrofit.Builder();
builder.client(okHttpClient)
.baseUrl(ApiService.SERVER_URL)
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create());
return builder.build();
}
@Provides
@Singleton
public ApiService provideApiService(Retrofit restAdapter) {
return restAdapter.create(ApiService.class);
}
@Provides
@Singleton
public ApiManager provideApiManager(Application application,ApiService githubApiService) {
return new ApiManager(githubApiService,application);
}
}
- 所有的全域性共用物件都可以在AppModule裡對外提供,比如PreferencesManager DatabaseManager等等
更高階的用法--dagger2 劃分更細的scope
目前demo示例是將功能模組直接依賴於整個APP,其實我們可以劃分更細的作用域。使一個物件的生命週期存在於多個功能模組中。
比如:專案中登入成功後,獲取文章列表需要使用者資訊,獲取文章詳情以及文章下的評論列表,又需要當前文章和使用者的資訊。那麼我們就可以這樣設計我們的工程架構如圖:
scope.png