基于MVP的原子性架構(gòu)

先說下背景窄潭,市面上大部分公司在用的MVP MVVM等,google基于 MVP MVVM做了很多擴展架構(gòu)纫骑,在每個項目實際使用中都會有不同的擴展,接下來我會介紹一下基于這些思想的理解并設(shè)計的我們目前項目中使用的架構(gòu)

項目地址:android-mvp-architecture


項目架構(gòu)圖

實現(xiàn)思路

  • 架構(gòu)主題服從mvp的思想,在model層做了一些更加細化的劃分,model層由各種原子化的usecase組合而成九孩,通過組合的方式使用先馆,以適應(yīng)復(fù)雜的ui需求,在個別耦合性比較強的業(yè)務(wù)之中躺彬,甚至可以考慮將這些usecase進行封裝煤墙,達到模塊化的目的梅惯。
  • 整體架構(gòu)按照mvp和分層的思想設(shè)計,四層結(jié)構(gòu)仿野,自頂向下分別是:view視圖層铣减、presenter視圖控制層、usecase業(yè)務(wù)層和data repository數(shù)據(jù)層脚作。下層不依賴上層葫哗,以接口的方式為上層提供服務(wù)。

分層介紹

  • View視圖層

負責(zé)用戶圖形界面的展示球涛,根據(jù)下層的數(shù)據(jù)劣针,驅(qū)動界面的顯示,比如按鈕的狀態(tài)亿扁,控件的顯示和隱藏等等捺典。
View是顯示數(shù)據(jù)(model)并且將用戶指令(events)傳送到presenter以便作用于那些數(shù)據(jù)的一個接口,通常將Activity或者Fragment作為View層。

  • Presenter 視圖控制層

負責(zé)將下層拿到的數(shù)據(jù)結(jié)構(gòu)轉(zhuǎn)化為ui上面要使用的數(shù)據(jù)从祝,同時根據(jù)數(shù)據(jù)去控制上層ui界面的顯示襟己。
Presenter 是連接View層與Model層的橋梁并對業(yè)務(wù)邏輯進行處理,起到一個承上啟下的作用牍陌。負責(zé)從Model層獲得所需要的數(shù)據(jù)擎浴,進行一些適當?shù)奶幚砗蠼挥蒝iew層進行顯示。通過Presenter能很好的將View與Model進行隔離呐赡,使得View和Model之間不存在耦合退客,同時也將業(yè)務(wù)邏輯從View中抽離骏融,以及測試用例的編寫链嘀。

  • Usecase 業(yè)務(wù)層

負責(zé)從下層讀取數(shù)據(jù),根據(jù)不同的業(yè)務(wù)選擇相關(guān)策略档玻,緩存讀取以及遠端讀取怀泊。在原則上
按照最小粒度設(shè)計,以便上層能夠更好的復(fù)用和組合多個業(yè)務(wù)误趴。
原子性就體現(xiàn)到了這一層上霹琼,不同的業(yè)務(wù)Task可以隨意組合,高復(fù)用凉当、擴展特性枣申,可以在presenter隨意組合使用

  • Data repository 數(shù)據(jù)層

負責(zé)對接網(wǎng)絡(luò)和數(shù)據(jù)庫的相關(guān)邏輯,從遠端取數(shù)據(jù)以及更新數(shù)據(jù)庫緩存看杭。

代碼簡析

一忠藤、 首先看下幾個Base類
  • BaseActivity
public class BaseActivity<P extends IRxPresenter> extends AppCompatActivity {
private P mPresenter;

 @Override
     public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mPresenter = createPresenter();
     }
protected P createPresenter() {
         return null;
   }
protected P getPresenter() {
    return mPresenter;
 }

@Override
public void onDestroy() {
  super.onDestroy();
  if (mPresenter != null) { 
      mPresenter.destroy();
      }
   }
 }

通過使用泛型,傳入一個IRxPresenter 接口楼雹,在使用view的時候直接指定對應(yīng)的Presenter進行強綁定模孩,一個View只會對應(yīng)一個Presenter的原則尖阔,createPresenter()返回對應(yīng)的presenter,在整個view里面使用的是時候通過getPresenter()來獲取對象進行調(diào)度榨咐,在Activity生命周期走到onDestroy()時候會調(diào)用 mPresenter.destroy()介却,這時候可以在presenter里面的destroy()去cancel Task,這樣的好處就是退出activity時候會自動清理掉正在執(zhí)行還沒有回調(diào)的任務(wù)块茁。

  • BasePresenter:
