EasyMvp

A simple, powerful and flexible MVP framework. Project address: EasyMvp

The characteristics of

  • An Activity can be bound to multiple presenters to maximize reuse.
  • Using annotations to implement dependency injection to reduce coupling.
  • The lifecycle can be flexibly managed.
  • Easy to use
  • Use examples can be found in the project.

Method of use

(Take simple login registration as an example)

  • The case of a single Presenter
  1. Define your View layer interface methods and inherit a public BaseMvpView such as LoginView:
public interface LoginView extends BaseMvpView {
    void loginSuccess(a);
}


Copy the code
  1. Write LoginPresenter to inherit from BasePresenter and implement your Presenter functionality methods:
public class LoginPresenter extends BasePresenter<LoginView> {

    public void login(a) { mView.loginSuccess(); }}Copy the code
  1. The Activity inherits from BaseMvpActivity and implements your View interface:
@CreatePresenter(presenter = LoginPresenter.class)
public class ExampleActivity3 extends BaseMvpActivity<LoginPresenter> implements LoginView {

    @Override
    protected int getContentView(a) {
        return R.layout.activity_main;
    }

    @Override
    public void init(a) {
        getPresenter().login();
    }

    @Override
    public void loginSuccess(a) {
        Log.i("ExampleActivity1"."Login successful"); }}Copy the code

In the Activity, a Presenter instance can be fetched by getPresenter as shown in the code above. This method requires the generic parameter BaseMvpActivity to be followed by your Presenter implementation class. For example, the LoginPresenter shown above. In addition to this method, instances can also be obtained via annotations:

@CreatePresenter(presenter = LoginPresenter.class)
public class ExampleActivity3 extends BaseMvpActivity implements LoginView {
    @PresenterVariable
    private LoginPresenter mLoginPresenter;

    @Override
    protected int getContentView(a) {
        return R.layout.activity_main;
    }

    @Override
    public void init(a) {
        mLoginPresenter.login();
    }

    @Override
    public void loginSuccess(a) {
        Log.i("ExampleActivity1"."Login successful"); }}Copy the code

If you don’t like annotations, you can also get them directly:

@CreatePresenter(presenter = LoginPresenter.class)
public class ExampleActivity3 extends BaseMvpActivity implements LoginView {

    private LoginPresenter mLoginPresenter;

    @Override
    protected int getContentView(a) {
        return R.layout.activity_main;
    }

    @Override
    public void init(a) {
      mLoginPresenter = getPresenterProviders().getPresenter(0);
      mLoginPresenter.login();
    }

    @Override
    public void loginSuccess(a) {
        Log.i("ExampleActivity1"."Login successful"); }}Copy the code

GetPresenterProviders ().getPresenter(int Index). The parameter is the array index of the class object that you pass in via the @createPresenter annotation. There’s only one Presenter, so just pass 0.

  • Multiple presenters

In the case of multiple presenters, the first two steps are the same, with some differences in the Activity binding:

@CreatePresenter(presenter = {LoginPresenter.class, RegisterPresenter.class})
public class ExampleActivity1 extends BaseMvpActivity implements  LoginView.RegisterView  {

    @PresenterVariable
    private LoginPresenter mLoginPresenter;
    @PresenterVariable
    private RegisterPresenter mRegisterPresenter;

    @Override
    protected int getContentView(a) {
        return R.layout.activity_main;
    }

    @Override
    public void init(a) {
        // It is also possible to obtain instances this way
        //mLoginPresenter = getPresenterProviders().getPresenter(0);
        //mRegisterPresenter = getPresenterProviders().getPresenter(1);

        mLoginPresenter.login();
        mRegisterPresenter.register();
    }

    @Override
    public void loginSuccess(a) {
        Log.i("ExampleActivity1"."Login successful");
    }

    @Override
    public void registerSuccess(a) {
        Log.i("ExampleActivity1"."Registration successful"); }}Copy the code

As shown above, if an Activity needs to use both LoginPresenter and RegisterPresenter methods, just pass in their class objects using the @createPresenter annotation to complete the binding. Similarly, to get an instance of each Presenter, you can use the @PresenterVariable annotation. If you prefer not to use the annotation, It can also be obtained via getPresenterProviders().getPresenter(int Index).

  • Where Mvp is not needed

