我的MVP学习之路

MVP(面向接口编程)

1.为了解耦,MVC中V-C耦合

  • Model:Bean+业务逻辑
  • View:Activity、Fragment
  • Presenter:起到桥梁作用,相当于经纪人的作用

  • 登录模块:

MVP 开发遵守一个视图( Activity、Fragment) 对应一个 Presenter
我的疑惑:有很多博客说逻辑操作应该在 P 层,单个人认为应该在 M 层,因为 P 层只是一个连接 V 层和 M 层的桥梁,既是桥梁就只因该实现两者之间的联系交互,而具体的业务逻辑因该交给 M 层去处理,将处理的结果反馈给 P 层,然后在 P 层中实现和 V 层的交互。

Model层:数据模型和逻辑业务处理层
  •  先创建数据模型(bean),如登录模块的User(bean)
  • 然后创建业务逻辑的接口,包含登录的业务逻辑,包含所需的参数和回调接口(接口回调判断登录逻辑的结果处理),
  • 然后创建具体的Model层实现类,实现该接口,处理具体的业务逻辑
具体代码:
      User数据模型(bean类)
    1. package com.itheima.mvpdemo.model;
    2. /**
    3. * 登录用户
    4. */
    5. public class User {
    6. private String username;
    7. private String password;
    8. public String getUsername() {
    9. return username;
    10. }
    11. public void setUsername(String username) {
    12. this.username = username;
    13. }
    14. public String getPassword() {
    15. return password;
    16. }
    17. public void setPassword(String password) {
    18. this.password = password;
    19. }
    20. public User(String username, String password) {
    21. this.username = username;
    22. this.password = password;
    23. }
    24. public User() {
    25. }
    26. }
    Model业务逻辑层:

    1. /**
    2. * Model层:登录业务逻辑
    3. */
    4. public interface IUserLoginModel {
    5. /**
    6. * 登录业务逻辑
    7. * @param username 用户名
    8. * @param password 密码
    9. * @param listener 回调接口
    10. */
    11. void login(String username, String password, OnUserLoginListener listener);
    12. }
    Model登录回调接口层

    1. package com.itheima.mvpdemo.model;
    2. /**
    3. * 登录回调接口
    4. */
    5. public interface OnUserLoginListener {
    6. /**
    7. * 登录成功回调
    8. * @param user
    9. */
    10. void loginSuccess(User user);
    11. /**
    12. * 登录失败回调
    13. */
    14. void loginFailed();
    15. }
    Model业务逻辑具体实现层
    1. package com.itheima.mvpdemo.model;
    2. import android.os.SystemClock;
    3. /**
    4. * Model层具体实现
    5. */
    6. public class UserLoginModel implements IUserLoginModel {
    7. @Override
    8. public void login(final String username, final String password, final OnUserLoginListener listener) {
    9. //模拟子线程
    10. new Thread(new Runnable() {
    11. @Override
    12. public void run() {
    13. SystemClock.sleep(1500);
    14. //登录:判断用户名和密码
    15. if("heima27".equals(username) && "123456".equals(password)){//登录成功
    16. User user = new User(username, password);
    17. listener.loginSuccess(user);
    18. }else{//登录失败
    19. listener.loginFailed();
    20. }
    21. }
    22. }).start();
    23. }
    24. }

View层:Activity/Fragment实现该接口,View需要考虑三个问题( 和UI相关的操作 )
  • (1)有什么操作,该操作需要什么?:1.获取用户名、 2.获取密码;
  • (2)这些操作结果是什么?对应的反馈是什么?:1.登录成功跳转到主页、 2.登录失败提示用户;
  • (3)有什么用户交互:1.显示滚动框、 2.隐藏滚动框。
具体代码:
  1. package com.itheima.mvpdemo.view;
  2. import com.itheima.mvpdemo.model.User;
  3. /**
  4. * View层接口:Activity/Fragment实现该接口
  5. * View层如何写代码?看页面哪些是View,通过下面几个方面分析
  6. *
  7. * 1.有哪些操作?获取用户名、获取密码
  8. * 2.用哪些用户交互(为了提高用户体验):显示滚动条、隐藏滚动条
  9. * 3.上面的操作和用户结合带来上面结果(有什么结果):成功跳转主页、失败提示用户
  10. */
  11. public interface IUserLoginView {
  12. /**获取用户名*/
  13. String getUsername();
  14. /**获取密码*/
  15. String getPassword();
  16. /**显示滚动条*/
  17. void showProgressbar();
  18. /**隐藏滚动条*/
  19. void hideProgressbar();
  20. /**成功跳转主页*/
  21. void jumpToMain(User user);
  22. /**失败提示用户*/
  23. void showLoginError();
  24. }

           