public class BasePresenter<V extends IRxView> implements IRxPresenter<V> {
private Map<String, Subscription> mSubscriptions = new HashMap<>();
private V mView;

public BasePresenter(V view) {
    mView = view;
}

@Override
public V getView() {
    return mView;
}

@Override
public void addSubscription(Subscription subscription) {
    addSubscription(String.valueOf(subscription.hashCode()), subscription);
}

@Override
public void addSubscription(String tag, Subscription subscription) {
    mSubscriptions.put(tag, subscription);
}

@Override
public void cancelSubscription(String tag) {
    if (mSubscriptions.containsKey(tag)) {
        mSubscriptions.get(tag).unsubscribe();
    }
}

@Override
public void cancelAll() {
    for (Subscription subscription : mSubscriptions.values()) {
        subscription.unsubscribe();
    }
}

@Override
public void destroy() {
    cancelAll();
    mSubscriptions = null;
}

}
在創(chuàng)建presenter的時候會通過泛型指定一個對應(yīng)的IRxView齿坷,這就是上面說的View和Presenter的強綁定,其他幾個方法就是簡單的封裝数焊,方便presenter進行add和cancel的操作胃夏。

  • UseCase
public abstract class UseCase<Q extends UseCase.RequestValues, P extends IDataProtocol> {

private boolean mStopAllWithFuture;

private Q mRequestValues;

private UseCaseCallback<P> mUseCaseCallback;

private CompositeSubscription mCompositeSubscription = new CompositeSubscription();

public void setRequestValues(Q requestValues) {
    mRequestValues = requestValues;
}

public Q getRequestValues() {
    return mRequestValues;
}

public UseCaseCallback<P> getUseCaseCallback() {
    return mUseCaseCallback;
}

public void setUseCaseCallback(UseCaseCallback<P> useCaseCallback) {
    mUseCaseCallback = useCaseCallback;
}

void run() {
    Observable<P> task = buildUseCase(mRequestValues);
    if (task == null) {
        return;
    }
    mCompositeSubscription.add(task.compose(new ApplySchedulers<P>())
                   .subscribe(new EntitySubscriber<>(mUseCaseCallback)));
}

protected abstract Observable<P> buildUseCase(Q requestValues);

public final void execute(Q values, UseCase.UseCaseCallback<P> callback) {
    if (mStopAllWithFuture) {
        return;
    }
    setRequestValues(values);
    setUseCaseCallback(callback);
    run();
}

/**
 * Unsubscribes from current {@link Subscription}.
 */
public void cancel() {
    cancel(true);
}


/**
 * Unsubscribes from current {@link Subscription}.
 *
 * @param stopAllWithFuture stop the future tasks.
 */
public void cancel(boolean stopAllWithFuture) {
    mStopAllWithFuture = stopAllWithFuture;
    mCompositeSubscription.clear();
}

/**
 * empty request.
 */
public static class EmptyRequestValues extends BaseRequestValues {

}

/**
 * base request.
 */
public abstract static class BaseRequestValues implements RequestValues {

}

/**
 * Data passed to a request.
 */
public interface RequestValues {

}

public interface UseCaseCallback<R> {

    void onSuccess(R response);

    void onError(int code, String error);
}

}

UseCase這一層的核心類,主要功能是在創(chuàng)建Task的時候?qū)⒄埱髮嶓w和返回實體直接通過泛型定義好昌跌,當Task在執(zhí)行execute(Q values, UseCase.UseCaseCallback<P> callback) 方法時就自動進行觀察者和訂閱者進行綁定并傳入回調(diào)仰禀。

  • RepositoryProvider
public class RepositoryProvider {
public static BankCardRepository getTasksRepository() {
    return BankCardRepository.getInstance(BankCardRemoteDataSource.getInstance(),
                   BankCardLocalDataSource.getInstance());
    }
}