Mvp is not needed everywhere, and when it’s not needed, it’s not so special, it’s just used:

public class ExampleActivity4 extends BaseMvpActivity {
    @Override
    protected int getContentView(a) {
        return R.layout.activity_main;
    }

    @Override
    public void init(a) {}}Copy the code

Write BasePresenter, BaseMvpView, BaseMvpActivity and other basic classes

BasePresenter, BaseMvpView, BaseMvpActivity and other basic classes are used in the above example. Here is an example that users can write according to their own needs.

  • BaseMvpView

BaseMvpView is the basic View layer interface:

public interface BaseMvpView {
    void showError(String msg);

    void complete(a);

    void showProgressUI(boolean isShow);
}

Copy the code

As you can see, it defines some common interface methods. In fact, this interface may not be required, but generally for convenience, write it.

  • BasePresenter

BasePresenter is the base BasePresenter, which implements the common methods of the Presenter interface:

public class BasePresenter <V>  {

    protected Context mContext;
    protected V mView;

    protected void onCleared(a) {}public void attachView(Context context, V view) {
        this.mContext = context;
        this.mView = view;
    }

    public void detachView(a) {
        this.mView = null;
    }

    public boolean isAttachView(a) {
        return this.mView ! =null;
    }

    public void onCreatePresenter(@Nullable Bundle savedState) {}public void onDestroyPresenter(a) {
        this.mContext = null;
        detachView();
    }

    public void onSaveInstanceState(Bundle outState) {}}Copy the code

There are a few common methods to implement, such as attachView, detachView, isAttachView, etc., which are mandatory because they are related to lifecycle bindings and can do resource assignment and release.

  • BaseMvpActivity

This is the base class for the Activity.

public abstract class BaseMvpActivity<P  extends BasePresenter> extends AppCompatActivity  implements BaseMvpView {

    private PresenterProviders mPresenterProviders;
    private PresenterDispatch mPresenterDispatch;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(getContentView());
        mPresenterProviders = PresenterProviders.inject(this);
        mPresenterDispatch = new PresenterDispatch(mPresenterProviders);

        mPresenterDispatch.attachView(this.this);
        mPresenterDispatch.onCreatePresenter(savedInstanceState);
        init();
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        mPresenterDispatch.onSaveInstanceState(outState);
    }

    protected abstract int getContentView(a);

    public abstract void init(a);

    protected P getPresenter(a) {
        return mPresenterProviders.getPresenter(0);
    }

    public PresenterProviders getPresenterProviders(a) {
        return mPresenterProviders;
    }

    @Override
    public void showError(String msg) {}@Override
    public void complete(a) {}@Override
    public void showProgressUI(boolean isShow) {}@Override
    protected void onDestroy(a) {
        super.onDestroy(); mPresenterDispatch.detachView(); }}Copy the code

The generic P defined in the following BaseMvpActivity is intended to be used when using the getPresenter() method for a Presenter, as shown in the above example. Look at the implementation of getPresenter() :

protected P getPresenter(a) {
    return mPresenterProviders.getPresenter(0);
}
Copy the code

Just call the PresenterProviders. GetPresenter (int index) method.

BaseMvpActivity then implements the BaseMvpView interface, which by default implements some public methods that you need to be able to override when you inherit it.

Talking about PresenterProviders, this is a class that resolves annotations and performs common Presenter operations such as binding and unbinding views.

  1. We first call the inject method to instantiate, passing in the context parameter.
  2. By looking at the inject implementation, which calls the constructor, The constructor then calls the resolveCreatePresenter and resolvePresenterVariable methods to resolve the @CreatePresenter and @PresenterVariable annotations.
public static PresenterProviders inject(Context context) {
    return new PresenterProviders(context);
}

private PresenterProviders(Context context) {
    mContext = checkContext(context);
    resolveCreatePresenter();
    resolvePresenterVariable();
}
Copy the code

The following is a brief analysis of the implementation of PresenterProviders:

  1. For more information on annotations, check out this article: Understanding Java Annotation Types (@annotation)

  2. PresenterStore: This class stores an instance of a Presenter using a HashMap implementation:

private static final String DEFAULT_KEY = "PresenterStore.DefaultKey";
private final HashMap<String, P> mMap = new HashMap<>();

public final void put(String key, P presenter) {
    P oldPresenter = mMap.put(DEFAULT_KEY + ":" + key, presenter);
    if(oldPresenter ! =null) { oldPresenter.onCleared(); }}public final P get(String key) {
    return mMap.get(DEFAULT_KEY + ":" + key);
}

public final void clear(a) {
    for (P presenter : mMap.values()) {
        presenter.onCleared();
    }
    mMap.clear();
}
Copy the code

Since you are dealing with one or more Presenter objects, the goal is to be able to manage and find them uniformly.

  1. Then we go to the main PresenterProviders class and we look at a couple of methods, the first resolveCreatePresenter() method:
public <P extends BasePresenter> PresenterProviders resolveCreatePresenter(a) {
    CreatePresenter createPresenter = mContext.getClass().getAnnotation(CreatePresenter.class);
    if(createPresenter ! =null) {
        Class<P>[] classes = (Class<P>[]) createPresenter.presenter();
        for (Class<P> clazz : classes) {
            String canonicalName = clazz.getCanonicalName();
            try {
                mPresenterStore.put(canonicalName, clazz.newInstance());
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch(IllegalAccessException e) { e.printStackTrace(); }}}return this;
}
Copy the code

The resolveCreatePresenter() method resolves the @createPresenter annotation. It works like this: First get all classes defined on the annotation array, then loop through, take their canonicalName as the key, invoke newInstance method instantiate them and store them as value in HasMap above.

Next is the resolvePresenterVariable method:

public <P extends BasePresenter> PresenterProviders resolvePresenterVariable(a) {
    for (Field field : mContext.getClass().getDeclaredFields()) {
        // Get the annotation on the field
        Annotation[] anns = field.getDeclaredAnnotations();
        if (anns.length < 1) {
            continue;
        }
        if (anns[0] instanceof PresenterVariable) {
            String canonicalName = field.getType().getName();
            P presenterInstance = (P) mPresenterStore.get(canonicalName);
            if(presenterInstance ! =null) {
                try {
                    field.setAccessible(true);
                    field.set(mContext, presenterInstance);
                } catch(IllegalAccessException e) { e.printStackTrace(); }}}}return this;
}
Copy the code

The main purpose of the resolvePresenterVariable method is to find and assign an instance of the @PresenterVariable annotated object in the HashMap. GetDeclaredFields (); getDeclaredFields (); getPresEnterVariable (); The value of this Name will be the same as the canonicalName, so you can use it as the key to look up the corresponding instance in the HashMap, and then assign a value to the variable using the set method of the Field.

There is also a class called PresenterDispatch:

As you can see, attachView and detachView are used to do things like attachView and detachView. The reason for this is to separate the PresenterProviders from the PresenterStore.

public class PresenterDispatch {
    private PresenterProviders mProviders;

    public PresenterDispatch(PresenterProviders providers) {
        mProviders = providers;
    }

    public <P extends BasePresenter> void attachView(Context context, BaseContract.View view) {
        PresenterStore store = mProviders.getPresenterStore();
        HashMap<String, P> mMap = store.getMap();
        for (Map.Entry<String, P> entry : mMap.entrySet()) {
            P presenter = entry.getValue();
            if(presenter ! =null) { presenter.attachView(context, view); }}}public <P extends BasePresenter> void detachView(a) {
        PresenterStore store = mProviders.getPresenterStore();
        HashMap<String, P> mMap = store.getMap();
        for (Map.Entry<String, P> entry : mMap.entrySet()) {
            P presenter = entry.getValue();
            if(presenter ! =null) { presenter.detachView(); }}}... }Copy the code

So you can see that he’s got the PresenterProviders object, and then he’s got the HashMap that holds the Presenter instance, and he’s doing it. Other public implementations can do the same.

The whole process is done, isn’t it easy. In the actual use of the process can be according to their own needs to do the corresponding modification. Like to give a Star, welcome to leave a message to raise Issues and suggestions.