MVP+Retrofit+RxJava2 封裝

使用架構(gòu)的目的是為使程序模塊化,做到模塊內(nèi)的高聚合和模塊間的低耦合战秋,架構(gòu)和模式并不是讓你的代碼減少,可能往往會增大讨韭,但是它幫你在邏輯上更簡潔了脂信,很好的定義了單一性原則,提供了更好的擴展性透硝,方便定位問題以及后續(xù)開發(fā)中需求變跟時不至于滿篇的去改一大堆東西 狰闪。(一下圖片均來自網(wǎng)絡(luò),侵刪)

MVC簡述

首先熟悉下在Android開發(fā)中濒生,傳統(tǒng)的MVC模式(view埋泵、model、controller)。


mvc.jpg

Activity丽声、Fragment礁蔗、布局xml就相當(dāng)于View層。業(yè)務(wù)模型雁社,建立的數(shù)據(jù)結(jié)構(gòu)浴井,以及網(wǎng)絡(luò)請求、數(shù)據(jù)庫處理霉撵、I/O操作相當(dāng)于model層磺浙。至于controller層就有點模糊了,我們大家都將Activity喊巍、Fragment視作controller層屠缭,也就是把view和controller都揉和在Activity和Fragment中箍鼓,視圖顯示和控制邏輯錯綜復(fù)雜崭参。

這樣經(jīng)過幾次需求變跟、代碼迭代后一個Activity動輒上千行代碼款咖,最主要的修改某個頁面時控制邏輯也得跟著變動何暮,當(dāng)然了可能會出現(xiàn)的bug以及后續(xù)開發(fā)人員閱讀代碼的難度也就增加了很多,這就違背了架構(gòu)的低耦合铐殃。

MVP簡述

這樣更優(yōu)的架構(gòu)模式MVP也就得到更多開發(fā)者的青睞海洼。

MVP中每個單詞具體代表的含義是什么?

M層 和MVC中的model一樣富腊,都是模型層坏逢,負(fù)責(zé)處理存儲、檢索赘被、操作數(shù)據(jù)是整,包括網(wǎng)絡(luò)請求、數(shù)據(jù)庫處理以及IO操作民假。

V層 和MVC的view一樣浮入,都是視圖層,負(fù)責(zé)繪制UI元素以及與用戶的交互羊异,對應(yīng)的是activity事秀、Fragment、adapter野舶、xml

P層 (Presenter)是真?zhèn)€MVP的控制中心易迹,作為View和Model的中間樞紐,處理View和Model間的交互和業(yè)務(wù)邏輯

這樣通過Presenter將View與Model進(jìn)行隔離平道,使得View和Model之間不存在耦合睹欲,同時也將業(yè)務(wù)邏輯從View中抽離 ,視圖層專注View的展示和用戶的交互巢掺。

MVP.jpg

這樣做的優(yōu)勢也就是顯而易見了

  • Model層與View層完全分離句伶,修改view層不會影響Model層劲蜻,降低了耦合

  • 復(fù)雜的邏輯處理放在presenter進(jìn)行處理,減少了Activity的臃腫

  • 可以將一個Presenter用于多個視圖考余,而不需要改變Presenter的邏輯先嬉, 提高了代碼復(fù)用 ,代碼靈活性

  • 模塊職責(zé)劃分明顯 楚堤, 具有良好的可擴展性

  • Presenter層與View層的交互是通過接口來進(jìn)行的疫蔓,便于單元測試。

當(dāng)然MVP也是有缺點的身冬。

視圖和Presenter的交互會過于頻繁衅胀,使得他們的聯(lián)系過于緊密。也就是說酥筝,一旦視圖變更了滚躯,presenter也要變更。

MVP 存在的問題

1嘿歌、接口過多問題

使用MVP模式去構(gòu)建項目掸掏,會造成類文件和接口文件的過多,進(jìn)而增大包的體積宙帝。

解決方式:寫一個Contract接口丧凤,然后把與MVP相關(guān)接口全部列入到里面去。

2步脓、內(nèi)存泄漏

當(dāng)用戶關(guān)閉了View層愿待,但這時Model層如果仍然在進(jìn)行耗時操作,因為Presenter層也持有View層的引用靴患,所以造成垃圾回收器無法對View層進(jìn)行回收仍侥,這樣一來,就造成了內(nèi)存泄漏蚁廓。

解決方式:可以重寫onDestroy()方法访圃,在View銷毀時強制回收掉Presenter;或是采用弱引用的方式

MVP例子

基類

從上面說明中相嵌,我們知道MVP有三次View層腿时、Presenter層、Model層構(gòu)成饭宾,在構(gòu)建MVP架構(gòu)的時候需考慮到代碼的復(fù)用性已經(jīng)以后的可擴展性批糟,這樣我們先設(shè)計基類。

基類BaseActivity看铆、基類BaseFragment徽鼎,所有activity和fragment的基類,

基類BasePresenter,所有Presenter的基類

接口IBaseView否淤, 說明了每一個View基本需要的一些操作

這樣主要的一些基類就設(shè)計好了

mvp_base.png
BaseView

定義每一個View基本需要的一些操作

public interface IBaseView {

    void showLoading();

    void hideLoading();

    void showError(String msg);
}
BasePresenter

因為Presenter將View與Model進(jìn)行隔離悄但,也將業(yè)務(wù)邏輯從View中抽離 。

1石抡、Presenter處理的數(shù)據(jù)需要回顯到View上時檐嚣,就需要持有View的引用,也就是Presenter需要 綁定 View 和解綁 View (onAttachView(V view) onDetachView())啰扛。所有的View都是IBaseView的子類嚎京, 所以必須要傳入一個泛型的 View 層接口 。

2隐解、View展現(xiàn)的數(shù)據(jù)需要從Model層獲取鞍帝,而Presenter是他們的橋梁,所有Presenter也需要持有Model層引用煞茫。

public class BasePresenter<V extends IBaseView> {
    //用于及時取消訂閱帕涌,以防止內(nèi)存泄漏
    private CompositeDisposable mDisposable;

    protected ApiServer mApiServer = ApiServer.getInstance();

    private SoftReference <V> mReferenceView;

    /**
     * activity 創(chuàng)建的時關(guān)聯(lián)presenter
     * @param view
     */
    public void onAttachView(V view) {
        this.mReferenceView = new SoftReference<>(view);
    }

    /**
     * activity 關(guān)閉時解除綁定,防止內(nèi)存泄漏
     */
    public void onDetachView() {
        this.mReferenceView.clear();
        this.mReferenceView = null;
        removeDisposable();
    }

    public V getMvpView() {
        return this.mReferenceView.get();
    }

    protected void addDisposable(Disposable disposable) {
        if (mDisposable == null) {
            mDisposable = new CompositeDisposable();
        }
        if (!mDisposable.isDisposed()) {
            mDisposable.add(disposable);
        }
    }

    protected void removeDisposable() {
        if (mDisposable != null) {
            mDisposable.dispose();
            mDisposable = null;
        }
    }
}

在BasePresenter中用到了SoftReference,這樣就更加嚴(yán)謹(jǐn)溜嗜,防止內(nèi)存泄漏宵膨。

BaseActivity
BaseActivity
public abstract class BaseActivity extends AppCompatActivity {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(initLayoutID());
        initViews();
        initViewListener();
    }

    /**
     * 初始化布局文件
     * @return  返回布局文件的xml
     */
    protected abstract int initLayoutID();

    /**
     * 初始化View控件
     */
    protected abstract void initViews();

    /**
     * 初始化View的事件
     */
    protected abstract void initViewListener();

上面的BaseActivity就是我們在MVC里面用到的基類,但在MVP架構(gòu)中炸宵,需要實現(xiàn)IBaseView,同時要初始化Presenter以及綁定Presenter谷扣。

public abstract class BaseActivity<P extends BasePresenter> extends AppCompatActivity implements IBaseView {
    private MaterialDialog mDialog;
    protected P mPresenter;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(initLayoutID());
        initPresenter();
        initViews();
        initViewListener();
    }

    /**
     * 初始化Presenter
     */
    protected void initPresenter() {
        mPresenter = createrPresenter();
        if (mPresenter != null) {
            mPresenter.onAttachView(this);
        }
    }

    protected abstract P createrPresenter();

    /**
     * 初始化布局文件
     *
     * @return 返回布局文件的xml
     */
    protected abstract int initLayoutID();

    /**
     * 初始化View控件
     */
    protected abstract void initViews();

    /**
     * 初始化View的事件
     */
    protected abstract void initViewListener();

    @Override
    public void showLoading(String msg) {
        if (mDialog != null) {
            mDialog = mDialog.getBuilder().title(msg).build();
            mDialog.show();
        } else {
            MaterialDialog.Builder builder = MaterialDialogUtils.showIndeterminateProgressDialog(this, msg, true);
            mDialog = builder.show();
        }
    }

    @Override
    public void hideLoading() {
        if (mDialog != null && mDialog.isShowing()) {
            mDialog.dismiss();
        }
    }

    @Override
    public void showError(String msg) {
        Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mPresenter != null) {
            mPresenter.onDetachView();
        }
    }

這樣簡單的基類代碼就完成了土全。

具體的用法

那到底具體怎么用呢?定義一個簡單的按鈕会涎,然后點擊請求網(wǎng)絡(luò)數(shù)據(jù)裹匙,然后顯示。