RepositoryProvider這里可以將usecase進行業(yè)務(wù)層的劃分,每一個單例里面包含了這個業(yè)務(wù)對應(yīng)的接口蚕愤,可以看到
BankCardRepository.getInstance(BankCardRemoteDataSource.getInstance(),
BankCardLocalDataSource.getInstance()初始化的時候需要傳入local和reomte兩個實例

public interface BankCardDataSource {

Observable<BaseListEntity<BankCardEntity>> addBankCard(BankCardEntity task);

Observable<BaseListEntity<BankCardEntity>> deleteBankCard(String taskId);

Observable<BaseListEntity<BankCardEntity>> getBankCardList();

Observable<BaseListEntity<BankCardEntity>> chageBankCardStatus(String taskId);

Observable<BaseListEntity<BankCardEntity>> delteAllBankCard();

Observable<BankCardEntity> getBankCard(String taskId);

}

可以看到BankCardLocalDataSource和BankCardRemoteDataSource分別實現(xiàn)了BankCardDataSource這個接口

public class BankCardRepository implements BankCardDataSource {
private static BankCardRepository INSTANCE;

private BankCardDataSource mRemoteDataSource;
private BankCardDataSource mLocalDataSource;

public BankCardRepository(BankCardDataSource remoteDataSource, BankCardDataSource localDataSource) {
    this.mRemoteDataSource = remoteDataSource;
    this.mLocalDataSource = localDataSource;
}

public static BankCardRepository getInstance(BankCardDataSource remoteDataSource, BankCardDataSource localDataSource) {
    if (INSTANCE == null) {
        BankCardRepository source = new BankCardRepository(remoteDataSource, localDataSource);
        INSTANCE = source;
    }
    return INSTANCE;
}

@Override
public Observable<BaseListEntity<BankCardEntity>> addBankCard(BankCardEntity task) {
    mRemoteDataSource.addBankCard(task).doOnNext(new Action1<BaseListEntity<BankCardEntity>>() {
        @Override
        public void call(BaseListEntity<BankCardEntity> taskEntityBaseListEntity) {
            //TODO: save data to local
            for (BankCardEntity entity : taskEntityBaseListEntity.list()) {
                mLocalDataSource.addBankCard(entity);
            }
        }
    });
    return mLocalDataSource.addBankCard(task);
}

@Override
public Observable<BaseListEntity<BankCardEntity>> deleteBankCard(String taskId) {

    mRemoteDataSource.deleteBankCard(taskId).doOnNext(new Action1<BaseListEntity<BankCardEntity>>() {
        @Override
        public void call(BaseListEntity<BankCardEntity> taskEntityBaseListEntity) {
            //TODO: save data to local
            for (BankCardEntity entity : taskEntityBaseListEntity.list()) {
                mLocalDataSource.addBankCard(entity);
            }
        }
    });
    return mLocalDataSource.deleteBankCard(taskId);
}

@Override
public Observable<BaseListEntity<BankCardEntity>> getBankCardList() {
    mRemoteDataSource.getBankCardList().doOnNext(new Action1<BaseListEntity<BankCardEntity>>() {
        @Override
        public void call(BaseListEntity<BankCardEntity> taskEntityBaseListEntity) {
            //TODO: save data to local
            for (BankCardEntity entity : taskEntityBaseListEntity.list()) {
                mLocalDataSource.addBankCard(entity);
            }
        }
    });
    return mLocalDataSource.getBankCardList();
}

@Override
public Observable<BankCardEntity> getBankCard(String cardId) {
    Observable<BankCardEntity> remote = mRemoteDataSource.getBankCard(cardId).doOnNext(new Action1<BankCardEntity>() {
        @Override
        public void call(BankCardEntity entity) {
            mLocalDataSource.addBankCard(entity);
        }
    });
    Observable<BankCardEntity> local = mLocalDataSource.getBankCard(cardId);
    return Observable.concat(remote, local).first();
}

@Override
public Observable<BaseListEntity<BankCardEntity>> delteAllBankCard() {
           mRemoteDataSource.delteAllBankCard();
    return mLocalDataSource.delteAllBankCard();
}

@Override
public Observable<BaseListEntity<BankCardEntity>> chageBankCardStatus(String cardId) {
          mRemoteDataSource.chageBankCardStatus(cardId);
    return mLocalDataSource.chageBankCardStatus(cardId);
}

}

這樣就很清晰了答恶,最終的調(diào)用者是在BankCardRepository這個類里面進行處理的,這樣一來我們就很容易擴展新的數(shù)據(jù)源(獲取數(shù)據(jù)的方式)萍诱,整個架構(gòu)涉及到的核心類基本介紹完了悬嗓。
四層架構(gòu)里面每一次都是面向接口編程,這為以后的擴展帶來了極大的好處裕坊。

總結(jié)

  • 無論是MVP還是MVVM都只是形包竹,不要去被其外表所固定思維,真正用到具體項目要通過自己對整體的理解去擴展屬于自己的架構(gòu)籍凝,架構(gòu)都是需要根據(jù)業(yè)務(wù)形態(tài)去演進周瞎,目前這種架構(gòu)應(yīng)用在我們線上的項目里面。
  • 為了更好的適應(yīng)業(yè)務(wù)的發(fā)展饵蒂,在之后的計劃之中声诸,app的設(shè)計會慢慢傾向于組件化的開發(fā)模式,各個模塊之間進行解耦退盯,使得代碼架構(gòu)更加清晰彼乌,降低項目的維護難度,也便于業(yè)務(wù)的擴展渊迁。

點贊加關(guān)注是給我最大的鼓勵!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末慰照,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子琉朽,更是在濱河造成了極大的恐慌毒租,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,602評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件漓骚,死亡現(xiàn)場離奇詭異蝌衔,居然都是意外死亡榛泛,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評論 2 382
  • 文/潘曉璐 我一進店門噩斟,熙熙樓的掌柜王于貴愁眉苦臉地迎上來曹锨,“玉大人,你說我怎么就攤上這事剃允∨婕颍” “怎么了?”我有些...
    開封第一講書人閱讀 152,878評論 0 344
  • 文/不壞的土叔 我叫張陵斥废,是天一觀的道長椒楣。 經(jīng)常有香客問我,道長牡肉,這世上最難降的妖魔是什么捧灰? 我笑而不...
    開封第一講書人閱讀 55,306評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮统锤,結(jié)果婚禮上毛俏,老公的妹妹穿的比我還像新娘。我一直安慰自己饲窿,他們只是感情好煌寇,可當我...
    茶點故事閱讀 64,330評論 5 373
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著逾雄,像睡著了一般阀溶。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上鸦泳,一...
    開封第一講書人閱讀 49,071評論 1 285
  • 那天银锻,我揣著相機與錄音,去河邊找鬼辽故。 笑死徒仓,一個胖子當著我的面吹牛腐碱,可吹牛的內(nèi)容都是我干的誊垢。 我是一名探鬼主播,決...
    沈念sama閱讀 38,382評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼症见,長吁一口氣:“原來是場噩夢啊……” “哼喂走!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起谋作,我...
    開封第一講書人閱讀 37,006評論 0 259
  • 序言:老撾萬榮一對情侶失蹤芋肠,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后遵蚜,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體帖池,經(jīng)...
    沈念sama閱讀 43,512評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡奈惑,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,965評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了睡汹。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片肴甸。...
    茶點故事閱讀 38,094評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖囚巴,靈堂內(nèi)的尸體忽然破棺而出原在,到底是詐尸還是另有隱情,我是刑警寧澤彤叉,帶...
    沈念sama閱讀 33,732評論 4 323
  • 正文 年R本政府宣布庶柿,位于F島的核電站,受9級特大地震影響秽浇,放射性物質(zhì)發(fā)生泄漏浮庐。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,283評論 3 307
  • 文/蒙蒙 一柬焕、第九天 我趴在偏房一處隱蔽的房頂上張望兔辅。 院中可真熱鬧,春花似錦击喂、人聲如沸维苔。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,286評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽介时。三九已至,卻和暖如春凌彬,著一層夾襖步出監(jiān)牢的瞬間沸柔,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,512評論 1 262
  • 我被黑心中介騙來泰國打工铲敛, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留褐澎,地道東北人。 一個月前我還...
    沈念sama閱讀 45,536評論 2 354
  • 正文 我出身青樓伐蒋,卻偏偏與公主長得像工三,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子先鱼,可洞房花燭夜當晚...
    茶點故事閱讀 42,828評論 2 345

推薦閱讀更多精彩內(nèi)容