Presenter层:Activity/Fragment初始化Presenter层对象,调用Presenter中M层和View层通信的方法,Presenter层需要考虑两个问题:
  • 1.既然P层是M和V的桥梁,那么P层类中肯定有M和V层对象;
  •  2.P层是桥梁,那么肯定要提供方法,让M层和V层在桥梁上“对话”。
  •  其实也是主要看该功能有什么操作
具体代码:
  1. package com.itheima.mvpdemo.presenter;
  2. import android.os.Handler;
  3. import com.itheima.mvpdemo.model.IUserLoginModel;
  4. import com.itheima.mvpdemo.model.OnUserLoginListener;
  5. import com.itheima.mvpdemo.model.User;
  6. import com.itheima.mvpdemo.model.UserLoginModel;
  7. import com.itheima.mvpdemo.view.IUserLoginView;
  8. /**
  9. * presenter:起到桥梁作用
  10. * 1.既然p层起到桥梁的作用:p层必须持有view层和model层对象,联系view和model
  11. * 2.既然p层起到桥梁作用,p层必须提供桥梁方法
  12. */
  13. public class UserLoginPresenter implements OnUserLoginListener {
  14. private IUserLoginModel userLoginModel;//model层
  15. private IUserLoginView userLoginView;//view层
  16. /**
  17. * 问题:两个参数的构造方法对吗?
  18. * 1.p层是View层调用的,如果是两个参数构造方法,说明view层持有model层对象,view-model耦合,不符合MVP架构
  19. * p层构造方法只能传入View层对象,Model层对象是通过创建Model层的具体实现类来获取的
  20. * @param userLoginView
  21. */
  22. /*public UserLoginPresenter(IUserLoginModel model, IUserLoginView userLoginView) {
  23. this.model = model;
  24. this.userLoginView = userLoginView;
  25. }*/
  26. public UserLoginPresenter(IUserLoginView userLoginView) {
  27. this.userLoginModel = new UserLoginModel();
  28. this.userLoginView = userLoginView;
  29. }
  30. /**
  31. * presenter层桥梁方法:model层和view层在该方法通信
  32. */
  33. public void login(){
  34. //1.显示滚动条
  35. userLoginView.showProgressbar();
  36. //2.调用model层登录业务:下面一行代码集中体现了p层起到桥梁的作用
  37. userLoginModel.login(userLoginView.getUsername(), userLoginView.getPassword(), this);
  38. }
  39. @Override
  40. public void loginSuccess(final User user) {
  41. //放到UI线程
  42. mHandler.post(new Runnable() {
  43. @Override
  44. public void run() {
  45. //1.隐藏滚动条
  46. userLoginView.hideProgressbar();
  47. //2.跳转主页
  48. userLoginView.jumpToMain(user);
  49. }
  50. });
  51. }
  52. @Override
  53. public void loginFailed() {
  54. mHandler.post(new Runnable() {
  55. @Override
  56. public void run() {
  57. //1.隐藏滚动条
  58. userLoginView.hideProgressbar();
  59. //2.提示用户登录失败
  60. userLoginView.showLoginError();
  61. }
  62. });
  63. }
  64. private Handler mHandler = new Handler();
  65. }

