:提问:明白如何选择开发框架,和为什么要学 MVP 模式观察:比较 MVC 模式和 MVP 模式,理解 MVP 模式的概念使用:通过一个例子,学习如何使用 MVP 模式总结。提问
首先自问自答:
在 Android 开发中如何选择开发框架呢?
首先要知道为什么要选择开发框架?目的如下:
代码可读维护性好方便测试框架的核心思想:解耦分层:纵向上的解耦模块化:横向上的解耦
横向的模块化:对大家来可能并不陌生,在一个项目建立项目文件夹的时候就会遇到这个问题,通常的做法是将相同功能的模块放到同一个目录下,更复杂的,可以通过插件化来实现功能的分离与加载。纵向的分层,不同的项目可能就有不同的分法,并且随着项目的复杂度变大,层次可能越来越多
根据自己具体项目而定,如果业务简单的项目,用 MVC 模式就可以了,不管是什么开发框架,都是为了更好的提高工作效率,是手段,方法,不是目的,不要为了设计而设计,否则浪费时间,精力,得不偿失。
为什么要学习 MVP 模式,很重要吗?
为什么要思考这问题?因为时间短暂,想要做的事情很多,必须学会取舍,才能更好的做好事情。
我有个原则:
最重要的事情,只有一件,要么不做,要么就做得最好
如果没有学习 MVP 模式,会怎么样?
好处:
可以做其他,自己认为更重要的事情
坏处:
思维方面:对于一些基于 MVP 模式优秀项目,但是看不懂啊,阅读大神优秀思想的作品,
无法享受思维上的乐趣,怎么能阻碍我们成为大神的前进道路呢?不可原谅,beat it !
工作方面:如果项目业务很复杂,使用 MVC 模式,项目开发效率,管理,代码可读性低,测试和维护时间长,工作效率低
所以从否定角度,证明学习 MVP 模式是重要,是必须的,于是花时间,专门学习,研究 MVP 模式,运用到工作项目中,提高工作效率
如何学习 MVP 模式?
1)如何认识 MVP 模式?(观察现象)
回顾 MVC 模式
比较 MVC 模式和 MVP 模式的区别,通过比较区别,显出 MVP 模式的特点,优势
2)如何使用 MVP 模式?(使用方法)
通过例子分析
观察回顾 MVC 模式 View:对应于布局文件 Model:业务逻辑和实体模型 Controllor:对应于 Activity,或 Fragment 如下图所示
MVC 之间的数据是如何流动的?首先 View 控件设置监听事件,在哪里设置呢?可以在 Controller 中进行设置,比如在 Activity 中设置按钮的点击事件,也可以在自定义控件中进行设置,比如在 Listview 中设置滑动处理事件。这些事件是什么?可以通知 Controller 去操作 Model, 更新 Model 数据,也可以直接更新 Model 数据,也可以是 Controller 更新 View 数据举例:用户对界面进行操作,比如点击,滑动界面(事先注册的 View), 监听的 View 被响应,告诉 Controler 进行处理响应事件,比如操作 Model, 访问网络数据,更新 Model 对象,然后更新界面 View 的数据,或控件的状态变化 MVC 模式存在的问题:数据据绑定的操作,事件处理的代码都在 Activity 中,造成了 Activity 既像 View 又像 Controller,导致 Activity 负责本来应该是 View 负责的工作(控件的状态改变等等),又要负责操作 Model 层,导致 Activity 的责任太重,业务多,代码太多,维护需要更多时间。于是 MVP 模式出现了,解决 MVC 模式的 Controller 责任太重的问题概念
当将架构改为 MVP 以后,Presenter 的出现,将 Actvity 视为 View 层,Presenter 负责完成 View 层与 Model 层的交互。现在是这样的:
View 对应于 Activity,负责显示数据,View 的绘制以及与用户交互 Model 业务逻辑和实体模型 Presenter 负责完成 View 于 Model 间的交互,处理着程序各种逻辑的分发,收到 View 层 UI 上的反馈命令、定时命令、系统命令等指令后分发处理逻辑交由 Model 层做具体的业务操作。
如图所示:
有图观察可知:MCV 模式与 MVP 模式最明显的区别就是,
MVC 中是允许 Model 和 View 进行交互的,而 MVP 中很明显,Model 与 View 之间的交互由 Presenter 完成。还有一点就是 Presenter 与 View 之间的交互是通过接口的
MVP 的优点
1. 降低耦合度,实现了 Model 和 View 真正的完全分离,可以修改 View 而不影响 Modle
2. 模块职责划分明显,层次清晰
3. 隐藏数据
4. Presenter 可以复用,一个 Presenter 可以用于多个 View,而不需要更改 Presenter 的逻辑(当然是在 View 的改动不影响业务逻辑的前提下)
5. 利于测试驱动开发。
6. View 可以进行组件化。在 MVP 当中,View 不依赖 Model。这样就可以让 View 从特定的业务场景中脱离出来,可以说 View 可以做到对业务完全无知。它只需要提供一系列接口提供给上层操作。这样就可以做到高度可复用的 View 组件。
7. 代码灵活性
MVP 的缺点:
1. Presenter 中除了应用逻辑以外,还有大量的 View->Model,Model->View 的手动同步逻辑,造成 Presenter 比较笨重,维护起来会比较困难。
2. 由于对视图的渲染放在了 Presenter 中,所以视图和 Presenter 的交互会过于频繁,view 里的行为太过复杂,应对复杂场景势必造成代码行为膨胀
3. 如果 Presenter 过多地渲染了视图,往往会使得它与特定的视图的联系过于紧密。一旦视图需要变更,那么 Presenter 也需要变更了。
4. 额外的代码复杂度及学习成本
使用
例子:实现效果:简单登陆界面功能,输入姓名,密码,然后点击登陆按键,提示登陆结果,点击清除按键,清除输入内容
(一)Model 层
1)分析 Model 数据模型,实体类有什么属性和行为。
比如:这个例子中,登陆功能,需要一个用户模型 User
- package com.example.juphome.mvpdemo.Login.bean;
- /** * Created by Juphome on 2017/1/15. */
- public class User {
- private String username;
- private String password;
- public String getUsername() {
- return username;
- }
- public void setUsername(String username) {
- this.username = username;
- }
- public String getPassword() {
- return password;
- }
- public void setPassword(String password) {
- this.password = password;
- }
- }
2)定义操作具体业务类的回调接口: OnLoginListener,用于回调监听登陆结果状态
- package com.example.juphome.mvpdemo.Login.biz;
- import com.example.juphome.mvpdemo.Login.bean.User;
- /** * Created by Juphome on 2017/1/15. */
- public interface OnLoginListener {
- void loginSuccess(User user);
- void loginFailed();
- }
定义操作业务类的抽象接口:IUserBiz
- package com.example.juphome.mvpdemo.Login.biz;
- import com.example.juphome.mvpdemo.Login.bean.User;
- /** * Created by Juphome on 2017/1/15. */
- public interface OnLoginListener {
- void loginSuccess(User user);
- void loginFailed();
- }
实现操作业务类的抽象接口:UserBiz
- package com.example.juphome.mvpdemo.Login.biz;
- import com.example.juphome.mvpdemo.Login.bean.User;
- /** * Created by Juphome on 2017/1/15. */
- public class UserBiz implements IUserBiz {@Override public void login(final String username, final String password, final OnLoginListener loginListener) { //模拟子线程耗时操作 new Thread() { @Override public void run() { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } //模拟登录成功 if ("zhy".equals(username) && "123".equals(password)) { User user = new User(); user.setUsername(username); user.setPassword(password); loginListener.loginSuccess(user); } else { loginListener.loginFailed(); } } }.start(); }}
(二)View 层和 Presenter 层的接口
定义契约类(接口)
使用契约类来统一管理 view 与 presenter 的所有的接口,这种方式使得 view 与 presenter 中有哪些功能,一目了然,维护起来也很方便。
2.1 如何定义 UserBizContract 接口中的 View 的接口呢?去观察功能上的操作,然后考虑该操作需要什么?(getUserName, getPassword)该操作的结果,对应的反馈?(toMainActivity, showFailedError) 该操作过程中对应的友好的交互?(showLoading, hideLoading)2.2 如何定义 UserBizContract 中的 presenter 接口呢?
Presenter 是用作 Model 和 View 之间交互的桥梁,那么应该有什么方法呢?
其实也是主要看该功能有什么操作,比如本例,两个操作: login 和 clear。
- package com.example.juphome.mvpdemo.Login.biz;
- import com.example.juphome.mvpdemo.Login.bean.User;
- /** * Created by Juphome on 2017/1/15. */
- public interface UserBizContract {
- interface IUserLoginView {
- String getUserName();
- String getPassword();
- void clearUserName();
- void clearPassword();
- void showLoading();
- void hideLoading();
- void toMainActivity(User user);
- void showFailedError();
- }
- interface Presenter {
- void login();
- void clear();
- }
- }
(三)View 层和 Presenter 层的接口实现定义 presenter 接口的实体类
- package com.example.juphome.mvpdemo.Login.presenter;
- import android.os.Handler;
- import com.example.juphome.mvpdemo.Login.bean.User;
- import com.example.juphome.mvpdemo.Login.biz.IUserBiz;
- import com.example.juphome.mvpdemo.Login.biz.OnLoginListener;
- import com.example.juphome.mvpdemo.Login.biz.UserBiz;
- import com.example.juphome.mvpdemo.Login.biz.UserBizContract;
- /** * Created by Juphome on 2017/1/15. */
- public class UserLoginPresenter implements UserBizContract.Presenter {
- private IUserBiz userBiz;
- private UserBizContract.IUserLoginView userLoginView;
- private Handler mHandler = new Handler();
- public UserLoginPresenter(UserBizContract.IUserLoginView userLoginView) {
- this.userLoginView = userLoginView;
- this.userBiz = new UserBiz();
- }@Override public void login() {
- userLoginView.showLoading();
- userBiz.login(userLoginView.getUserName(), userLoginView.getPassword(), new OnLoginListener() {@Override public void loginSuccess(final User user) { //需要在UI线程执行 mHandler.post(new Runnable() { @Override public void run() { userLoginView.toMainActivity(user); userLoginView.hideLoading(); } }); } @Override public void loginFailed() { //需要在UI线程执行 mHandler.post(new Runnable() { @Override public void run() { userLoginView.showFailedError(); userLoginView.hideLoading(); } }); } }); } @Override public void clear() { userLoginView.clearUserName(); userLoginView.clearPassword(); }}
定义 View 的实体类
最后让 Model-View-Presenter 动态联系起来
让 Activity 实现 View 的接口 IUserLoginView,MVP 中的 View 其实就是 Activity,调用 Presenter,处理业务操作,Presenter 处理过程中,通知 Activity 界面友好提示,显示缓冲数据图标,Presenter 处理业务操作完成后,回调通知 Activity 结果状态,失败或成功的数据
- package com.example.juphome.mvpdemo.Login.view;
- import android.app.Activity;
- import android.os.Bundle;
- import android.view.View;
- import android.widget.Button;
- import android.widget.EditText;
- import android.widget.ProgressBar;
- import android.widget.Toast;
- import com.example.juphome.mvpdemo.Login.bean.User;
- import com.example.juphome.mvpdemo.Login.biz.UserBizContract;
- import com.example.juphome.mvpdemo.Login.presenter.UserLoginPresenter;
- import com.example.juphome.mvpdemo.R;
- /** * Created by Juphome on 2017/1/15. */
- public class UserLoginActivity extends Activity implements UserBizContract.IUserLoginView {
- private EditText mEtUsername,
- mEtPassword;
- private Button mBtnLogin,
- mBtnClear;
- private ProgressBar mPbLoading;
- private UserLoginPresenter mUserLoginPresenter = new UserLoginPresenter(this);@Override protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_user_login);
- initViews();
- }
- private void initViews() {
- mEtUsername = (EditText) findViewById(R.id.id_et_username);
- mEtPassword = (EditText) findViewById(R.id.id_et_password);
- mBtnClear = (Button) findViewById(R.id.id_btn_clear);
- mBtnLogin = (Button) findViewById(R.id.id_btn_login);
- mPbLoading = (ProgressBar) findViewById(R.id.id_pb_loading);
- mBtnLogin.setOnClickListener(new View.OnClickListener() {@Override public void onClick(View v) {
- mUserLoginPresenter.login();
- }
- });
- mBtnClear.setOnClickListener(new View.OnClickListener() {@Override public void onClick(View v) {
- mUserLoginPresenter.clear();
- }
- });
- }@Override public String getUserName() {
- return mEtUsername.getText().toString();
- }@Override public String getPassword() {
- return mEtPassword.getText().toString();
- }@Override public void clearUserName() {
- mEtUsername.setText("");
- }@Override public void clearPassword() {
- mEtPassword.setText("");
- }@Override public void showLoading() {
- mPbLoading.setVisibility(View.VISIBLE);
- }@Override public void hideLoading() {
- mPbLoading.setVisibility(View.GONE);
- }@Override public void toMainActivity(User user) {
- Toast.makeText(this, user.getUsername() + " login success , to MainActivity", Toast.LENGTH_SHORT).show();
- }@Override public void showFailedError() {
- Toast.makeText(this, "login failed", Toast.LENGTH_SHORT).show();
- }
- }
使用 MVP 模式的步骤:
定义 mode 层的实体类,操作业务类的接口操作业务类完成的结果状态监听接口定义 view 的接口。定义 Presenter 接口实现定义好的 View 接口和 Presenter 接口让 MVP 动起来 Model-View-Presenter 的动态过程:
让 Acivity 实现 view 接口,在 Activity 中创建 Presenter 引用对象,用 Presenter 对象,操作 Model 的业务逻辑处理,Model 层处理完成后,通过业务监听回调接口,告诉 Presenter 处理业务的结果如何,最后 Persenter 通过 VIew 接口,让 Activity 进行页面数据更新
总结最后,我们再来看这张图。Activity 作为 View,View 和 Presenter 在 Activity 中来进行关联,Presenter 与 Model 之间双向交互,Model 与 View 之间没有直接接触,达到解耦的作用
就爱阅读 www.92to.com 网友整理上传, 为您提供最全的知识大全, 期待您的分享,转载请注明出处。
来源: