Android开发模式之MVP

一、 什么是MVP

MVP 是从经典的模式MVC演变而来,它们的基本思想有相通的地方:Controller/Presenter负责逻辑的处理,Model提供数据,View负责显示。

二、MVP与MVC的区别

作为一种新的模式,MVP与MVC有着一个重大的区别:在MVP中View并不直接使用Model,它们之间的通信是通过Presenter (MVC中的Controller)来进行的,所有的交互都发生在Presenter内部,而在MVC中View会从直接Model中读取数据而不是通过 Controller。


MVC模式

MVP模式

三、Android中的MVP

代码案例:Login MVP

  • Model

    /**  模型层 ——— 登录接口  **/
    public interface ILoginModel {    
       void login(String username, String password, LoginCallBack callBack);
    }
    /**  模型层 ——— 完成具体的数据操作。  **/
    public class LoginModel implements ILoginModel {    
    
       @Override    
       public void login(String username, String password, LoginCallBack callBack) {        
          if (username.equals("MVP") && password.equals("MVP")){            
              callBack.onLoginSuccess();        
          }else{            
              callBack.onLoginFail();        
          }    
       }
    }
  • View

    /**  视图层 ——— 视图操作接口     **/
    public interface ILoginView {    
    
       void initView();   
    
       void onUsernameEmpty();   // 用户名为空时的显示操作    
       void onPasswordEmpty();   // 密码为空时的显示操作    
       void onLoginSuccess();   // 登陆成功时的显示操作    
       void onLoginFail();   // 密码失败时的显示操作
    }
    /**  视图层 ——— 只是作为接受用户数据和展示数据的方式     **/
    public class LoginView extends LinearLayout implements ILoginView, View.OnClickListener{  
    
       private Context context;    
       private EditText mUsername;    
       private EditText mPassword;    
       private Button mLoginBtn;    
    
       private ILoginPresenter presenter;
    
       public LoginView(Context context, AttributeSet attrs) {        
          super(context, attrs);        
          this.context = context;    
       }    
    
       @Override
       public void initView(){        
          mUsername = (EditText) findViewById(R.id.et_username);        
          mPassword = (EditText) findViewById(R.id.et_password);        
          mLoginBtn = (Button) findViewById(R.id.btn_login);    
    
          presenter = new LoginPresenter(this);
       }    
    
       @Override
       public void onClick(View v) {    
          switch (v.getId()){        
             case R.id.btn_login:            
                presenter.login(mUsername.getText().toString(), 
                                mPassword.getText().toString());            
                break;    
           }
       }
    
       @Override
       public void onUsernameEmpty() {        
          Toast.makeText(context, "用户名不能为空", Toast.LENGTH_SHORT).show();    
       } 
    
       @Override
       public void onPasswordEmpty() {        
          Toast.makeText(context, "密码不能为空", Toast.LENGTH_SHORT).show();    
       }
    
       @Override
       public void onLoginSuccess() {        
          Toast.makeText(context, "登录成功", Toast.LENGTH_SHORT).show();    
       }
    
       @Override
       public void onLoginFail() {       
          Toast.makeText(context, "登录失败", Toast.LENGTH_SHORT).show();    
       }
    }
  • Presenter

    public interface ILoginPresenter {    
       void login(String username, String password);
    }
    public class LoginPresenter implements ILoginPresenter, LoginCallBack{
    
       private ILoginModel loginModel;    
       private ILoginView loginView;    
    
       public LoginPresenter(ILoginView loginView){        
          loginModel = new LoginModel();        
          this.loginView = loginView;    
       }    
    
       @Override    
       public void login(String username, String password) {        
          if (TextUtils.isEmpty(username)){            
               loginView.onUsernameEmpty();        
          }else if (TextUtils.isEmpty(password)){            
               loginView.onPasswordEmpty();        
          }else {            
               loginModel.login(username, password, this);        
          }    
       }    
    
       @Override    
       public void onLoginSuccess() {        
          loginView.onLoginSuccess();    
       }   
    
       @Override    
       public void onLoginFail() {        
          loginView.onLoginFail();    
       }
    }
  • else

    /**  此时Activity就变为了承载视图层的容器。 **/
    public class MainActivity extends AppCompatActivity {    
    
       private LoginView loginview;    
    
       @Override    
       protected void onCreate(Bundle savedInstanceState) {        
          super.onCreate(savedInstanceState);        
          setContentView(R.layout.activity_main);        
          initPresenter();    
       }    
    
       private void initPresenter(){        
          loginview = (LoginView) findViewById(R.id.loginview);        
          loginview.initView(); 
       }
    }
    <!--  将LoginView作为父容器  -->
    <com.scnu.zhou.mvc.view.LoginView 
        xmlns:android="http://schemas.android.com/apk/res/android"    
        android:layout_width="match_parent"    
        android:layout_height="match_parent"    
        android:paddingBottom="@dimen/activity_vertical_margin"    
        android:paddingLeft="@dimen/activity_horizontal_margin"    
        android:paddingRight="@dimen/activity_horizontal_margin"    
        android:paddingTop="@dimen/activity_vertical_margin"    
        android:orientation="vertical"    
        android:id="@+id/loginview">    
    
        <EditText        
            android:id="@+id/et_username"        
            android:layout_width="match_parent"        
            android:layout_height="wrap_content"        
            android:inputType="text"/>    
    
        <EditText        
            android:id="@+id/et_password"        
            android:layout_width="match_parent"        
            android:layout_height="wrap_content"        
            android:layout_marginTop="10dp"        
            android:inputType="textPassword"/>    
    
        <Button        
            android:id="@+id/btn_login"        
            android:layout_width="match_parent"        
            android:layout_height="50dp"        
            android:text="Login"        
            android:layout_marginTop="10dp"/>
    </com.scnu.zhou.mvc.view.LoginView>

    三、 MVP模式的优缺点

优点:

1、模型与视图完全分离,我们可以修改视图而不影响模型
2、可以更高效地使用模型,因为所有的交互都发生在一个地方——Presenter内部
3、我们可以将一个Presenter用于多个视图,而不需要改变Presenter的逻辑。这个特性非常的有用,因为视图的变化总是比模型的变化频繁。
4、如果我们把逻辑放在Presenter中,那么我们就可以脱离用户接口来测试这些逻辑(单元测试)

缺点:

由于对视图的渲染放在了Presenter中,所以视图和Presenter的交互会过于频繁。还有一点需要明白,如果Presenter过多地渲染了视图,往往会使得它与特定的视图的联系过于紧密。一旦视图需要变更,那么Presenter也需要变更了。比如说,原本用来呈现Html的Presenter现在也需要用于呈现Pdf了,那么视图很有可能也需要变更。

posted @ 2017-04-29 14:48  天涯海角路  阅读(120)  评论(0)    收藏  举报