手把手寫一個Clean(+mvp+rxjava)架構的Demo

天星技術團隊發表於2019-03-02

作者:點先生 時間:2018.12.26

前言

前段時間在天星群中有朋友說到了clean架構。剛好在最近的專案裡面我在搭建框架的時候用到了clean,所以在這裡就把搭建過程在這裡描述一下。Demo在文章末尾。本文側重於clean的搭建,mvp+rxjava的部分,不做介紹。
(咦?我聽到有人在問什麼是天星群。既然你誠心誠意的發問了,那我就給你們透露一下。天星是一群Android開發者閒的沒事幹搞的一個部落格團隊,這是團隊部落格地址:點這裡,這個是粉絲qq群:557247785。歡迎妹子和女裝大佬們!)

CleanArchitecture

mvp和rxjava基本是現在開發標配,有很多寫的比較好的文章。對mvp和rxjava比較熟的同學可以繼續往下看了。還不熟的同學可以看看MVPRxjava1Rxjava2

Clean是一種架構的思想,跟MVP一樣,解耦就完事兒了。(那一群解耦狂魔,你根本不知道他們究竟想怎樣!)關於clean框架的解析很多,我不再贅述,畢竟本文是手把手寫一個Demo。

Clean思想由Uncle Bob提出的,英文不錯的看這個 The Clean Code Blog
Google爸爸寫的Demo可以作為參考todo‑mvp‑clean
android10的文章圖解多,比較容易理解,推薦!Android-CleanArchitecture
中文版可以看看來自谷歌清潔工Clean架構探討,我覺得講的很好。

按慣例這裡該有一張表情包,但是我沒有找到可以皮的理由,所以,算了。

手把手寫mvp+rxjava+clean

現在來模擬一個登陸功能。mvp是clean架構的基礎,我們先把mvp+rxjava的框架給弄好。我的專案結構是這樣的。

專案結構

分為了data,domain,presentation 三層。分別對應clean的三層。 data層和presentation層大家應該都比較熟悉,就是一些mvp的東西。 那這個domain layout是幹嘛的?

業務邏輯,use case實現的地方 以前在mvp架構的時候,我們會說。我要是不知道這個app能幹嘛,看一眼presenter的介面就知道了。那在clean裡面就可以說。我要是不知道這app能幹嘛,看一眼domain層就知道了。
usecase更加簡化了presenter裡面的程式碼 以前presenter裡面會呼叫data層的東西,現在presenter只管負責的usecase。程式碼量大大的減少。
usecase純java程式碼,不含androd依賴

那usecase是幹嘛的? 看看google官方todo‑mvp‑clean裡面Usecase在幹嘛,又是如何呼叫的。

Google clean demo核心解析

UseCase

手把手寫一個Clean(+mvp+rxjava)架構的Demo

  1. 抽象類
  2. 傳進來了請求引數Q,響應引數P
  3. 有一個回撥介面UseCaseCallback
  4. set幾個屬性的方法。
  5. run(): 帶著Q執行usecase

UseCase實現類

手把手寫一個Clean(+mvp+rxjava)架構的Demo

手把手寫一個Clean(+mvp+rxjava)架構的Demo

  1. 建構函式傳入了repository
  2. P,Q兩個靜態內部類
  3. executeUseCase():通過傳入的repository執行資料請求的操作,並執行回撥函式。

UseCaseHandler

手把手寫一個Clean(+mvp+rxjava)架構的Demo

  1. 統一管理Usecase的execute方法。
  2. 通過execute方法把需要處理的UseCase<T, R>、請求引數T、回撥介面UseCase.UseCaseCallback進行繫結,方便請求與回撥。

presenter裡的使用

手把手寫一個Clean(+mvp+rxjava)架構的Demo

手把手寫一個Clean(+mvp+rxjava)架構的Demo

使用哪個方法的時候,就呼叫傳入哪個usecase,和請求需要的引數,和相應的回撥介面。

我們繼續手把手

UseCaseHandler主要作用就是統一管理,回撥直觀。 當我們把rxjava2加入進來之後,UseCaseHandler就沒有存在的意義了。 去掉handler,不需要繫結這雜七雜八的東西之後,usecase可以簡化了。所以我的BaseUseCase是這樣的

MyUseCase

public abstract class BaseUseCase<P, Q> {
    private final Scheduler observerThread;
    private final Scheduler subcriberThread;
    private final CompositeDisposable disposables;

    public BaseUseCase(Scheduler observerThread, Scheduler subcriberThread) {
        this.observerThread = observerThread;
        this.subcriberThread = subcriberThread;
        this.disposables = new CompositeDisposable();
    }

    protected abstract Observable<Q> buildUseCaseObservable(P request);

    public void execute(DisposableObserver<Q> observer, P request) {
        Preconditions.checkNotNull(observer);
        final Observable<Q> observable = this.buildUseCaseObservable(request)
                .subscribeOn(observerThread)
                .observeOn(subcriberThread);
        addDisposable(observable.subscribeWith(observer));
    }

    public void dispose() {
        if (!disposables.isDisposed()) {
            disposables.dispose();
        }
    }

    public void addDisposable(Disposable disposable) {
        Preconditions.checkNotNull(disposable);
        Preconditions.checkNotNull(disposables);
        disposables.add(disposable);
    }
}
複製程式碼

跟google的demo做了一樣的事,不過加了CompositeDisposable統一管理而已。

MyUseCase實現類

public class LoginUseCase extends BaseUseCase<LoginUseCase.RequestValue, User> {

    private final Repository repository;

    public LoginUseCase(Scheduler observerThread, Scheduler subcriberThread, Repository repository) {
        super(observerThread, subcriberThread);
        this.repository = repository;
    }

    @Override
    protected Observable<User> buildUseCaseObservable(RequestValue request) {
        return repository.getUser(request.account, request.password);
    }

    public static final class RequestValue {
        final String account;
        final String password;

        public RequestValue(String account, String password) {
            this.account = Preconditions.checkNotNull(account);
            this.password = Preconditions.checkNotNull(password);
        }
    }
}
複製程式碼

跟google的demo差不多基本一致,把ResponseValue改成了返回的實體類。

MyPresenter裡的使用

 @Override
    public void login(String account, String password) {
        loginUseCase.execute(new LoginObserver(), new LoginUseCase.RequestValue(account, password));
    }

    private final class LoginObserver extends DisposableObserver<User> {
        @Override
        public void onNext(User user) {  loginUseCase.saveUser(user); }

        @Override
        public void onError(Throwable e) {  }

        @Override
        public void onComplete() {  }
    }
複製程式碼

其他

    /*ButterKnife*/
    implementation 'com.jakewharton:butterknife:8.8.1'
    annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'
    /*GreenDao*/
    implementation 'org.greenrobot:greendao:3.2.2'
    /*Retrofit*/
    implementation 'com.squareup.retrofit2:retrofit:2.0.2'
    implementation 'com.squareup.okhttp3:okhttp:3.1.2'
    implementation 'com.squareup.retrofit2:converter-gson:2.3.0'
    implementation 'com.squareup.retrofit2:adapter-rxjava2:2.3.0'
    implementation 'com.squareup.okhttp3:okhttp-urlconnection:3.5.0'
    /*Rxjava2*/
    implementation 'io.reactivex.rxjava2:rxjava:2.1.3'
    implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'
複製程式碼

全部原始碼在這裡:github.com/GuitarDian/…

相關文章