MVP框架学习系列

初识MVP模式

2018-05-26  本文已影响0人  肚皮怪_Sun

记得自己刚开始的时候学习MVP模式的时候,在网上扒拉了各种各种的文章,下载各种高start的Demo,不管三七二十对葫芦画瓢一步步搭建自己的项目。开始时其实也不是特别懂,每次按照模式化的步骤写V层、M层、P层,一步步写自己项目(其实内心觉得前期的准备工作好繁琐)。可能是写的熟练了,遇到了一些问题,慢慢的也一些想法,后来就想设计一个自己使用框架。
这个系列的文章也是把自己最近学习内容的一个汇总,以此自勉,继续前行。
那下面就开始正题了,我相信大家也看过不少关于MVP模式的文章,其中关于各个层的关系及其作用内容的讲解,并且列了一大堆图解说明,这些我们直接绕过去,直接上代码我想比什么都更直接更直观:

首先定义M层:

public interface MvpModel {
}

然后是V层:

public interface MvpView {
}

在定义一个P层对View的绑定和解绑,内存上的优化:

public interface MvpPresenter<V extends MvpView> {

void attachView(V view);//View的绑定

void detachView();//View的解绑

}

接下来我要实现一个具体的P层绑定:

 public class MvpBasePresenter<V extends MvpView> implements MvpPresenter<V> {

      private V view;

      public V getView() {
        return view;
      }

      @Override public void attachView(V view) {
        this.view = view;
      }

      @Override public void detachView() {
        this.view = null;
      }
    }

MvpBasePresenter中我们先这样简单的处理后面再优化这块代码,比如view使用弱引用、动态代理对view的判空处理等。
刚刚写的这一段代码,其实就是一个中介者模式,P层就是个抽象的中介者对象,V和M就是抽象同事对象,根据具体的业务实现的M和V就是具体的同事对象,用P层隔离M和V彼此不知道对方,以达到解耦的作用。M—V—P各自的角色作用以及之间的关系就很明朗了。

我们通过一件登陆的案例来实现刚刚写的代码:

V层->抽象同事B

public interface LoginView extends MvpView {

   void onLoginResult(String result);

}

M层->具体同事A

public class LoginModel implements MvpModel {

    public String login(String username, String password) {
        if (username.equals("wds") && password.equals("123456")) {
             return "登陆成功";
        } else {
             return "登陆失败";
        }

    }
 }

P->具体的中介

public class LoginPresenter extends MvpBasePresenter<LoginView> {

    // 持有同事引用
    // 两个同事:M层、V层
    private LoginModel model;

    public LoginPresenter() {
        this.model = new LoginModel();
    }

    public void login(String username, String password) {
        String loginState = this.model.login(username, password);
        this.getView().onLoginResult(loginState);

    }

}

在Activity中使用

public class MainActivity extends AppCompatActivity implements LoginView {

    private LoginPresenter  loginPresenter;

    private EditText        et_name;

    private EditText        et_password;

    @Override protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        et_name = findViewById(R.id.et_name);
        et_password = findViewById(R.id.et_password);
        findViewById(R.id.login).setOnClickListener(new View.OnClickListener() {

            @Override public void onClick(View v) {
                loginPresenter.login(et_name.getText().toString(), et_password.getText().toString());
            }
        });

        loginPresenter = new LoginPresenter();//创建P成
        loginPresenter.attachView(this);//绑定
    }

    @Override public void onLoginResult(String result) {
        Log.e("TAG", "result :" + result);
    }

    @Override protected void onDestroy() {
        super.onDestroy();
        loginPresenter.detachView();//解绑
    }
}

这样就完成了一个简单的MVP模式,在M层处理数据如http请求、数据库查询,在P层管理M和V处理业务逻辑,其实有时候会省略M层,直接在P层处理数据,但我个人还是习惯写M层。写到这里其实已经把MVP最重要的内容(,M和V各自处理自己的内容,通过P层关联M和V,以达到解耦作用),但如果我们在实际的开发中这样写代码,估计要吐血,步骤很繁琐,重复操作很多,这就不是一个合格的程序猿该做的事。

接下就对刚刚写的内容做一个简单的封装和优化,抽取一个抽象的MvpActivity,把一些共同的操作都放在这里统一处理。

在使用P层的时候,有两个重要的步骤,一个绑定(loginPresenter.attachView(this))和解绑(loginPresenter.detachView()),需要在每个使用P的Activity都要进行的操作。
如下面这代码:

public abstract class MvpActivity<V extends MvpView, P extends MvpPresenter<V>> extends AppCompatActivity implements MvpView {

    private P presenter;

    public P getPresenter() {
        return presenter;
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (presenter == null){
            this.presenter = createPresenter();
        }
        if (presenter != null){
            this.presenter.attachView((V)this);
        }
    }

    public abstract P createPresenter();

    @Override
    protected void onDestroy() {
        super.onDestroy();
        this.presenter.detachView();
    }
}

代码很简单,定义一个抽象方法createPresenter,通过向下的泛型设计,返回值为继承了MvpPresenter的Presenter,在onCreate中创建P,并做了attachView绑定,在onDestroy执行detachView解绑。

在MvpBasePresenter的attachView方法中,通过动态代理对view判空进行统一的处理,这样就不用在每次使用view前进行不等于空的处理,代码如下:

public class MvpBasePresenter<V extends MvpView> implements MvpPresenter<V> {

    //弱引用
    private V viewProxy;
    private V view;

    public V getView() {
        return viewProxy;
    }

    @Override
    public void attachView(V view) {
        this.view = view;
        //目标接口->实际上是MvpView
        Class<?>[] interfaces = view.getClass().getInterfaces();
        try {
            viewProxy = (V)Proxy.newProxyInstance(view.getClass().getClassLoader(), interfaces, new ProxyInvocationHandler<V>(view));
        } catch (Exception e){
            e.printStackTrace();
        }

    }

    @Override
    public void detachView() {
        this.view = null;
    }

    @Override
    public void destory() {

    }

    private class ProxyInvocationHandler<V extends MvpView> implements InvocationHandler {

        private V view;
        public ProxyInvocationHandler(V view){
            this.view = view;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            if (view != null){
                //执行
                return method.invoke(view, args);
            }
            //不存在->报错了
            throw new NullPointerException("空异常");
        }
    }

}

最后在A具体ctivity的实现由原来的代码就变成如下:

public class MainActivity extends MvpActivity<LoginView,LoginPresenter> implements LoginView {


    private EditText        et_name;

    private EditText        et_password;

    @Override protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        et_name = findViewById(R.id.et_name);
        et_password = findViewById(R.id.et_password);
        findViewById(R.id.login).setOnClickListener(new View.OnClickListener() {

            @Override public void onClick(View v) {
                getPresenter().login(et_name.getText().toString(), et_password.getText().toString());
            }
        });

    }

    @Override public LoginPresenter createPresenter() {
        return new LoginPresenter();
    }

    @Override public void onLoginResult(String result) {
        Log.e("TAG", "result :" + result);
    }

}

今天的内容就到这了😁😁


风后面是风,天空上面是天空,而你的生活可以与众不同

上一篇 下一篇

猜你喜欢

热点阅读