Android開發(fā)之MVP模式

前言:Android開發(fā)中有很多的設(shè)計(jì)模式鹿寻,MVP模式無(wú)疑是現(xiàn)在最流行的模式之一。MVP模式有效的降低了項(xiàng)目的復(fù)雜性及耦合性,view與model間完全解耦,通過(guò)presenter作為中間的連接紐帶丢胚,降低了view的復(fù)雜性及view與model耦合程度,使的項(xiàng)目邏輯更加清晰受扳,方便后期的修改與維護(hù)携龟。

MVP:Model--View--Presenter

model:用于數(shù)據(jù)的處理,如請(qǐng)求網(wǎng)絡(luò)數(shù)據(jù)等

view:用于將presenter傳遞的數(shù)據(jù)在UI中展示

presenter:將model獲取的數(shù)據(jù)傳遞給view勘高,是view與model聯(lián)系的中間橋梁

注:文中有用到Retrofit2+Rxjava2+Okhttp3實(shí)現(xiàn)網(wǎng)絡(luò)數(shù)據(jù)請(qǐng)求相關(guān)的知識(shí)峡蟋,這一篇文章 Android網(wǎng)絡(luò)請(qǐng)求Retrofit2+Rxjava2+Okhttp3的簡(jiǎn)單封裝中有詳細(xì)介紹。

1.Model

(1)定義model基類接口

/**  
* Created by ruancw on 2018/5/22. 
* model接口基類 
*/   
public interface IBaseModel {
    //獲取Rxjava的disposable
    <T>Disposable getObservable(Observable<HttpResponse<T>> observable, String tag);
    //網(wǎng)絡(luò)請(qǐng)求接口回調(diào)
    interface IOnRequestListener<T>{
        void onRequestSuccess(HttpResponse<T> tHttpResponse, String tag);
         void onRequestFail(Throwable throwable);
    }
}

(2)定義model實(shí)現(xiàn)類

/**  
* Created by ruancw on 2018/5/22. 
* 通用model實(shí)現(xiàn)類 
*/   
public class CommonModelImpl implements IBaseModel {
    private IOnRequestListener onRequestListener;   
    public CommonModelImpl(IOnRequestListener onRequestListener) {
        this.onRequestListener = onRequestListener;
    }

    @Override
    public <T> Disposable getObservable(Observable<HttpResponse<T>> observable, String tag) {
        return DisposableUtils.getDisposable(observable,tag,onRequestListener);
    }
}

2.View

定義view接口類

/**  
* Created by ruancw on 2018/5/22. 
* view基類接口 
*/   
public interface IBaseView {
    //顯示默認(rèn)的進(jìn)度框
    void showProgress();
    //顯示可設(shè)置提示信息的進(jìn)度框
    void showProgress(String message);
    //隱藏進(jìn)度框
    void hideProgress();
    //網(wǎng)絡(luò)請(qǐng)求數(shù)據(jù)成功后返回?cái)?shù)據(jù)的方法
    void onSuccess(HttpResponse httpResponse, String tag);
    //網(wǎng)絡(luò)請(qǐng)求失敗的方法
    void onFailed(Throwable throwable); 
}

3.Presenter

(1)定義presenter的基類

/**  
* Created by ruancw on 2018/5/22. 
* presenter基類 
*/   
public class BasePresenter implements IBaseModel.IOnRequestListener {
    //view接口
    protected IBaseView mView;
    //model接口實(shí)現(xiàn)類
    protected CommonModelImpl mModel;
    //Rxjava中的類
    private CompositeDisposable mCompositeDisposable;    
    /**  
    * 綁定view 
    * @param mView   
    */   
    public void attach(IBaseView mView){
        this.mView=mView;
        mModel=new CommonModelImpl(this);
    }

    /**  
    * 解綁view 
    */  
    public void detach(){
        if (mView!=null){
            mView=null;
        }
    }

    /**  
    * 反注冊(cè)rxjava 
    */  
    public void unSubscribe(){
        if (mCompositeDisposable != null) {
            mCompositeDisposable.dispose();
        }
    }

    /**  
    * 注冊(cè)rxjava 
    * @param disposable   
    */   
    public void addSubscription(Disposable disposable) {
        //rxjava進(jìn)行注冊(cè)
        if (mCompositeDisposable == null) {
            mCompositeDisposable= new CompositeDisposable();
        }
        mCompositeDisposable.add(disposable);
    }

    @Override
     public void onRequestSuccess(HttpResponse httpResponse, String tag) {
        if (mView!=null){
            mView.hideProgress();
            mView.onSuccess(httpResponse,tag);
        }
    }

    @Override
     public void onRequestFail(Throwable throwable) {
        if (mView!=null){
            mView.hideProgress();
            mView.onFailed(throwable);
        }
    }
}