Activity代码:Activity/Fragment实现该View层接口,Activity/Fragment初始化Presenter层对象,调用Presenter中M层和View层通信的方法,
  1. public class LoginActivityMVP extends Activity implements OnClickListener, IUserLoginView {
  2. private EditText etUsername;
  3. private EditText etPassword;
  4. private ProgressBar progressBar;
  5. private Context context;
  6. private Button btnLogin;
  7. private UserLoginPresenter presenter;
  8. @Override
  9. protected void onCreate(Bundle savedInstanceState) {
  10. super.onCreate(savedInstanceState);
  11. setContentView(R.layout.login_activity_mvp);
  12. context = this;
  13. initViews();
  14. btnLogin.setOnClickListener(this);
  15. //创建presenter对象
  16. presenter = new UserLoginPresenter(this);
  17. }
  18. private void initViews() {
  19. etUsername = (EditText) findViewById(R.id.et_username);
  20. etPassword = (EditText) findViewById(R.id.et_password);
  21. progressBar = (ProgressBar) findViewById(R.id.progressBar1);
  22. btnLogin = (Button) findViewById(R.id.btnLogin);
  23. }
  24. @Override
  25. public void onClick(View v) {
  26. switch (v.getId()) {
  27. case R.id.btnLogin:
  28. presenter.login();
  29. break;
  30. }
  31. }
  32. @Override
  33. public String getUsername() {
  34. return etUsername.getText().toString().trim();
  35. }
  36. @Override
  37. public String getPassword() {
  38. return etPassword.getText().toString().trim();
  39. }
  40. @Override
  41. public void showProgressbar() {
  42. progressBar.setVisibility(View.VISIBLE);
  43. }
  44. @Override
  45. public void hideProgressbar() {
  46. progressBar.setVisibility(View.GONE);
  47. }
  48. @Override
  49. public void jumpToMain(User user) {
  50. //模拟跳转
  51. Toast.makeText(LoginActivityMVP.this, "登录成功,跳转主页", Toast.LENGTH_SHORT).show();
  52. }
  53. @Override
  54. public void showLoginError() {
  55. Toast.makeText(LoginActivityMVP.this, "用户名或者密码错误,重新登录!", Toast.LENGTH_SHORT).show();
  56. }
  57. }

案例:
目录结构:

gradle文件

1
compile 'com.loopj.android:android-async-http:1.4.9'

说明:请求网络使用async-http

 MainModelBean 数据模型(bean类)
  1. package com.example.chuxiong.mvpdemo.bean;
  2. /*
  3. * @项目名: MVP Demo
  4. * @包名: com.example.chuxiong.mvpdemo.model
  5. * @文件名: MainModelBean
  6. * @创建者: chuxiong
  7. * @创建时间: 2017/3/21 14:41
  8. * @描述: TODO
  9. */
  10. public class MainModelBean {
  11. private static final String TAG = "MainModelBean";
  12. private String city;
  13. private String wd;
  14. private String ws;
  15. private String time;
  16. public String getCity() {
  17. return city+"\n";
  18. }
  19. public void setCity(String city) {
  20. this.city = city;
  21. }
  22. public String getWd() {
  23. return wd+"\n";
  24. }
  25. public void setWd(String wd) {
  26. this.wd = wd;
  27. }
  28. public String getTime() {
  29. return time+"\n";
  30. }
  31. public void setTime(String time) {
  32. this.time = time;
  33. }
  34. public String getWs() {
  35. return ws+"\n";
  36. }
  37. public void setWs(String ws) {
  38. this.ws = ws;
  39. }
  40. }
Model业务逻辑层:    
  1. package com.example.chuxiong.mvpdemo.model;
  2. /*
  3. * @项目名: MVP Demo
  4. * @包名: com.example.chuxiong.mvpdemo.model
  5. * @文件名: IMainLoading
  6. * @创建者: chuxiong
  7. * @创建时间: 2017/3/21 15:50
  8. * @描述: TODO
  9. */
  10. public interface IMainLoading {
  11. void load(IMainLoadingListener listener);
  12. }
Model监听回调接口层
  1. package com.example.chuxiong.mvpdemo.model;
  2. import com.example.chuxiong.mvpdemo.bean.MainModelBean;
  3. /*
  4. * @项目名: MVP Demo
  5. * @包名: com.example.chuxiong.mvpdemo.model
  6. * @文件名: IMainLoadingListener
  7. * @创建者: chuxiong
  8. * @创建时间: 2017/3/21 15:51
  9. * @描述: TODO
  10. */
  11. public interface IMainLoadingListener {
  12. void loadDataSuccess(MainModelBean mainModelBean);
  13. void loadDataFailure();
  14. }
