Android框架模式MVP

第一、基本概念

MVP是模型(Model)、视图(View)、主持人(Presenter)的缩写,分别代表项目中3个不同的模块。 

模型(Model):负责处理数据的加载或者存储,比如从网络或本地数据库获取数据等;

视图(View):负责界面数据的展示,与用户进行交互;

主持人(Presenter):相当于协调者,是模型与视图之间的桥梁,将模型与视图分离开来。

     MPV 是从经典的MVC模式演变过来的,其基本思路都是相通的。其中M是model模型,提供业务数据;P和MVC中的C担当的角色相似,是Presenter主持人,进行逻辑处理。V是View视图,显示页面操作。

     在MVP中View并不直接使用Model,它们之间的通信是通过Presenter 来进行的,所有的交互都发生在Presenter这个类内部,在代码中可以发现Presenter类既有View对象,又有Model对象,但是在Activity中是没有Model对象的,是使用Presenter对象来调用Model对象;Activity是要有View对象和Presenter对象的,Presenter对象怎么来呢,这个是要在构造方法中去创建。View对象怎么来呢,其实是Activity实现这个View,这个Activity就相对于这个View对象,并且需要重写View接口的方法!而且还有一个重要的就是要用这个Presenter来绑定Activity(其实是为了绑定View接口的方法)。这样在Presenter用model处理完数据后,Presenter就会调用View接口的方法,实现页面的操作。  

如下图所示,View与Model并不直接交互,而是使用Presenter作为View与Model之间的桥梁。其中Presenter中同时持有Viwe层以及Model层的Interface的引用,而View层持有Presenter层Interface的引用。当View层某个界面需要展示某些数据的时候,首先会调用Presenter层的某个接口,然后Presenter层会调用Model层请求数据,当Model层数据加载成功之后会调用Presenter层的回调方法通知Presenter层数据加载完毕,最后Presenter层再调用View层的接口将加载后的数据展示给用户。这就是MVP模式的整个核心过程。 
s

         这样分层的好处就是大大减少了Model与View层之间的耦合度。一方面可以使得View层和Model层单独开发与测试,互不依赖。另一方面Model层可以封装复用,可以极大的减少代码量。

注意:之前我也是有这样一个疑问:view和ui这两个不是都是放视图的包吗?

         其实是这样的:MVP中的View的包下放的并不是activity这样的视图类,而是activity视图类的操作,比如这个登录的activity下,会有获得用户的账号,密码的需要,也有点击登录的需要等等,这就需要定义这几个操作的方法,其实这里view包下定义的是接口类,里面的方法都是抽象方法,定义这个页面需要的操作,具体页面内容的实现还是要在Activity中来!

2.mvp包下的文件设计

  (1)model包名下的类

       设计数据处理的逻辑,这里一般还设计一个内部接口类,让presenter对象可以设置监听并得到数据。因为好多数据处理都是需要子线程的,不能马上就返回数据。

  (2)view包名下的类

       设计视图中需要的逻辑,都是接口方法,该类也是接口类。这个接口类也是需要固定的Activity页面来实现,并重写里面的方法,在方法里面写入页面的操作,这些重写方法方法能否得到回调都是Presenter对象来控制的,一般都是数据或逻辑处理完后再判断相应的方法回调。

  (3)presenter包名下的类

           设计activity和model的交互,既要有View对象,也要有model对象。

         Activity中把数据相关的逻辑操作都扔给了Presenter去做,View只负责处理与用户界面操作。而Presenter调用Model处理完数据之后,再通过View接口方法更新View显示的信息。

第二、在项目中的使用

项目地址:https://github.com/lantian0314/MVP

2.1 布局文件

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <EditText
        android:id="@+id/et_username"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="请输入用户名" />

    <EditText
        android:id="@+id/et_userpwd"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="请输入密码"
        android:inputType="textPassword"
        />

    <Button
        android:id="@+id/btn_login"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="loginClick"
        android:text="登录" />
</LinearLayout>

2.2 Activity文件

/**
 * Created by xingyatong on 2018/3/13.
 * Model层的定义
 */
public class LoginModel {

    /**
     * 处理登录逻辑判断,验证用户名以及密码
     *
     * @param user
     * @param onLoginResultListener
     */
    public void Login(User user, onLoginResultListener onLoginResultListener) {
        String name = user.getUserName();
        String pwd = user.getUserPwd();
        if (name.equals("mvp") && pwd.equals("123456")) {
            if (onLoginResultListener != null) {
                onLoginResultListener.loginSuccess(user);//登陆成功的回调
            }
        } else {
            if (onLoginResultListener != null) {
                onLoginResultListener.loginFail();//登陆失败
            }
        }

    }


    /**
     * 网络检查后的回调
     */
    public interface onLoginResultListener {
        void loginSuccess(User user);

        void loginFail();
    }
}


/**
 * Created by xingyatong on 2018/3/13.
 * mvp中Presenter中的设计
 * 也是比较难,需要重点理解的一个
 * presenter是主持人的意思,view和model的中间者
 * 需要同时要有View的对象和Model的对象!一般做法是:在构造方法中创建model对象,并创建一个方法绑定View接口
 * 这里可以发现数据处理后或者逻辑判断完后都是给mvp中的View对象来做操作的!
 */