(2)繼承基類BasePresenter(以登錄為例)

/**  
* Created by ruancw on 2018/5/23. 
* 登錄的presenter實(shí)現(xiàn)類 
*/   
public class LoginPresenterImpl extends BasePresenter {
    public void login(String url, LinkedHashMap<String, String> paramsMap,String tag){
        mView.showProgress("正在登錄");
        addSubscription(mModel.getObservable(HttpApiService.getInstance().getRemoteData(url,paramsMap),tag));
    }
}

注:直接定義的LoginPresenter的實(shí)現(xiàn)類华望,沒有定義ILoginPresenter接口

4.模塊中使用

(1)定義Activity基類

public abstract class BaseMvpActivity<B extends BasePresenter> extends BaseActivity {

    public B mPresenter;    
    @Override
    protected void beforeSetContentView() {
        mPresenter=initPresenter();
        mPresenter.attach(this);
    }

    protected abstract B initPresenter();    
    @Override
    protected void onDestroy() {
        mPresenter.detach();
        //mPresenter.unSubscribe();
        super.onDestroy();
    }
}

(2)LoginActivity繼承BaseMvpActivity

public class LoginActivity extends BaseMvpActivity<LoginPresenterImpl> {
    @BindView(R.id.btn_login)
    Button btnLogin;
    @BindView(R.id.et_account)
    ClearEditText etAccount;
    @BindView(R.id.et_password)
    PasswordView etPassword;
    private int userType=1;
    private UserBean userBean;
    private boolean autoLogin;      @Override
    protected boolean hasHeadTitle() {
        return false;
    }

    @Override
    protected LoginPresenterImpl initPresenter() {
        return new LoginPresenterImpl();
    }  

    @Override
    protected int getLayoutId() {
        return R.layout.activity_login;
    }

    @Override
    protected void initView() {
        //獲取上一次填寫的用手機(jī)號(hào)與密碼
        userBean=SharePreferencesUtil.getInstance().getUserBean();
        if (this.getIntent() != null && this.getIntent().getExtras() != null && this.getIntent().getExtras().containsKey("username")) {
            String username = this.getIntent().getExtras().getString("username");
            etAccount.setText(username);
        } else {
            if (userBean != null) {
                etAccount.setText(userBean.getAccount());
                etPassword.setContent(userBean.getPassword());
            }
        }
        //判斷是否是自動(dòng)登錄
        autoLogin=SharePreferencesUtil.getInstance().getBoolean("AUTO_LOGIN");
        if (autoLogin){
            //自動(dòng)登錄
            login();
        }
    }

    /**  
    * 登錄的方法 
    */  
    private void login() {
        etPassword.clearFocus();
        //系統(tǒng)的輸入管理器
        InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
        imm.showSoftInput(etPassword, InputMethodManager.SHOW_FORCED);
        imm.hideSoftInputFromWindow(etPassword.getWindowToken(), 0);
       if (TextUtils.isEmpty(etAccount.getText().toString().trim())) {
            UIUtil.toast("手機(jī)號(hào)不能為空");
       } else if (TextUtils.isEmpty(etPassword.getContent().trim())) {
            UIUtil.toast("密碼不能為空");
       } else {
            LinkedHashMap<String, String> paramsMap = new LinkedHashMap<>();
            paramsMap.put("loginName", etAccount.getText().toString().trim());
            paramsMap.put("password", etPassword.getContent().trim());
            paramsMap.put("userType", userType + "");
            mPresenter.login(ApiConstant.LOGIN, paramsMap, "tag");
      }
    }

    @OnClick(R.id.btn_login)
    public void onViewClicked() {
        //點(diǎn)擊的動(dòng)畫效果
        CommonUtil.animateClickView(btnLogin, new CommonUtil.ClickAnimation() {
            @Override
            public void onClick(View v) {
                login();
            }
        });    
    }

