再說Android中MVP模式設(shè)計(jì)-單頁面實(shí)現(xiàn)多種業(yè)務(wù)山孔,有多個(gè)presenter懂讯。

前言:Android中使用MVP開發(fā)已經(jīng)很普遍了,網(wǎng)上也已經(jīng)有好多種MVP模式的文章和設(shè)計(jì)台颠。但是一直沒能達(dá)到想要的效果褐望。之前一直采用的 一個(gè)頁面對(duì)應(yīng)一個(gè)presenter(class)和一個(gè)view(interface)的模式,但是這種模式遇到頁面業(yè)務(wù)邏輯過多的時(shí)候串前,p層和v層的代碼量會(huì)特別大√崩铮現(xiàn)在說一下根據(jù)這個(gè)模式改進(jìn)后,達(dá)到單個(gè)頁面實(shí)現(xiàn)多個(gè)presenter荡碾。方便多種業(yè)務(wù)解耦谨读,讓單個(gè)文件代碼量不至于過大的方法。

防止看代碼太費(fèi)勁坛吁。
直接放鏈接:https://github.com/hanswook/mvpdemo

一劳殖、按照google的sample中contract(契約類)模式會(huì)存在一些內(nèi)存泄漏問題(長(zhǎng)時(shí)間的操作在頁面銷毀后,p層持有v層導(dǎo)致頁面無法被回收)拨脉。變種產(chǎn)生了basepresenter中進(jìn)行view和presenter的綁定解綁操作哆姻。如下代碼所示

public abstract class BasePresenter<V> {
    public WeakReference<V> mViewRef;
    protected Reference<Context> mContextRef;
    public void attachModelView(V pView,Context context) {
        mViewRef = new WeakReference<V>(pView);
        this.mContextRef = new WeakReference<>(context);
    }
  //...省略中間部分
    public void onDettach() {
        if (null != mViewRef) {
            mViewRef.clear();
            mViewRef = null;
        }
        if (mContextRef != null) {
            mContextRef.clear();
            mContextRef = null;
        }
    }

BaseView

public interface BaseView {
//做一些統(tǒng)一的方法處理
}

BaseActivity

public abstract class BaseActivity<V, T extends BasePresenter<V>> extends AppCompatActivity implements BaseView{
    protected T mPresenter;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(getLayoutId());
        mPresenter = createPresenter();
        if (mPresenter != null) {
            mPresenter.attachModelView((V) this, this);
        }
        mActivity = this;
        init();
    }
    protected abstract int getLayoutId();
    protected abstract void init();
    /**
     * 使用MVP模式時(shí)需要重寫該方法
     * @return
     */
    protected T createPresenter() {
        return null;
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mPresenter != null) {
            mPresenter.onDettach();
        }
    }
}

頁面代碼(偽)

public class HomeActivity extends BaseActivity<IHomeView, HomePresenter> 
implements IHomeView{
}

通過泛型,指明類型玫膀。一個(gè)頁面綁定一個(gè)presenter 實(shí)現(xiàn)一個(gè)view矛缨。統(tǒng)一在base里面處理presenter的綁定解綁,避免內(nèi)存泄漏,達(dá)到mvp的效果箕昭。

二灵妨、改進(jìn),上面的模式雖然可以實(shí)現(xiàn)mvp落竹,但是當(dāng)頁面邏輯過多時(shí)泌霍,P層代碼增長(zhǎng)速度會(huì)急劇上升。所以考慮之后筋量,決定一個(gè)頁面可以有多個(gè)presenter烹吵,實(shí)現(xiàn)多個(gè)view。將不同的業(yè)務(wù)解耦桨武,放在不同的presenter中肋拔,如果粒度合理,甚至可以同一套presenter復(fù)用到多個(gè)需要這種業(yè)務(wù)的頁面上呀酸。
話不多所凉蜂,直接放代碼。
base類 性誉,這是三個(gè)文件窿吩。

public interface IBaseView {
}

public interface IBasePresenter<V extends IBaseView> {

    /**
     * 綁定view到presenter
     *
     * @param pView
     * @param context
     */
    void onAttach(V pView, Context context);

    /**
     * 解綁view
     */
    void onDettach();

}

public abstract class BasePresenter<V extends IBaseView> implements IBasePresenter<V> {

    public WeakReference<V> mViewRef;
    protected Reference<Context> mContextRef;

    @Override
    public void onAttach(V pView, Context context) {
        mViewRef = new WeakReference<V>(pView);
        this.mContextRef = new WeakReference<>(context);
    }

   // ....省略部分代碼
    @Override
    public void onDettach() {
        if (null != mViewRef) {
            mViewRef.clear();
            mViewRef = null;
        }
        if (mContextRef != null) {
            mContextRef.clear();
            mContextRef = null;
        }
    }
}

BaseActivity 省略了部分代碼。完整的可以看鏈接错览。

