I spent the last few days working on how to build a solid and practical MVP architecture as a basis for rapid development. I have been struggling with how to reuse the Presenter layer for a long time. After looking up a lot of information on the Internet, I still haven’t found a suitable method. Some writing methods are simply for the reuse of Presenter, but add burden to other modules. Some implementations are too rigid to follow the principles of writing code. I have an idea of how to implement presenter reuse, but I don’t know if it’s feasible. I’ll post it when I’m done.

Start O(∩_∩)O Github project address

In this article, you can learn the basic framework of MVP:

  • What does a decent Mvp framework look like
  • How does the Mvp framework avoid memory leaks
  • How to reuse the Presenter layer?We’ll masturbate later when we know this one works

Follow my train of thought to once again, first construct base class: prime minister is to View layer base class start, IBaseView

package com.example.administrator.mvpframedemo.base;

public interface IBaseView {

}
Copy the code

To be honest, I haven’t defined a method for this. I don’t agree with showToast() because it’s too repetitive and I think it’s too cumbersome to put it in the base class and let each subclass implement it, so what should I do? I put repeated implementations like showToast in BaseActivity; Let’s take a look at BaseActivity

public abstract class BaseActivity extends AppCompatActivity {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        initBeforeCreate();
        super.onCreate(savedInstanceState);
        setContentView(getLayoutId());
        init(savedInstanceState);
        initData();
        initView();
        logic();
    }

    protected abstract void initBeforeCreate();

    protected abstract int getLayoutId();

    protected abstract void init(Bundle savedInstanceState);

    protected abstract void initData();

    protected abstract void initView();

    protected abstract void logic();

    protected void showToast (String toastStr) {
        Toast.makeText(this, toastStr, Toast.LENGTH_SHORT).show();
    };
}
Copy the code

This is nothing to say, but the release is more baseactivity-related than MVP-related. As for the BaseActivity that you know handles presenter binding, I’m going to create a separate BaseMvpActivity for Mvp handling. It feels refreshing to keep the structure clean; Of course, BaseMvpActivity inherits from BaseActivity; In case I receive this module every day to use MVC and so on, it is good to deal with a little bit. Next up is BasePresenter

public class BasePresenter<V extends IBaseView>  {

    private WeakReference<V> mViewRef;
    public V mView;

    public void attachView(V view) {
        mViewRef = new WeakReference<V>(view);
        mView = mViewRef.get();
    }

    public void detachView() { mViewRef.clear(); mView = null; }}Copy the code

AttachView () and detachView() are defined; Why is that? And why do we have mViewRef? First, why attachView()? As you’ll know if you’ve ever typed some simple MVP code, each time you write something like this:

MainActivity{// This line of code does two things, ①View layer creates its own suitable Presenter, Mainactivity. this = new Presenter(mainActivity. this); }Copy the code

AttachView () is what completes ②, and ① is left to the View layer to implement, as we’ll see later. So we know why attachView() was born, but what about the detachView() design? DetachView () and mViewRef exist to solve memory leaks. So how do you solve memory leaks? When an Activity exits while being displayed, the GC will want to reclaim the Activity if it feels memory is tight, but the Presenter holds the Activity object, so the GC cannot reclaim it, and there is a risk of leakage. This holding of activity objects is a very common cause of memory leaks. So we use deacttach() and mViewRef(weak reference) to unbind the two and let the GC do whatever it wants. So when do you unbind them? This is appropriate during the Activity’s onDestroy() life cycle. So next up: BaseMvpActivity

public abstract class BaseMvpActivity<T extends BasePresenter> extends BaseActivity implements IBaseView {

    protected T mPresenter;

    @Override
    protected void init(Bundle savedInstanceState) {
        mPresenter = bindPresenter();
        mPresenter.attachView(this);
    }

    protected abstract T bindPresenter();

    @Override
    protected void onDestroy() { super.onDestroy(); mPresenter.detachView(); }}Copy the code

The BaseMvpActivity does some things for the Mvp, including defining bindPresenter() to create its own suitable presenter, and then executing Presby.attachView (this) to bind the two. Finally, you touch the binding in the onDestroy() method.

— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — artificial line — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — here. The base class design for Mvp seems to be about right. Step 1: Create a LoginConstract in constract (the contract layer, which maintains the relationship between layers P and V)

public interface LoginContract { abstract class LoginPresenter extends BasePresenter<LoginView>{ public abstract void login(String name, String password); } interface LoginView extends IBaseView { void showTips(String str); }}Copy the code

Define LoginPresenter and LoginView interfaces in the contract; The implementation goes elsewhere. For example: First View uses Presetner to initiate a login request. After the request is completed, View will show the result to the user (showTips). So the interface defined above also comes from this. Implementation: LoginPresenter:

public  class LoginPresenter extends LoginContract.LoginPresenter {
    ILoginModel loginModel;
    @Override
    public void login(String name, String password) {
        loginModel = new LoginModel();
        loginModel.login(name, password, new LoginCallBack());
    }

    private class LoginCallBack implements ICallBack<LoginDomain, Exception> {

        @Override
        public void onSuccess(LoginDomain result) {
            mView.showTips("Login successful");
        }

        @Override
        public void onFail(Exception error) {
            mView.showTips("Login failed"); }}}Copy the code

Simply put, the login() method needs to use the Model layer to help fetch the data. So instantiate the appropriate Model and call its interface for fetching data. I generally divide the Model layer into interface and IMPL layers, one defining interface and one implementing. A word of caution about presenters’ interaction with the Model layer: Because the Model layer does not asynchronously retrieve data, interface callbacks are usually used. So a callback object is passed when login () of Model is called to fetch data to implement asynchrony. LoginModel:

public class LoginModel implements ILoginModel { @Override public void login(String username, String password, ICallBack<LoginDomain, Exception> callBack) {// Try {thread.sleep (2000); callBack.onSuccess(new LoginDomain("Chow Yun-fat"."123")); } catch (InterruptedException e) { e.printStackTrace(); callBack.onFail(new Exception()); }}}Copy the code

There are only a few details here, such as how the ICallBack callbacks for Presenter and model are standardized. Most importantly, how do Presenter reuse? Stay tuned for my next blog post.

Finally, the directory structure of my project: