mvp+dagger2+retrofit2+rxjava 項(xiàng)目框架 最佳實(shí)踐

概述

原來(lái)一直在用Android最原生的框架進(jìn)行開發(fā),最多也就使用了butterknife,減少了很多的findviewById。前段時(shí)間看google的IO大會(huì),偶爾聽到了新型的Android開發(fā)框架dagger2等等,然后對(duì)此框架產(chǎn)生了濃厚的興趣。

通過一段時(shí)間的深入學(xué)習(xí),把我的學(xué)習(xí)分享出來(lái),希望大家能夠喜歡。

mvp+dagger2+retrofit2+rxjava 一套開發(fā)模式自我感覺將是以后Android開發(fā)的趨勢(shì),盡早的用起來(lái)吧。

使用新型框架能給我們帶來(lái)什么好處?

  • 解耦,降低模塊耦合度。
  • 可以更方便的寫單元測(cè)試。
  • 減少Activity編碼
  • 提高團(tuán)隊(duì)協(xié)作的效率
  • 提高編碼的效率
  • 提高代碼的可讀性

示例:

本文示例功能:

  • retrofit2+Rxjava進(jìn)行Http和Https網(wǎng)絡(luò)請(qǐng)求封裝
  • MVP工程結(jié)構(gòu)
  • Rxjava的使用示例
  • dagger2的使用示例

說(shuō)明

閱讀此文首先你要對(duì)以下技術(shù)有一定的了解。對(duì)以上技術(shù)還不熟悉的朋友可以先去了解一下。
在我閱讀過無(wú)數(shù)相關(guān)技術(shù)文章之后,我給大家推薦這些技術(shù)學(xué)習(xí)的文章:

dagger2

理論:http://android.jobbole.com/82694/
實(shí)例(網(wǎng)絡(luò)上沒有找到合適的,我自己寫的一篇博文):http://m.itdecent.cn/p/269c3f70ec1e
官方:http://google.github.io/dagger/

mvp:

這個(gè)理論很簡(jiǎn)單,自己百度或者google吧
示例:https://github.com/googlesamples/android-architecture

retrofit2:

官方:http://square.github.io/retrofit/

rxjava:

偏理論:http://gank.io/post/560e15be2dca930e00da1083
偏實(shí)踐:http://blog.chinaunix.net/uid-20771867-id-5187376.html

對(duì)上面的技術(shù)有一定的了解后,我們開始一個(gè)示例:

架構(gòu)搭建

首先我們要一個(gè)示例的方式來(lái)詳細(xì)說(shuō)明整體項(xiàng)目的架構(gòu)與思想

示例功能;

  • 登錄功能
    • 檢查用戶名和密碼是否合法
    • 登錄按鈕如果不合法則不可點(diǎn)擊,合法后登錄按鈕可以點(diǎn)擊
    • 調(diào)用登錄接口進(jìn)行登錄
    • 將用戶名和密碼保存本地
  • 文章列表
    • 從網(wǎng)絡(luò)獲取文章列表并展示
    • 將文章列表保存到數(shù)據(jù)庫(kù)
    • 點(diǎn)擊列表進(jìn)入文章詳情
    • 網(wǎng)絡(luò)獲取圖片
  • 單元測(cè)試
    • 集成測(cè)試
    • 單元測(cè)試

整體架構(gòu)圖

未命名.png
未命名.png

運(yùn)行webserver json

  //安裝json-server
  $ npm install -g json-server
  //進(jìn)入工程目錄
  $ cd AndroidArchitecture/
  //運(yùn)行服務(wù)
  json-server --watch login.json

運(yùn)行后可以通過此地址訪問接口
http://localhost:3000/users
http://localhost:3000/topics

關(guān)于工程

由于工程代碼較多,在這里就不一一將代碼貼出了,詳細(xì)的demo地址見我的github
https://github.com/wlj32011/AndroidArchitecture

下圖為demo目錄接口,查看demo源碼可以參考此結(jié)構(gòu)


AndroidArchitecture.png
AndroidArchitecture.png

關(guān)鍵代碼

  • 網(wǎng)絡(luò)請(qǐng)求返回消息體統(tǒng)一錯(cuò)誤處理

    消息體結(jié)構(gòu)