public abstract class BaseActivity extends AppCompatActivity implements IBaseView {
    protected List<IBasePresenter> mPresenters;
    protected AppCompatActivity mActivity;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(getLayoutId());
        mPresenters = new ArrayList<>();
        mActivity = this;
        addPresenters();
        for (IBasePresenter presenter : mPresenters) {
            presenter.onAttach(this, mActivity);
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        for (IBasePresenter presenter : mPresenters) {
            presenter.onDettach();
        }
    }
    protected abstract void addPresenters();
    protected void addToPresenters(IBasePresenter child) {
        mPresenters.add(child);
    }

這里我們模擬兩個(gè)業(yè)務(wù)纫雁,一個(gè)登錄模塊的獲取名稱,一個(gè)新聞模塊的獲取新聞列表倾哺。

public interface LoginContract {
    interface IView extends IBaseView {
        void showName(String name);
    }
    interface Presenter extends IBasePresenter<IView> {
        void getName();
    }
}
public interface NewsContract {
    interface IView extends IBaseView {
        void showNews(List<String> news);
    }
    interface Presenter extends IBasePresenter<IView> {
        void getNews();
    }
}

public class LoginPresenter extends BasePresenter<LoginContract.IView> implements LoginContract.Presenter {
    @Override
    public void getName() {
        if (isAttach()) {
            getView().showName("小剛");
        }
    }
}


public class NewsPresenter extends BasePresenter<NewsContract.IView> implements NewsContract.Presenter {
    @Override
    public void getNews() {
        if (isAttach()) {
            List<String> news = mockNews(); //模擬一些數(shù)據(jù)返回轧邪,方法省略
            getView().showNews(news);
        }
    }
}

測(cè)試頁面代碼

public class MainActivity extends BaseActivity implements LoginContract.IView, NewsContract.IView {
    private LoginContract.Presenter loginPresenter;
    private NewsContract.Presenter newsPresenter;

    @Override
    protected void addPresenters() {
        loginPresenter = new LoginPresenter();
        newsPresenter = new NewsPresenter();
        addToPresenters(loginPresenter);
        addToPresenters(newsPresenter);
    }

    @Override
    protected int getLayoutId() {
        return R.layout.activity_main;
    }
    @Override
    protected void init() {
    }
    public void testLogin(View view) {
        loginPresenter.getName();
    }
    public void testNews(View view) {
        newsPresenter.getNews();
    }
    @Override
    public void showName(String name) {
        Toast.makeText(mActivity, "name:" + name, Toast.LENGTH_SHORT).show();
    }
    @Override
    public void showNews(List<String> news) {
        Toast.makeText(mActivity, "news一共有" + news.size() + "條消息", Toast.LENGTH_SHORT).show();
    }
}

在基類中統(tǒng)一處理presenter,和上面邏輯類似羞海,只是將單個(gè)presenter換成list忌愚,挨個(gè)綁定和解綁。basepresenter抽象類實(shí)現(xiàn)presenter接口却邓。采用契約類的模式約束業(yè)務(wù)硕糊。業(yè)務(wù)與業(yè)務(wù)之間解耦,有幾種業(yè)務(wù)腊徙,就產(chǎn)生幾種presenter简十。

三、數(shù)據(jù)層做了統(tǒng)一的封裝撬腾,全局單例獲取統(tǒng)一的實(shí)例勺远。無論是網(wǎng)絡(luò) 內(nèi)存 數(shù)據(jù)庫,統(tǒng)一將數(shù)據(jù)分業(yè)務(wù)放到一起去處理时鸵。這里p層中并沒有模擬出model的代碼,看似mvp中只有vp,沒有了m饰潜。 只是因?yàn)檫@篇記錄主要記錄關(guān)于vp的改動(dòng)初坠。model層并沒有太大修改。經(jīng)過統(tǒng)一封裝了之后彭雾,在presenter的init方法中獲取實(shí)例并操作碟刺。 故此處省略的model層的代碼。

僅此記錄開發(fā)中的一些改進(jìn)薯酝。歡迎勘誤半沽。多謝~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市吴菠,隨后出現(xiàn)的幾起案子者填,更是在濱河造成了極大的恐慌,老刑警劉巖做葵,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件占哟,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡酿矢,警方通過查閱死者的電腦和手機(jī)榨乎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來瘫筐,“玉大人蜜暑,你說我怎么就攤上這事〔吒危” “怎么了肛捍?”我有些...
    開封第一講書人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)驳糯。 經(jīng)常有香客問我篇梭,道長(zhǎng),這世上最難降的妖魔是什么酝枢? 我笑而不...
    開封第一講書人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任恬偷,我火速辦了婚禮,結(jié)果婚禮上帘睦,老公的妹妹穿的比我還像新娘袍患。我一直安慰自己,他們只是感情好竣付,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開白布诡延。 她就那樣靜靜地躺著,像睡著了一般古胆。 火紅的嫁衣襯著肌膚如雪肆良。 梳的紋絲不亂的頭發(fā)上筛璧,一...
    開封第一講書人閱讀 51,631評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音惹恃,去河邊找鬼夭谤。 笑死,一個(gè)胖子當(dāng)著我的面吹牛巫糙,可吹牛的內(nèi)容都是我干的朗儒。 我是一名探鬼主播,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼参淹,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼醉锄!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起浙值,我...
    開封第一講書人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤恳不,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后亥鸠,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體妆够,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年负蚊,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了神妹。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡家妆,死狀恐怖鸵荠,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情伤极,我是刑警寧澤蛹找,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站哨坪,受9級(jí)特大地震影響庸疾,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜当编,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一届慈、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧忿偷,春花似錦金顿、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至茶凳,卻和暖如春嫂拴,著一層夾襖步出監(jiān)牢的瞬間播揪,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來泰國打工筒狠, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留剪芍,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓窟蓝,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國和親饱普。 傳聞我的和親對(duì)象是個(gè)殘疾皇子运挫,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355