从0搭建MVP架构的简易Demo

一、扯淡

MVP是在MVC架构的基础上演化来的一种架构,在MVC中Activity属于C,因此业务逻辑也都是在Activity中实现,造成Activity十分臃肿,而且使用不当容易造成内存泄漏,比如在Activity中有线程在执行耗时操作,然后点back键,销毁Activity,此时因为有线程存在,线程依然持有Activity的引用,造成Activity无法正常销毁,从而内存泄漏,MVP的主要优点是将业务层与Activity层解耦。

  • Model 业务逻辑和实体模型
  • View 对应于Activity,负责View的绘制以及与用户交互
  • Presenter 负责完成View于Model间的交互
  • Contract 这个根据具体个人习惯,起一个契约作用

这里以网络请求登录模块做一个简单的Demo,用户点击登录按钮,这一步是在View层做的,也就是Activity,然后我们去调用Presenter的登录业务接口,Presenter不需要自己去完成网络请求,而是继续调用Model层去具体实现网络请求,Model从网络拿到数据后封装成JavaBean,然后将请求结果(Javabean或者请求异常消息)告诉给Presenter,Presenter拿到结果后再次回调View层去更新UI,这就是一个简单的MVP流程,这里也很明显,Model层和View层是不直接沟通的,而是通过Presenter这个桥梁。

二、基类

BaseModel

model层就是真正业务逻辑实现的地方,当业务逻辑实现完之后需要通知Presenter层,因此BaseModel需要拿到Presenter的引用,所以在BaseModel构造方法中需要将Presenter传进来,然后还需要契约方法,让子类去实现具体的业务逻辑。

//接受P层给他的需求(基类)
public abstract class BaseModel<P extends BasePresenter, CONTRACT> {
    protected P p;

    //业务结束通过,通过presenter调用契约、合同 void responseResult(T t);

    public BaseModel(P p) {
        this.p = p;
    }

    protected abstract CONTRACT getContract();
}

BasePresenter

Presenter层首先在其构造方法中创建Model,业务逻辑不同是不同的Model,因此我们生命抽象方法getModel交给子类去创建具体的Model,另外我们需要持有View层(Activity)的引用,但是直接在构造方法中把Activity传进来这种强引用很容易因为代码不当而内存泄漏,因此采用弱引用是比较好的一种方式。

public abstract class BasePresenter<M extends BaseModel, V extends BaseView, CONTRACT> {

    protected M m;

    //绑定View层的弱引用
    private WeakReference<V> vWeakReference;

    public BasePresenter() {
        m = getModel();
    }

    public void bindView(BaseView v) {
        vWeakReference = (WeakReference<V>) new WeakReference<>(v);
    }

    public void unbindView() {
        if (vWeakReference != null) {
            vWeakReference.clear();
            vWeakReference = null;
            System.gc();
        }
    }

    //获取View  ,P->V
    public V getView() {
        if (vWeakReference != null) {
            return vWeakReference.get();
        }
        return null;
    }

    public abstract M getModel();

    //获取子类具体契约
    public abstract CONTRACT getContract();
    
}

BaseView

View层就是Activity,View层需要和Presenter层进行通讯,我们在onCreate方法中创建Presenter,因为但是具体的Presenter要根据业务逻辑不同而不同,因此我们让子类去实现,我们定义抽象方法getPresenter,并在onCreate中调用,这样View层就持有了Presenter的一个对象,同时我们调用presenter层的bindView将自身传给Presenter层,Presenter通过弱引用就持有了View层的引用,最后别忘了在onDestroy中释放Presenter层,解绑为了避免内存泄漏。

public abstract class BaseView<P extends BasePresenter, CONTRACT> extends AppCompatActivity {
    protected P p;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        p = getPresenter();

        p.bindView(this);
    }

    //P层做什么需求
    public abstract CONTRACT getContract();

    //从子类中获取具体的契约
    public abstract P getPresenter();


    //如果Presenter层出现异常,需要告知View层
    public void error(Exception e) {
    }

    @Override
    protected void onDestroy() {
        p.unbindView();
        super.onDestroy();
    }
}

三、具体实现类

契约类LoginContract

契约类就是将M、V、P层协商的共同业务封装成接口,让代码逻辑更清晰。Model层就是一个请求登录的最终实现。View层(Activity)则是处理登录的具体结果,刷新UI。Presenter层既要去调用Model层的具体登录,又要讲回调结果通知给View层。