Model业务逻辑具体实现层
  1. package com.example.chuxiong.mvpdemo.model;
  2. import com.example.chuxiong.mvpdemo.bean.MainModelBean;
  3. import com.loopj.android.http.AsyncHttpClient;
  4. import com.loopj.android.http.JsonHttpResponseHandler;
  5. import org.json.JSONException;
  6. import org.json.JSONObject;
  7. import cz.msebera.android.httpclient.Header;
  8. /*
  9. * @项目名: MVP Demo
  10. * @包名: com.example.chuxiong.mvpdemo.model
  11. * @文件名: MainLoadModel
  12. * @创建者: chuxiong
  13. * @创建时间: 2017/3/21 15:53
  14. * @描述: TODO
  15. */
  16. public class MainLoadModel
  17. implements IMainLoading
  18. {
  19. @Override
  20. public void load(final IMainLoadingListener listener) {
  21. AsyncHttpClient asyncHttpClient = new AsyncHttpClient();
  22. asyncHttpClient.get("http://www.weather.com.cn/adat/sk/101010100.html",
  23. new JsonHttpResponseHandler() {
  24. @Override
  25. public void onSuccess(int statusCode,
  26. Header[] headers,
  27. JSONObject response)
  28. {
  29. super.onSuccess(statusCode, headers, response);
  30. try {
  31. MainModelBean mainModelBean = new MainModelBean();
  32. JSONObject weatherinfo = response.getJSONObject(
  33. "weatherinfo");
  34. mainModelBean.setCity(weatherinfo.optString("city"));
  35. mainModelBean.setWd(weatherinfo.optString("WD"));
  36. mainModelBean.setWs(weatherinfo.optString("WS"));
  37. mainModelBean.setTime(weatherinfo.optString("time"));
  38. //加载成功
  39. listener.loadDataSuccess(mainModelBean);
  40. } catch (JSONException e) {
  41. e.printStackTrace();
  42. }
  43. }
  44. @Override
  45. public void onFailure(int statusCode,
  46. Header[] headers,
  47. Throwable throwable,
  48. JSONObject errorResponse)
  49. {
  50. super.onFailure(statusCode, headers, throwable, errorResponse);
  51. //加载失败
  52. listener.loadDataFailure();
  53. }
  54. });
  55. }
  56. }

  1. package com.example.chuxiong.mvpdemo.presenter;
  2. /*
  3. * @项目名: MVP Demo
  4. * @包名: com.example.chuxiong.mvpdemo.presenter
  5. * @文件名: IPresenter
  6. * @创建者: chuxiong
  7. * @创建时间: 2017/3/21 16:04
  8. * @描述: TODO
  9. */
  10. public interface IPresenter<V> {
  11. void attachView(V view);
  12. void detachView();
  13. }
Presenter层:Activity/Fragment初始化Presenter层对象,调用Presenter中M层和View层通信的方法,Presenter层需要考虑两个问题:
  • 1.既然P层是M和V的桥梁,那么P层类中肯定有M和V层对象;
  •  2.P层是桥梁,那么肯定要提供方法,让M层和V层在桥梁上“对话”。
  1. package com.example.chuxiong.mvpdemo.presenter;
  2. import android.os.Handler;
  3. import com.example.chuxiong.mvpdemo.model.IMainLoading;
  4. import com.example.chuxiong.mvpdemo.model.IMainLoadingListener;
  5. import com.example.chuxiong.mvpdemo.model.MainLoadModel;
  6. import com.example.chuxiong.mvpdemo.bean.MainModelBean;
  7. import com.example.chuxiong.mvpdemo.view.IMainView;
  8. /*
  9. * @项目名: MVP Demo
  10. * @包名: com.example.chuxiong.mvpdemo.presenter
  11. * @文件名: MainPresenter
  12. * @创建者: chuxiong
  13. * @创建时间: 2017/3/21 16:02
  14. * @描述: TODO
  15. */
  16. public class MainPresenter
  17. implements IPresenter<IMainView>, IMainLoadingListener
  18. {
  19. private static final String TAG = "MainPresenter";
  20. private IMainView mIMainView;//view层
  21. private IMainLoading mIMainLoading;//model层
  22. private Handler mHandler = new Handler();
  23. public MainPresenter(IMainView IMainView) {
  24. attachView(IMainView);
  25. this.mIMainLoading = new MainLoadModel();
  26. }
  27. @Override
  28. public void attachView(IMainView view) {
  29. this.mIMainView = view;
  30. }
  31. @Override
  32. public void detachView() {
  33. this.mIMainView = null;
  34. }
  35. /**
  36. * presenter层桥梁方法:model层和view层在该方法通信
  37. */
  38. public void loadData() {
  39. mIMainView.showProgress();
  40. //制造延迟效果
  41. mHandler.postDelayed(new Runnable() {
  42. @Override
  43. public void run() {
  44. mIMainLoading.load(MainPresenter.this);
  45. }
  46. },3000);
  47. }
  48. @Override
  49. public void loadDataSuccess(MainModelBean mainModelBean) {
  50. mIMainView.showData(mainModelBean);
  51. mIMainView.hideProgress();
  52. }
  53. @Override
  54. public void loadDataFailure() {
  55. mIMainView.hideProgress();
  56. mIMainView.showLoginError();
  57. }
  58. }