定義一個Activity 繼承BaseActivity末秃,實現(xiàn)IMainView接口概页,對應(yīng)的MainPresenter里面處理請求網(wǎng)絡(luò),結(jié)果回顯給頁面练慕。

MainActivity:

public class MainActivity extends BaseActivity<MainPresenter> implements IMainView {

    TextView textView;
    Button btnSuccess;

    @Override
    protected MainPresenter createrPresenter() {
        return new MainPresenter();
    }

    @Override
    protected int initLayoutID() {
        return R.layout.activity_main;
    }

    @Override
    protected void initViews() {
        textView = findViewById(R.id.textView);
        btnSuccess = findViewById(R.id.btn_success);
    }

    @Override
    protected void initViewListener() {
        btnSuccess.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mPresenter.requestData();
            }
        });
    }

    @Override
    public void showData(List<Banner> banners) {
        textView.setText(new Gson().toJson(banners));
    }
}

IMainView:

public interface IMainView extends IBaseView {
    void showData(List<Banner> banners);
}

MainPresenter:

public class MainPresenter extends BasePresenter<IMainView> {

    void requestData() {
        if (getView() == null) {
            return;
        }
        addDisposable(mApiServer.toSubscribe(mApiServer.getApi().getBanner(),
                new BaseObserver<List<Banner>>(getView(), "加載中...") {
                    @Override
                    protected void onSuccess(List<Banner> banners) {
                        if (getView() != null) {
                            getView().showData(banners);
                        }
                    }

                    @Override
                    protected void onError(ApiException ex) {
                        if (getView() != null) {
                            getView().showError(ex.getMessage());
                        }
                    }

                }));
    }
}

這樣顯示整個例子就完成了惰匙,效果如下:


Screenshot_20200304_143119_com.wenbing.mvpdemo.jpg

項目地址

歡迎轉(zhuǎn)載,但是請注明出處

示例源碼 個人博客

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末铃将,一起剝皮案震驚了整個濱河市项鬼,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌劲阎,老刑警劉巖绘盟,帶你破解...
    沈念sama閱讀 222,590評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡龄毡,警方通過查閱死者的電腦和手機吠卷,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,157評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事肥矢∨璧ⅲ” “怎么了?”我有些...
    開封第一講書人閱讀 169,301評論 0 362
  • 文/不壞的土叔 我叫張陵飘痛,是天一觀的道長。 經(jīng)常有香客問我,道長程奠,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,078評論 1 300
  • 正文 為了忘掉前任祭钉,我火速辦了婚禮瞄沙,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘慌核。我一直安慰自己距境,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 69,082評論 6 398
  • 文/花漫 我一把揭開白布垮卓。 她就那樣靜靜地躺著垫桂,像睡著了一般。 火紅的嫁衣襯著肌膚如雪粟按。 梳的紋絲不亂的頭發(fā)上诬滩,一...
    開封第一講書人閱讀 52,682評論 1 312
  • 那天,我揣著相機與錄音灭将,去河邊找鬼疼鸟。 笑死,一個胖子當(dāng)著我的面吹牛庙曙,可吹牛的內(nèi)容都是我干的空镜。 我是一名探鬼主播,決...
    沈念sama閱讀 41,155評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼捌朴,長吁一口氣:“原來是場噩夢啊……” “哼吴攒!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起男旗,我...
    開封第一講書人閱讀 40,098評論 0 277
  • 序言:老撾萬榮一對情侶失蹤舶斧,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后察皇,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體茴厉,經(jīng)...
    沈念sama閱讀 46,638評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡泽台,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,701評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了矾缓。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片怀酷。...
    茶點故事閱讀 40,852評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖嗜闻,靈堂內(nèi)的尸體忽然破棺而出蜕依,到底是詐尸還是另有隱情,我是刑警寧澤琉雳,帶...
    沈念sama閱讀 36,520評論 5 351
  • 正文 年R本政府宣布样眠,位于F島的核電站,受9級特大地震影響翠肘,放射性物質(zhì)發(fā)生泄漏檐束。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,181評論 3 335
  • 文/蒙蒙 一束倍、第九天 我趴在偏房一處隱蔽的房頂上張望被丧。 院中可真熱鬧,春花似錦绪妹、人聲如沸甥桂。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,674評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽黄选。三九已至,卻和暖如春廊移,著一層夾襖步出監(jiān)牢的瞬間糕簿,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,788評論 1 274
  • 我被黑心中介騙來泰國打工狡孔, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人蜂嗽。 一個月前我還...
    沈念sama閱讀 49,279評論 3 379
  • 正文 我出身青樓苗膝,卻偏偏與公主長得像,于是被迫代替她去往敵國和親植旧。 傳聞我的和親對象是個殘疾皇子辱揭,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,851評論 2 361

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