MVP 筆記

公司項(xiàng)目代碼中使用MVP弛针,之前并沒有在項(xiàng)目中使用過(guò)叠骑,記錄一下最近一段個(gè)人寫代碼時(shí)學(xué)到的習(xí)慣,代碼中有哪些不合理的地方削茁,希望可以留言指出

1. Demo 代碼

項(xiàng)目中宙枷,是按照模塊分的包,例如新聞相關(guān)的代碼就放在同一個(gè)news包下

寫代碼時(shí)一個(gè)重要的思路:

V層只負(fù)責(zé)UI展示茧跋,任何數(shù)據(jù)源盡量不放V層

P層負(fù)責(zé)邏輯處理朦拖,盡量不要有任何Android環(huán)境的代碼,尤其是 Context厌衔,更符合MVP的規(guī)范璧帝,并且方便單元測(cè)試

M層負(fù)責(zé)數(shù)據(jù)獲取,主要負(fù)責(zé)從網(wǎng)絡(luò)富寿、數(shù)據(jù)庫(kù)睬隶、本地文件中獲取數(shù)據(jù)源

寫完代碼锣夹,檢查寫的是否合理,簡(jiǎn)單的辦法就是查看各個(gè)分層代碼中類的引用


1.1 Activity 代碼

UI

整個(gè)Actiivty就一個(gè)Button苏潜,點(diǎn)擊Button請(qǐng)求網(wǎng)絡(luò)银萍,然后將數(shù)據(jù)顯示

/**
 * 展示請(qǐng)求的新聞字符串
 */
public class NewsActivity extends AppCompatActivity 
                                 implements NewsContract.View {
    private TextView mTvContent;
    private NewsContract.Presenter mPresenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_news);
        mPresenter = new NewsPresenter(this);
        initView();
    }
    
    /**
     * 取消網(wǎng)絡(luò)請(qǐng)求
     */
    @Override
    protected void onPause() {
        super.onPause();
        mPresenter.onPause();
    }

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

    /**
     * 展示請(qǐng)求的內(nèi)容
     *
     * @param content 數(shù)據(jù)
     */
    @Override
    public void showRequestContent(String content) {
        mTvContent.setText(content);
    }

    private void initView() {
        mTvContent = findViewById(R.id.activity_news_tv_content);
        Button btNews = findViewById(R.id.activity_news_bt_news);
        btNews.setOnClickListener(v -> mPresenter.onBtNewsClick());
    }
}

onCreate()中,正常的初始化恤左,并初始化Presenter贴唇。有時(shí)實(shí)際項(xiàng)目中,需要考慮下初始化的合理順序

就一個(gè)Button飞袋,在點(diǎn)擊監(jiān)聽中戳气,mPresenter.onBtNewsClick(),調(diào)用P層邏輯代碼

onPause()中,取消未完成的網(wǎng)絡(luò)請(qǐng)求

onDestroy()方法中巧鸭,做一些回收銷毀工作

void showRequestContent(String content)就是實(shí)現(xiàn)的V層接口回調(diào)的方法瓶您,在請(qǐng)求網(wǎng)絡(luò)后,展示內(nèi)容


1.2 Base 接口

BasePresenter

public interface BasePresenter {
    /**
     * 在 Activity onDestroy()生命周期方法
     */
    void onDestroy();
}

聲明每一個(gè)Presenter都要實(shí)現(xiàn)的銷毀階段方法


BaseView

public interface BaseView {
}

在這個(gè)Demo中纲仍,BaseView里并沒有聲明啥方法呀袱,實(shí)際項(xiàng)目代碼中根據(jù)實(shí)際需要聲明View層的共有方法


BaseCallback

public interface BaseCallback {
    /**
     * 成功回調(diào)
     *
     * @param content 請(qǐng)求內(nèi)容
     */
    void onSuccess(String content);

    /**
     * 失敗回調(diào)
     *
     * @param errorInfo 錯(cuò)誤信息
     */
    void onError(String errorInfo);
}

要進(jìn)行網(wǎng)絡(luò)請(qǐng)求,肯定需要用到Callback郑叠,個(gè)人習(xí)慣定義一個(gè)Base型接口


1.3 Contract 契約

interface NewsContract {
    interface Model {
        /**
         * 從網(wǎng)絡(luò)請(qǐng)求新聞內(nèi)容
         *
         * @param callback 結(jié)果回調(diào)
         */
        void requestNewsFromNet(BaseCallback callback);

        /**
         * 取消網(wǎng)絡(luò)請(qǐng)求
         */
        void cancel();
    }

    interface View extends BaseView {
        /**
         * 展示請(qǐng)求的內(nèi)容
         *
         * @param content 數(shù)據(jù)
         */
        void showRequestContent(String content);

    }

    interface Presenter extends BasePresenter {
        /**
         * 點(diǎn)擊按鈕
         */
        void onBtNewsClick();

        /**
         * 在 onPause() 生命周期
         */
        void onPause();
    }
}

一般View層接口主要以show方法為主

Presenter層方法個(gè)人習(xí)慣以onXX()形式夜赵,方便在查看代碼,更容易通過(guò)名字知道方法負(fù)責(zé)做啥


1.4 NewPresenter 代碼

public class NewsPresenter implements NewsContract.Presenter {
    private NewsContract.View mView;
    private NewsContract.Model mNewModel;

    NewsPresenter(NewsContract.View mView) {
        this.mView = mView;
        mNewModel = new NewsModel();
    }

    /**
     * 點(diǎn)擊按鈕乡革,請(qǐng)求網(wǎng)絡(luò)
     */
    @Override
    public void onBtNewsClick() {
        mNewModel.requestNewsFromNet(new NewsCallback(this));
    }

    /**
     * 取消網(wǎng)絡(luò)請(qǐng)求
     */
    @Override
    public void onPause() {
        mNewModel.cancel();
    }

    /**
     * 銷毀
     */
    @Override
    public void onDestroy() {
        if (mNewModel != null) {
            mNewModel = null;
        }
        if (mView != null) {
            mView = null;
        }
    }

    /**
     * 成功處理
     *
     * @param content 內(nèi)容
     */
    private void onRequestSuccess(String content) {
        mView.showRequestContent(content);
    }


    /**
     * 失敗處理
     *
     * @param errorInfo 錯(cuò)誤信息
     */
    private void onRequestError(String errorInfo) {
        mView.showRequestContent(errorInfo);
    }

    /**
     * 結(jié)果回調(diào)
     */
    private static class NewsCallback implements BaseCallback {

        private WeakReference<NewsPresenter> mReference;

        private NewsCallback(NewsPresenter presenter) {
            mReference = new WeakReference<>(presenter);
        }

        @Override
        public void onSuccess(String content) {
            NewsPresenter newsPresenter = mReference.get();
            if (newsPresenter != null) {
                newsPresenter.onRequestSuccess(content);
            }
        }

        @Override
        public void onError(String errorInfo) {
            NewsPresenter newsPresenter = mReference.get();
            if (newsPresenter != null) {
                newsPresenter.onRequestError(errorInfo);
            }
        }
    }
}

在構(gòu)造方法關(guān)聯(lián)View層油吭,并初始化Model層對(duì)象

Model進(jìn)行網(wǎng)絡(luò)請(qǐng)求,需要一個(gè)接口署拟,回調(diào)Presenter的代碼來(lái)更新UI

Model中的網(wǎng)絡(luò)耗時(shí)任務(wù)持有Presenter對(duì)象,而Presenter又持有View對(duì)象歌豺,當(dāng)Activity關(guān)閉時(shí)推穷,Activity對(duì)象無(wú)法被回收造成內(nèi)存泄漏

NewCallback寫成靜態(tài)內(nèi)部類,并且使用WeakReference軟引用类咧,使Presenter能夠及時(shí)的回收馒铃,這樣Activity也能及時(shí)回收


1.5 NewsModel 代碼

public class NewsModel implements NewsContract.Model {
    private final static String NEWS_URL = "http://news-at.zhihu.com/api/4/news/latest";

    /**
     * 從網(wǎng)絡(luò)獲取新聞內(nèi)容
     *
     * @param callback 結(jié)果回調(diào)
     */
    @Override
    public void requestNewsFromNet(BaseCallback callback) {
        OkGo.<String>get(NEWS_URL).execute(new InnerCallback(callback));
    }

    /**
     * 取消網(wǎng)絡(luò)請(qǐng)求
     */
    @Override
    public void cancel() {
        OkGo.getInstance().cancelAll();
    }