View层:Activity/Fragment实现该接口,View需要考虑三个问题( 和UI相关的操作 )
  • (1)有什么操作:1.加载数据;
  • (2)这些操作结果是什么:1.加载成功,显示信息、 2.加载失败提示用户;
  • (3)有什么用户交互:1.显示滚动框、 2.隐藏滚动框。
  1. package com.example.chuxiong.mvpdemo.view;
  2. import com.example.chuxiong.mvpdemo.bean.MainModelBean;
  3. /*
  4. * @项目名: MVP Demo
  5. * @包名: com.example.chuxiong.mvpdemo.view
  6. * @文件名: IMainView
  7. * @创建者: chuxiong
  8. * @创建时间: 2017/3/21 14:39
  9. * @描述: TODO
  10. */
  11. public interface IMainView {
  12. void showData(MainModelBean mainModelBean);
  13. void showProgress();
  14. void hideProgress();
  15. /**失败提示用户*/
  16. void showLoginError();
  17. }
Activity代码:Activity/Fragment实现该View层接口,Activity/Fragment初始化Presenter层对象,调用Presenter中M层和View层通信的方法,
  1. package com.example.chuxiong.mvpdemo;
  2. import android.os.Bundle;
  3. import android.support.v7.app.AppCompatActivity;
  4. import android.view.View;
  5. import android.widget.Button;
  6. import android.widget.ProgressBar;
  7. import android.widget.TextView;
  8. import android.widget.Toast;
  9. import com.example.chuxiong.mvpdemo.bean.MainModelBean;
  10. import com.example.chuxiong.mvpdemo.presenter.MainPresenter;
  11. import com.example.chuxiong.mvpdemo.view.IMainView;
  12. public class MainActivity extends AppCompatActivity implements IMainView, View.OnClickListener {
  13. private Button mBtnLoad;
  14. private TextView mTv;
  15. private ProgressBar mProgressBar;
  16. private MainPresenter mMainPresenter;
  17. @Override
  18. protected void onCreate(Bundle savedInstanceState) {
  19. super.onCreate(savedInstanceState);
  20. setContentView(R.layout.activity_main);
  21. init();
  22. }
  23. private void init() {
  24. mBtnLoad = (Button) findViewById(R.id.load_btn);
  25. mTv = (TextView) findViewById(R.id.tv);
  26. mProgressBar = (ProgressBar) findViewById(R.id.progressBar);
  27. mBtnLoad.setOnClickListener(this);
  28. mMainPresenter = new MainPresenter(this);
  29. }
  30. @Override
  31. public void showData(MainModelBean mainModelBean) {
  32. String showData = getResources().getString(R.string.city) + mainModelBean.getCity()
  33. + getResources().getString(R.string.wd) + mainModelBean.getWd()
  34. + getResources().getString(R.string.ws) + mainModelBean.getWs()
  35. + getResources().getString(R.string.time) + mainModelBean.getTime();
  36. mTv.setText(showData);
  37. }
  38. @Override
  39. public void showProgress() {
  40. mProgressBar.setVisibility(View.VISIBLE);
  41. }
  42. @Override
  43. public void hideProgress() {
  44. mProgressBar.setVisibility(View.GONE);
  45. }
  46. @Override
  47. public void showLoginError() {
  48. Toast.makeText(this,"加载失败",Toast.LENGTH_SHORT).show();
  49. }
  50. @Override
  51. public void onClick(View v) {
  52. /* new Handler().postDelayed(new Runnable() {
  53. @Override
  54. public void run() {
  55. mMainPresenter.loadData();
  56. }
  57. }, 5000);*/
  58. mMainPresenter.loadData();
  59. }
  60. @Override
  61. protected void onDestroy() {
  62. super.onDestroy();
  63. mMainPresenter.detachView();
  64. }
  65. }


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章