Android开发之MVP模式

前言:Android开发中有很多的设计模式,MVP模式无疑是现在最流行的模式之一。MVP模式有效的降低了项目的复杂性及耦合性,view与model间完全解耦,通过presenter作为中间的连接纽带,降低了view的复杂性及view与model耦合程度,使的项目逻辑更加清晰,方便后期的修改与维护。

MVP:Model--View--Presenter

model:用于数据的处理,如请求网络数据等

view:用于将presenter传递的数据在UI中展示

presenter:将model获取的数据传递给view,是view与model联系的中间桥梁

注:文中有用到Retrofit2+Rxjava2+Okhttp3实现网络数据请求相关的知识,这一篇文章 Android网络请求Retrofit2+Rxjava2+Okhttp3的简单封装中有详细介绍。

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);
    //网络请求接口回调
    interface IOnRequestListener<T>{
        void onRequestSuccess(HttpResponse<T> tHttpResponse, String tag);
         void onRequestFail(Throwable throwable);
    }
}

(2)定义model实现类

/**  
* Created by ruancw on 2018/5/22. 
* 通用model实现类 
*/   
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 {
    //显示默认的进度框
    void showProgress();
    //显示可设置提示信息的进度框
    void showProgress(String message);
    //隐藏进度框
    void hideProgress();
    //网络请求数据成功后返回数据的方法
    void onSuccess(HttpResponse httpResponse, String tag);
    //网络请求失败的方法
    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接口实现类
    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;
        }
    }

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

    /**  
    * 注册rxjava 
    * @param disposable   
    */   
    public void addSubscription(Disposable disposable) {
        //rxjava进行注册
        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实现类 
*/   
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的实现类,没有定义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() {
        //获取上一次填写的用手机号与密码
        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());
            }
        }
        //判断是否是自动登录
        autoLogin=SharePreferencesUtil.getInstance().getBoolean("AUTO_LOGIN");
        if (autoLogin){
            //自动登录
            login();
        }
    }

    /**  
    * 登录的方法 
    */  
    private void login() {
        etPassword.clearFocus();
        //系统的输入管理器
        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("手机号不能为空");
       } 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() {
        //点击的动画效果
        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) {
            //存储账号和密码
            userBean=new UserBean();
            userBean.setAccount(etAccount.getText().toString().trim());
            userBean.setPassword(etPassword.getContent().trim());
            userBean.setUsertype(userType);
            SharePreferencesUtil.getInstance().saveUserBean(userBean);  
            //处理返回的data数据  
            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());
    }    

 }

总结:MVP模式使得View与Model间不能直接交互,通过中间纽带Presenter实现间接联系,降低了View与Model间的耦合。MVP模式虽好,但如果项目页面相对简单,可以直接使用MVC模式实现。

如有任何问题,欢迎评论及留言,不胜感激,谢谢!!!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,457评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,837评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,696评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,183评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,057评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,105评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,520评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,211评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,482评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,574评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,353评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,897评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,489评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,683评论 2 335

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,051评论 25 707
  • 之前一直用的MVC模式,但是业务逻辑稍微复杂,MVC的缺点就暴露了:View和modle杂糅在一起,有时候为了代码...
    pkxutao阅读 220评论 0 0
  • 作者:李旺成 时间:2016年4月3日 “Android MVP 详解(下)”已经发布,欢迎大家提建议。 MVP ...
    diygreen阅读 128,784评论 86 1,321
  • 不知不觉来上海已经两个星期了。 本来打算在月初的周末去找几位同在帝都的朋友吃吃饭聊聊天打打牌,可是抵不...
    陌上丿花已开阅读 427评论 0 0
  • 高一的这一年,经历了很多很多的事情,我们都怀着对高中生活的美好憧憬,带着属于自己的那一份热忱和激情,迈进了高中的校...
    冬暖_75e1阅读 164评论 0 0