public class LoginPresenter extends BasePresenter {

    private LoginModel loginModel;
    private LoginView loginView;
    private User user;

    public LoginPresenter() {
        loginModel = new LoginModel();
        user = new User();
    }

    @Override
    public void bindView(Object mvpview) {
        super.bindView(mvpview);
        loginView = (LoginView) mvpview;
    }

    public void login() {
        user.setUserName(loginView.getUserName());
        user.setUserPwd(loginView.getUserPwd());
        if (isParamsOk()) {
            loginModel.Login(user, new LoginModel.onLoginResultListener() {
                @Override
                public void loginSuccess(User user) {
                    if (loginView != null) {
                        loginView.loginSuccess(user);
                    }
                }

                @Override
                public void loginFail() {
                    if (loginView != null) {
                        loginView.showVerifyFail();
                    }
                }
            });
        }
    }

    private boolean isParamsOk() {
        if (TextUtils.isEmpty(user.getUserName()) || TextUtils.isEmpty(user.getUserPwd())) {
            if (loginView != null) {
                loginView.showVerifyFail();
            }
        }
        return true;
    }
}


/**
 * Created by xingyatong on 2018/3/13.
 */
public class BasePresenter {
    public void bindView(Object mvpview) {
    }
}

/**
 * Created by xingyatong on 2018/3/13.
 */
public interface LoginView {

    String getUserName();

    String getUserPwd();

    void loginSuccess(User user);

    void showNetEror();//网络错误

    void showVerifyFail();//信息验证失败
}


**
 * Created by xingyatong on 2018/3/13.
 * 这里需要创建presenter对象,presenter对象中是有view对象和model对象的!
 */
public class LoginActivity extends AppCompatActivity implements LoginView {

    @BindView(R.id.et_username)
    EditText et_username;
    @BindView(R.id.et_userpwd)
    EditText et_userpwd;
    @BindView(R.id.btn_login)
    Button btn_login;

    private LoginPresenter loginPresenter;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.login_main);
        ButterKnife.bind(this);
        loginPresenter = new LoginPresenter();
        loginPresenter.bindView(LoginActivity.this);//绑定View和Presenter,因为这个Activity已经实现了接口,已经包含了View对象
    }

    @Override
    public String getUserName() {
        return et_username.getText().toString();
    }

    @Override
    public String getUserPwd() {
        return et_userpwd.getText().toString();
    }

    @Override
    public void loginSuccess(User user) {
        Toast.makeText(LoginActivity.this, "登陆成功", Toast.LENGTH_SHORT).show();
        Intent intent = new Intent(LoginActivity.this, MainActivity.class);
        startActivity(intent);
    }

    @Override
    public void showNetEror() {

    }

    @Override
    public void showVerifyFail() {
        Toast.makeText(LoginActivity.this, "验证失败", Toast.LENGTH_SHORT).show();
    }

    @OnClick({R.id.btn_login})
    public void loginClick(Button button) {
        switch (button.getId()) {
            case R.id.btn_login:
                //需要对登录的数据验证判断,用到presenter
                loginPresenter.login();
                break;
        }
    }
} 

第三、性能优化

       Presenter经常执行一些耗时的操作,而presenter持有Activity的强引用,如果在请求结束之前Activity被销毁,网络请求还没有返回,导致presenter一直持有activity的对象,使得activity对象无法被回收,此时发生内存泄漏,因此要对persenter基类进行弱引用

/**
 * Created by xingyatong on 2018/3/13.
 */
public class BasePresenter<T> {
    private Reference<T> mReference;

    public void bindView(T mvpview) {
        //建立关联
        mReference = new WeakReference<T>(mvpview);
    }

    public T getView() {
        return mReference.get();
    }

    /**
     * 接触弱引用关联
     */
    public void detechView() {
        if (mReference != null) {
            mReference.clear();
            mReference = null;
        }
    }
}
public class LoginPresenter extends BasePresenter<LoginView> {

    private LoginModel loginModel;
    private LoginView loginView;
    private User user;

    public LoginPresenter() {
        loginModel = new LoginModel();
        user = new User();
    }

    public void login() {
        loginView = getView();
        user.setUserName(loginView.getUserName());
        user.setUserPwd(loginView.getUserPwd());
        if (isParamsOk()) {
            loginModel.Login(user, new LoginModel.onLoginResultListener() {
                @Override
                public void loginSuccess(User user) {
                    if (loginView != null) {
                        loginView.loginSuccess(user);
                    }
                }

                @Override
                public void loginFail() {
                    if (loginView != null) {
                        loginView.showVerifyFail();
                    }
                }
            });
        }
    }

    private boolean isParamsOk() {
        if (TextUtils.isEmpty(user.getUserName()) || TextUtils.isEmpty(user.getUserPwd())) {
            if (loginView != null) {
                loginView.showVerifyFail();
            }
        }
        return true;
    }

@Override
    protected void onDestroy() {
        super.onDestroy();
        loginPresenter.detechView();
    }


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值