我的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. }


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