    /**
     * 結(jié)果回調(diào)
     */
    private static class InnerCallback extends StringCallback {
        private WeakReference<BaseCallback> mReference;

        private InnerCallback(BaseCallback callback) {
            mReference = new WeakReference<>(callback);
        }

        /**
         * 網(wǎng)絡(luò)
         */
        @Override
        public void onSuccess(Response<String> response) {
            String content = response.body();
            BaseCallback callback = mReference.get();
            if (callback != null) {
                callback.onSuccess(content);
            }
        }

        @Override
        public void onError(Response<String> response) {
            super.onError(response);
            String content = response.body();
            BaseCallback callback = mReference.get();
            if (callback != null) {
                callback.onError(content);
            }
        }

        /**
         * 緩存
         */
        @Override
        public void onCacheSuccess(Response<String> response) {
            super.onCacheSuccess(response);
            String content = response.body();
            BaseCallback callback = mReference.get();
            if (callback != null) {
                callback.onSuccess(content);
            }
        }
    }
}

Demo網(wǎng)絡(luò)請(qǐng)求使用的OkGo網(wǎng)絡(luò)框架,很強(qiáng)大蠻好用的網(wǎng)絡(luò)框架

內(nèi)部也使用一個(gè)靜態(tài)內(nèi)部類痕惋,使用WeakReference


2. 單元測(cè)試 TODO

單元測(cè)試主要針對(duì)的Presenter区宇,主要的邏輯都在P

View層主要是UIModel主要是拉取數(shù)據(jù)值戳,時(shí)間緊议谷,就先不做,不趕時(shí)再做

P層單元測(cè)試通過(guò)后堕虹,很大程度就說(shuō)明邏輯通過(guò)

由于P層不包含Android環(huán)境代碼卧晓,可以使用JUint + Mockit來(lái)進(jìn)行單元測(cè)試芬首。這也是為啥說(shuō)Presenter中盡量不要有Android SDK代碼的原因

單元測(cè)試正在學(xué),等實(shí)際做一段后再來(lái)補(bǔ)充

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末逼裆,一起剝皮案震驚了整個(gè)濱河市郁稍,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌胜宇,老刑警劉巖耀怜,帶你破解...
    沈念sama閱讀 218,941評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異桐愉,居然都是意外死亡财破,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門仅财,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)狈究,“玉大人,你說(shuō)我怎么就攤上這事盏求《蹲叮” “怎么了?”我有些...
    開封第一講書人閱讀 165,345評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵碎罚,是天一觀的道長(zhǎng)磅废。 經(jīng)常有香客問我,道長(zhǎng)荆烈,這世上最難降的妖魔是什么拯勉? 我笑而不...
    開封第一講書人閱讀 58,851評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮憔购,結(jié)果婚禮上宫峦,老公的妹妹穿的比我還像新娘。我一直安慰自己玫鸟,他們只是感情好导绷,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,868評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著屎飘,像睡著了一般妥曲。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上钦购,一...
    開封第一講書人閱讀 51,688評(píng)論 1 305
  • 那天檐盟,我揣著相機(jī)與錄音,去河邊找鬼押桃。 笑死葵萎,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播陌宿,決...
    沈念sama閱讀 40,414評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼锡足,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了壳坪?” 一聲冷哼從身側(cè)響起舶得,我...
    開封第一講書人閱讀 39,319評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎爽蝴,沒想到半個(gè)月后沐批,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,775評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蝎亚,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年九孩,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片发框。...
    茶點(diǎn)故事閱讀 40,096評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡躺彬,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出梅惯,到底是詐尸還是另有隱情宪拥,我是刑警寧澤,帶...
    沈念sama閱讀 35,789評(píng)論 5 346
  • 正文 年R本政府宣布铣减,位于F島的核電站她君,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏葫哗。R本人自食惡果不足惜缔刹,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,437評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望劣针。 院中可真熱鬧校镐,春花似錦、人聲如沸捺典。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)辣苏。三九已至,卻和暖如春哄褒,著一層夾襖步出監(jiān)牢的瞬間稀蟋,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工呐赡, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留退客,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,308評(píng)論 3 372
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像萌狂,于是被迫代替她去往敵國(guó)和親档玻。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,037評(píng)論 2 355

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