1. 程式人生 > >mvp+dagger2+retrofit2+rxjava 專案框架 最佳實踐

mvp+dagger2+retrofit2+rxjava 專案框架 最佳實踐

概述

轉載地址: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