Android:MVP模式
2016-05-24 16:12
459 查看
简介
关于MVP的介绍网上有很多,这里不再累述,虽然大家的实现方式也许各不相同,但是有一些基本的共识:Model:数据模型;
View:用户界面,一般对应于Activity、Fragment;
Presenter:作为View和Model的桥梁,实现两者解耦。
MVP的核心是:将View中与UI无关的部分逻辑转移到Presenter中。
其依赖关系如下:
View与Presenter相互依赖,Presenter依赖于Model,View和Model解耦。
实例
经典的登陆例子
Model
数据来源,一般是从网络或者数据库异步加载通过接口回调。我们从内存模拟,然后通过handler#postDelayed来模拟加载过程。publicclassLoginModel { publicstatic Map<String, String> maps = new HashMap<>(); private Handler mHandler = new Handler(); static { maps.put("key1", "password1"); maps.put("key2", "password2"); maps.put("key3", "password3"); } publicvoid getPassword(final String username, final OnGetPasswordListener listener) { mHandler.postDelayed(new Runnable() { @Overridepublicvoid run() { String password = maps.get(username); if (password == null) { listener.onGetError(); } else { listener.onGet(password); } } }, new Random().nextInt(2000)); } publicvoid addUser(final String username, final String password, final OnRegisterListener registerListener) { mHandler.postDelayed(new Runnable() { @Overridepublicvoid run() { try { if (LoginModel.maps.containsKey(username)) { registerListener.onUserExist(); } else { LoginModel.maps.put(username, password); registerListener.onSuccessful(); } } catch (Exception e) { e.printStackTrace(); registerListener.onNetError(); } } }, new Random().nextInt(2000)); } publicinterfaceOnGetPasswordListener { void onGet(String password); void onGetError(); } publicinterfaceOnRegisterListener { void onSuccessful(); void onUserExist(); void onNetError(); } }
View
首先明确一个View的职责,以LoginActivity为例,他要做的是:用户点击登陆:
检查账号密码合法性;
登陆过程给用户提示;
如果登陆成功,用户提示消失,执行其他操作;
如果登陆失败,用户提示消失,执行其他操作;
用户点击清除:清除已经输入的内容
用户点击注册:跳转到注册界面
我们将其抽象为一个接口:
publicinterface ILoginView { void usernameIllegal(); void passwordIllegal(); void showProgress(); void dismissProgress(); void loginSuccessfully(); void loginUnsuccessfully(); void clearState(); }17
然后View去实现上诉接口:
publicclassLoginActivityextendsAppCompatActivityimplementsILoginView { @InjectView(R.id.et_username) EditText mEtUsername; @InjectView(R.id.et_password) EditText mEtPassword; @InjectView(R.id.btn_login) Button mBtnLogin; @InjectView(R.id.btn_clear) Button mBtnClear; private ProgressDialog mProgressDialog; private LoginPresenter mLoginPresenter = new LoginPresenter(this); @Overrideprotectedvoid onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.inject(this); mProgressDialog = new ProgressDialog(this); mBtnLogin.setOnClickListener(new View.OnClickListener() { @Overridepublicvoid onClick(View v) { mLoginPresenter.doLogin(mEtUsername.getText().toString(), mEtPassword.getText().toString()); } }); mBtnClear.setOnClickListener(new View.OnClickListener() { @Overridepublicvoid onClick(View v) { mLoginPresenter.doClear(); } }); } @Overridepublicvoid showProgress() { mProgressDialog.show(); } @Overridepublicvoid dismissProgress() { mProgressDialog.dismiss(); } @Overridepublicvoid loginSuccessfully() { Toast.makeText(LoginActivity.this, "login successfully", Toast.LENGTH_SHORT).show(); } @Overridepublicvoid loginUnsuccessfully() { Toast.makeText(LoginActivity.this, "login unsuccessfully, please check", Toast.LENGTH_SHORT).show(); } @Overridepublicvoid clearState() { mEtUsername.getText().clear(); mEtPassword.getText().clear(); } @Overridepublicvoid usernameIllegal() { mEtUsername.setError("should not be empty"); } @Overridepublicvoid passwordIllegal() { mEtPassword.setError("should not be empty"); } @Overridepublicboolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.menu_main, menu); returntrue; } @Overridepublicboolean onOptionsItemSelected(MenuItem item) { int id = item.getItemId(); if (id == R.id.action_settings) { startActivity(new Intent(this, RegisterActivity.class)); returntrue; } returnsuper.onOptionsItemSelected(item); } }91
可以看到上诉的View只执行UI相关的操作,没有任何逻辑,逻辑都转移到了Prensenter。有一些比较简单的逻辑可以不必转移到Presenter Layer,比如clearState,这一逻辑完全可以直接在View层调用而不经过Presenter回调。还有输入合法性判断,只判断其是否为空也是很简单的逻辑,同样可以在View层实现。总而言之,简单的逻辑可以在View中处理。
Presenter
最后也是最关键的,在Presenter中连接View与Model,承担View的大部分逻辑。publicclass LoginPresenter { private ILoginView mLoginView; private LoginModel mModel = new LoginModel(); public LoginPresenter(ILoginView loginView) { mLoginView = loginView; } publicvoid doLogin(String inputUsername, final String inputPassword) { if (inputUsername.equals("")) { mLoginView.usernameIllegal(); return; } if (inputPassword.equals("")) { mLoginView.passwordIllegal(); return; } mLoginView.showProgress(); mModel.getPassword(inputUsername, new LoginModel.OnGetPasswordListener() { publicvoid onGet(String password) { if (inputPassword.equals(password)) { mLoginView.loginSuccessfully(); } else { mLoginView.loginUnsuccessfully(); } mLoginView.dismissProgress(); } publicvoid onGetError() { mLoginView.loginUnsuccessfully(); mLoginView.dismissProgress(); } }); } publicvoid doClear() { mLoginView.clearState(); } }
比较
假设没有使用MVP架构,常见的做法会是:mBtnLogin.setOnClickListener(new View.OnClickListener() { @Overridepublicvoid onClick(View v) { if (mEtUsername.getText().toString().equals("")) { usernameIllegal(); return; } if (mEtPassword.getText().toString().equals("")) { passwordIllegal(); return; } LoginModel loginModel = new LoginModel(); showProgress(); loginModel.getPassword(mEtUsername.getText().toString(), new LoginModel.OnGetPasswordListener() { publicvoid onGet(String password) { if (mEtPassword.getText().toString().equals(password)) { loginSuccessfully(); } else { loginUnsuccessfully(); } dism b0f9 issProgress(); } publicvoid onGetError() { loginUnsuccessfully(); dismissProgress(); } }); } });30
这种写法,虽然一开始写起来会非常方便,但是越到后面越臃肿而难以维护。所以使用MVP架构会使得层次清晰,View与Model解耦,代码也较为整洁。
假设现在我要测试一下逻辑是否有误,如果是上诉写法,除了run一下工程然后手动点击测试之外,仿佛无从下手。使用了MVP架构,将View职责抽象成接口后,我们写起单元测试会比较方便。
最后,很多文章会把Model和Prensenter也抽象出一个接口,目前我不知道这样做有什么好处,所以我倾向于Model和Presenter先不需要抽象接口,需要时再进行重构。
项目地址:
https://github.com/leelit/android-mvp-pattern
参考:
http://blog.csdn.net/vector_yi/article/details/24719873
https://segmentfault.com/a/1190000003927200
http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0227/2503.html
http://blog.csdn.net/lmj623565791/article/details/46596109
https://github.com/antoniolg/androidmvp
相关文章推荐
- Android6.0文件变动
- Android检测内存泄漏之leakcanary
- Android Studio导入aar文件的方法
- Android universal-image-loader详细解析
- Android阅读器文字分散对齐及TextView中的字分散对齐,首尾缩进
- Android——View对点击事件的处理
- android SQLite数据库的使用
- xutils3.0下载器的使用
- Android JNI Java类型签名
- android 布局文件中<include />标签的用法
- android中使用startactivityforresult跳转Activity后需要重写onBackPressed()方法
- android系统广播大全
- Android Handler警告,SimpleDateFormat警告
- Android Volley框架完全解析,灰常好用
- Android-APK文件结构和安装过程
- Android不同版本下Notification创建方法
- Android 数据库升级
- Android实现千变万化的ViewPager切换动画
- RxJava 和 RxAndroid 三(生命周期控制和内存优化)
- android之文本前面或后面多标签