//登錄
{
  "status_msg" : "登錄成功",
  "status_code" : 200,
  "data" : {
    "username" : "admin",
    "id" : 1,
    "password" : "123456",
    "gender" : "男"
  }
}

模型結(jié)構(gòu)

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對(duì)象變換

public class BaseResponseFunc<T> implements Func1<BaseResponse<T>, Observable<T>> {


    @Override
    public Observable<T> call(BaseResponse<T> tBaseResponse) {
        //遇到非200錯(cuò)誤統(tǒng)一處理,將BaseResponse轉(zhuǎn)換成您想要的對(duì)象
        if (tBaseResponse.getStatus_code() != 200) {
            return Observable.error(new Throwable(tBaseResponse.getStatus_msg()));
        }else{
            return Observable.just(tBaseResponse.getData());
        }
    }
}

自定義訂閱者

/**
 * 錯(cuò)誤統(tǒng)一處理
 *
 * 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, "網(wǎng)絡(luò)中斷,請(qǐng)檢查您的網(wǎng)絡(luò)狀態(tài)", Toast.LENGTH_SHORT).show();
        } else if (e instanceof ConnectException) {
           Toast.makeText(application, "網(wǎng)絡(luò)中斷,請(qǐng)檢查您的網(wǎng)絡(luò)狀態(tài)", 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);
    }
}

簡(jiǎn)單的回調(diào)模型

public interface SimpleCallback<T> {
    void onStart();
    void onNext(T t);
    void onComplete();
}

presenter層調(diào)用

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();
            }
        });
    }
  • 對(duì)外提供ApiManager以及retrofit的封裝
@Module
public class ApiModule {
    @Provides
    @Singleton
    public OkHttpClient provideOkHttpClient() {
        final OkHttpClient.Builder builder = new OkHttpClient.Builder();
        //添加logo日志打印網(wǎng)絡(luò)請(qǐng)求的攔截器
        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);
    }

}
  • 所有的全局共用對(duì)象都可以在AppModule里對(duì)外提供,比如PreferencesManager DatabaseManager等等

更高級(jí)的用法--dagger2 劃分更細(xì)的scope

目前demo示例是將功能模塊直接依賴于整個(gè)APP,其實(shí)我們可以劃分更細(xì)的作用域。使一個(gè)對(duì)象的生命周期存在于多個(gè)功能模塊中。

比如:項(xiàng)目中登錄成功后,獲取文章列表需要用戶信息,獲取文章詳情以及文章下的評(píng)論列表,又需要當(dāng)前文章和用戶的信息。那么我們就可以這樣設(shè)計(jì)我們的工程架構(gòu)如圖:

scope.png
scope.png

后記

由于寫的比較倉(cāng)促,架構(gòu)圖中的紅色字體部分還未實(shí)現(xiàn),更細(xì)的scope還沒有在demo中體現(xiàn)出來(lái),如果您對(duì)此感興趣,請(qǐng)關(guān)注,后續(xù)會(huì)陸續(xù)更新。

由于表達(dá)能力有限,可能有些地方解釋的不是很清楚,歡迎在下方評(píng)論,一起討論一起進(jìn)步~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,355評(píng)論 25 708
  • 出了人大,甚至僅僅出了新聞系,大概也很難再遇到這樣的師長(zhǎng),關(guān)心你的興趣,鼓勵(lì)你去做所謂的“無(wú)用之學(xué)”,鼓勵(lì)你做一個(gè)...
    請(qǐng)叫我黃姑娘閱讀 251評(píng)論 0 0
  • 最近在寫一個(gè)請(qǐng)假功能,要求選擇日期,然后我就用了pickView來(lái)做,在別的版本手機(jī)上選擇是沒問題的,但是到了版本...
    沒毛毛的大腿閱讀 318評(píng)論 0 0
  • 曾經(jīng)夢(mèng)想著做一名作家,因?yàn)樽骷沂切撵`的記錄著,既探索者自己,也可以幫助他人,喜歡文字輕輕的流過我的血液,享受著與中...
    倔強(qiáng)兔子dream2016閱讀 346評(píng)論 0 0
  • 今年的日子,有一大半都泡在健身房。 歙縣的汪教練,天津的酒店頂樓,民大的地下室,cumbaya的快速路旁,都留下了...
    胡安是娟娟閱讀 254評(píng)論 0 0

友情鏈接更多精彩內(nèi)容