    @Override
    public void onSuccess(HttpResponse httpResponse, String tag) {
        int code = httpResponse.getStatus();
        Log.i("rcw", "httpResponse=" + httpResponse);
        Log.i("rcw", "code=" + httpResponse.getStatus());
        Log.i("rcw", "data=" + httpResponse.getData());
        if (code == 200) {
            //存儲(chǔ)賬號(hào)和密碼
            userBean=new UserBean();
            userBean.setAccount(etAccount.getText().toString().trim());
            userBean.setPassword(etPassword.getContent().trim());
            userBean.setUsertype(userType);
            SharePreferencesUtil.getInstance().saveUserBean(userBean);  
            //處理返回的data數(shù)據(jù)  
            String datas = new Gson().toJson(httpResponse.getData());
            LoginBean loginBean = new Gson().fromJson(datas, LoginBean.class);
            SharePreferencesUtil.getInstance().saveLoginBean(loginBean);
            Log.i("rcw", "getDeptName=" + loginBean.getDeptName());
            startActivity(new Intent(this, MainActivity.class));
            finish();
            UIUtil.toast("登錄成功");
        } else if (code == 402) {
            exitToLogin(this);
        } else {
            UIUtil.toast(httpResponse.getMessage());
    }    

 }

總結(jié):MVP模式使得View與Model間不能直接交互层亿,通過(guò)中間紐帶Presenter實(shí)現(xiàn)間接聯(lián)系,降低了View與Model間的耦合立美。MVP模式雖好,但如果項(xiàng)目頁(yè)面相對(duì)簡(jiǎn)單方灾,可以直接使用MVC模式實(shí)現(xiàn)建蹄。

如有任何問(wèn)題碌更,歡迎評(píng)論及留言,不勝感激洞慎,謝謝M吹ァ!劲腿!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末旭绒,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子焦人,更是在濱河造成了極大的恐慌挥吵,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,692評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件花椭,死亡現(xiàn)場(chǎng)離奇詭異忽匈,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)矿辽,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,482評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門丹允,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人袋倔,你說(shuō)我怎么就攤上這事雕蔽。” “怎么了宾娜?”我有些...
    開封第一講書人閱讀 162,995評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵批狐,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我碳默,道長(zhǎng)贾陷,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,223評(píng)論 1 292
  • 正文 為了忘掉前任嘱根,我火速辦了婚禮髓废,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘该抒。我一直安慰自己慌洪,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,245評(píng)論 6 388
  • 文/花漫 我一把揭開白布凑保。 她就那樣靜靜地躺著冈爹,像睡著了一般。 火紅的嫁衣襯著肌膚如雪欧引。 梳的紋絲不亂的頭發(fā)上频伤,一...
    開封第一講書人閱讀 51,208評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音芝此,去河邊找鬼憋肖。 笑死因痛,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的岸更。 我是一名探鬼主播鸵膏,決...
    沈念sama閱讀 40,091評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼怎炊!你這毒婦竟也來(lái)了谭企?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,929評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤评肆,失蹤者是張志新(化名)和其女友劉穎债查,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體糟港,經(jīng)...
    沈念sama閱讀 45,346評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡攀操,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,570評(píng)論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了秸抚。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片速和。...
    茶點(diǎn)故事閱讀 39,739評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖剥汤,靈堂內(nèi)的尸體忽然破棺而出颠放,到底是詐尸還是另有隱情,我是刑警寧澤吭敢,帶...
    沈念sama閱讀 35,437評(píng)論 5 344
  • 正文 年R本政府宣布碰凶,位于F島的核電站,受9級(jí)特大地震影響鹿驼,放射性物質(zhì)發(fā)生泄漏欲低。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,037評(píng)論 3 326
  • 文/蒙蒙 一畜晰、第九天 我趴在偏房一處隱蔽的房頂上張望砾莱。 院中可真熱鬧,春花似錦凄鼻、人聲如沸腊瑟。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,677評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)闰非。三九已至,卻和暖如春峭范,著一層夾襖步出監(jiān)牢的瞬間财松,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,833評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工纱控, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留辆毡,地道東北人政敢。 一個(gè)月前我還...
    沈念sama閱讀 47,760評(píng)論 2 369
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像胚迫,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子唾那,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,647評(píng)論 2 354

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

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,082評(píng)論 25 707
  • 之前一直用的MVC模式,但是業(yè)務(wù)邏輯稍微復(fù)雜访锻,MVC的缺點(diǎn)就暴露了:View和modle雜糅在一起,有時(shí)候?yàn)榱舜a...
    pkxutao閱讀 233評(píng)論 0 0
  • 作者:李旺成 時(shí)間:2016年4月3日 “Android MVP 詳解(下)”已經(jīng)發(fā)布闹获,歡迎大家提建議期犬。 MVP ...
    diygreen閱讀 128,868評(píng)論 86 1,321
  • 不知不覺來(lái)上海已經(jīng)兩個(gè)星期了。 本來(lái)打算在月初的周末去找?guī)孜煌诘鄱嫉呐笥殉猿燥埩牧奶齑虼蚺票芊蹋墒堑植?..
    陌上丿花已開閱讀 439評(píng)論 0 0
  • 高一的這一年沙庐,經(jīng)歷了很多很多的事情鲤妥,我們都懷著對(duì)高中生活的美好憧憬,帶著屬于自己的那一份熱忱和激情拱雏,邁進(jìn)了高中的校...
    冬暖_75e1閱讀 173評(píng)論 0 0