public interface LoginContract {

    interface Model {
        //Model层子类完成方法的具体实现
        void executeLogin(String name, String psw) throws Exception;
    }

    interface View<T extends BaseEntity> {
        //项目中往往是以Javabean
        void handleResult(T t);
    }

    interface Presenter<T extends BaseEntity> {
        //登录请求(接受View层的指令,可以自己去做,也可以让model层去做)
        void requestLogin(String name, String psw);

        //结果响应(接受Model层处理的结果,通知View层去刷新)
        void responseResult(T t);
    }

}

LoginModel

Model类需要实现具体具体登录的契约。

public class LoginModel extends BaseModel<LoginPresenter, LoginContract.Model> {


    public LoginModel(LoginPresenter loginPresenter) {
        super(loginPresenter);
    }

    @Override
    protected LoginContract.Model getContract() {
        return new LoginContract.Model() {
            @Override
            public void executeLogin(String name, String psw) throws Exception {
                if ("123".equals(name) && "123".equals(psw)) {
                    p.getContract().responseResult(new UserInfo("123", "123"));
                } else {
                    p.getContract().responseResult(null);
                }
            }
        };
    }
}

LoginPresenter

首先实现基类的getModel方法,创建LoginModel,并将自己传给LoginModel。然后实现自己的契约,在请求登录的实现中调用Model层的登录方法,并在拿到Model层的结果后调用View层的处理方法。

public class LoginPresenter extends BasePresenter<LoginModel, LoginActivity, LoginContract.Presenter> {
    @Override
    public LoginModel getModel() {
        return new LoginModel(this);
    }

    @Override
    public LoginContract.Presenter getContract() {
        return new LoginContract.Presenter<UserInfo>() {
            @Override
            public void requestLogin(String name, String psw) {
                try {
                    //3种风格,P层很极端,要不不做事制作转发,要不拼命一个人干活,Google的MVPDemo中在P端做了所有的业务逻辑
                    m.getContract().executeLogin(name, psw);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

            @Override
            public void responseResult(UserInfo userInfo) {
                getView().getContract().handleResult(userInfo);
            }

        };
    }
}

LoginActivity

LoginActivity层首先实现父类的抽象方法getPresenter创建LoginPresenter,然后在按钮点击后调用P层的登录接口,并在请求结果返回的回调中刷新UI。

public class LoginActivity extends BaseView<LoginPresenter, LoginContract.View> {

    private EditText etCount;
    private EditText etPwd;
    private Button btnLongin;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);

        initView();
        initListener();

    }

    private void initView() {
        etCount = findViewById(R.id.et_count);
        etPwd = findViewById(R.id.et_password);
        btnLongin = findViewById(R.id.btn_login);


    }

    private void initListener() {
        btnLongin.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String name = etCount.getText().toString();
                String psw = etPwd.getText().toString();
                p.getContract().requestLogin(name, psw);
            }
        });
    }


    @Override
    public LoginContract.View getContract() {
        return new LoginContract.View<UserInfo>() {
            @Override
            public void handleResult(UserInfo userInfo) {
                if (userInfo != null) {
                    Toast.makeText(LoginActivity.this, userInfo.toString(), Toast.LENGTH_SHORT).show();
                } else {
                    Toast.makeText(LoginActivity.this, "登录失败", Toast.LENGTH_SHORT).show();
                }
            }
        };
    }

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

最后补充上两个JavaBean的代码,比较简单

public class BaseEntity {
    private int code;
    private boolean success;
    private String error;

    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    public boolean isSuccess() {
        return success;
    }

    public void setSuccess(boolean success) {
        this.success = success;
    }

    public String getError() {
        return error;
    }

    public void setError(String error) {
        this.error = error;
    }
}



public class UserInfo extends BaseEntity {
    private String name;
    private String company;

    public UserInfo() {
    }

    public UserInfo(String name, String company) {
        this.name = name;
        this.company = company;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getCompany() {
        return company;
    }

    public void setCompany(String company) {
        this.company = company;
    }

    @Override
    public String toString() {
        return "UserInfo{" +
                "name='" + name + '\'' +
                ", company='" + company + '\'' +
                '}';
    }
}

一个简易版的MVP Demo就完成了。

Demo地址:https://github.com/987570437/MVPDemo

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值