Combine my own understanding of MVP, plus personal habits, put together such an Android MVP framework

  • Inject Presenter dynamically through annotations
  • Presenter has a full life cycle
  • Support for multi-presenter injection
  • State recovery

0 x00 preface

This framework can be said to Fork the nucleus, but with personal understanding and custom changes, put the framework link github.com/izyhang/Dam…

0 x01 is introduced

First, a brief introduction to Damon

Create View and Presenter

Presenters have the same life cycle as activities, part of which is shown here

interface MainView {
    fun log(msg: String)
}

class MainPresenter : BasePresenter<MainView>() {
    override fun onCreate(arguments: Bundle? , savedState:Bundle?). {
        super.onCreate(arguments, savedState)
        view.log("MainPresenter.onCreate")}override fun onResume(a) {
        super.onResume()
        view.log("MainPresenter.onResume")}override fun onPause(a) {
        super.onPause()
        view.log("MainPresenter.onPause")}override fun onDestroy(a) {
        super.onDestroy()
        view.log("MainPresenter.onDestroy")}}Copy the code

Modify the MainActivity

Bind a MainActivity to a Presenter. To get a Presenter, go to @bindPresenter

@RequiresPresenter(MainPresenter::class)
class MainActivity : BaseActivity(), MainView {
    @BindPresenter
    private var mainPresenter: MainPresenter? = null

    override fun onCreate(savedInstanceState: Bundle?). {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        mainPresenter.xxx()
    }

    override fun log(msg: String) {
        println(msg)
    }
}
Copy the code

Presenter into more

@RequiresPresenter(value = [MainPresenter::class, SecondPresenter::class])
class MainActivity : BaseActivity(), MainView, SecondView {
    @BindPresenter
    private var mainPresenter: MainPresenter? = null
    @BindPresenter
    private var secondPresenter: SecondPresenter? = null

    override fun log(msg: String) {
        println(msg)
    }
}
Copy the code

0x02 Working principle

Here’s how Damon works

Dynamic injection and assignment for Presenter

Annotations instead of manually instantiating presenters are more convenient, and state recovery is implemented in encapsulated activities to avoid duplicate Presenter creation

Will pass into the current in the interface to create a class to ReflectionPresenterFactory, through the CLS. GetAnnotation get @ RequiresPresenter annotations and save, Members of the current class annotated by @bindPresenter are also saved here

@Nullable
public static ReflectionPresenterFactory fromViewClass(Object host, Class
        cls) {
    RequiresPresenter annotation = cls.getAnnotation(RequiresPresenter.class);
    Class<? extends MvpPresenter>[] pClass = null! = annotation ? annotation.value() :null;
    if (null == pClass) {
        return null;
    }
    List<Field> fields = new ArrayList<>();
    for (Field field : cls.getDeclaredFields()) {
        Annotation[] annotations = field.getDeclaredAnnotations();
        if (annotations.length < 1) {
            continue;
        }
        if (annotations[0] instanceofBindPresenter) { fields.add(field); }}return new ReflectionPresenterFactory(host, pClass, fields);
}
Copy the code

The creation of a Presenter

The above said Damon Presenter is a complete life cycle, see enclosed Activity MvpAppCompatActivity. Java

The Activity of the life cycle through the method of controlling the Presenter PresenterLifecycleDelegate to call, given its life cycle. It also overloads the onSaveInstanceState helper Presenter state recovery mechanism

public class MvpAppCompatActivity extends AppCompatActivity implements MvpView {

    private static final String PRESENTER_STATE_KEY = "presenter_state";
    private PresenterLifecycleDelegate mPresenterDelegate =
            new PresenterLifecycleDelegate(ReflectionPresenterFactory.fromViewClass(this, getClass()));

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mPresenterDelegate.onCreate(this, getIntent().getExtras(), null! = savedInstanceState ? savedInstanceState.getBundle(PRESENTER_STATE_KEY) :null);
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putBundle(PRESENTER_STATE_KEY, mPresenterDelegate.onSaveInstanceState());
    }

    @Override
    public void onStart(a) {
        super.onStart();
        mPresenterDelegate.onStart();
    }

    @Override
    public void onResume(a) {
        super.onResume();
        mPresenterDelegate.onResume();
    }

    @Override
    public void onPause(a) {
        mPresenterDelegate.onPause();
        super.onPause();
    }

    @Override
    public void onStop(a) {
        mPresenterDelegate.onStop();
        super.onStop();
    }

    @Override
    public void onDestroy(a) { mPresenterDelegate.onDestroy(! isChangingConfigurations());super.onDestroy(); }}Copy the code

PresenterLifecycleDelegate

The most important of these is the hub that connects the Activity to the Presenter and controls the Presenter’s creation, recovery, and lifecycle

public class PresenterLifecycleDelegate {

    private static final String PRESENTER_KEY = "presenter - ";
    private static final String PRESENTER_ID_KEYS = "presenter_ids";

    @Nullable
    private PresenterFactory mPresenterFactory;
    @Nullable
    private List<? extends MvpPresenter> mPresenters;

    private boolean mPresenterHasView;

    public PresenterLifecycleDelegate(@Nullable PresenterFactory presenterFactory) {
        this.mPresenterFactory = presenterFactory;
    }

    public void onCreate(MvpView view, @Nullable Bundle arguments, @Nullable Bundle savedState) {
        if (mPresenterFactory == null) {
            return;
        }
        Bundle presenterBundle = null;
        if(savedState ! =null) {
            presenterBundle = ParcelFn.unmarshall(ParcelFn.marshall(savedState));
        }
        createPresenter(presenterBundle);
        if(mPresenters ! =null && !mPresenters.isEmpty()) {
            mPresenterFactory.bindPresenter(mPresenters);
            for (MvpPresenter presenter : mPresenters) {
                //noinspection unchecked
                presenter.create(view, arguments, null! = presenterBundle ? presenterBundle.getBundle(PRESENTER_KEY.concat(presenter.getClass().getSimpleName())) :null); }}}private void createPresenter(Bundle presenterBundle) {
        if(presenterBundle ! =null) {
            mPresenters = PresenterStorage.INSTANCE.getPresenter(presenterBundle.getStringArray(PRESENTER_ID_KEYS));
        }

        if (mPresenters == null) {
            //noinspection ConstantConditionsmPresenters = mPresenterFactory.createPresenter(); PresenterStorage.INSTANCE.add(mPresenters); }}public void onStart(a) {
        if(mPresenters ! =null && !mPresenters.isEmpty()) {
            for(MvpPresenter presenter : mPresenters) { presenter.start(); }}}public Bundle onSaveInstanceState(a) {
        Bundle bundle = new Bundle();
        if(mPresenters ! =null && !mPresenters.isEmpty()) {
            String[] ids = new String[mPresenters.size()];
            for (MvpPresenter presenter : mPresenters) {
                Bundle presenterBundle = new Bundle();
                presenter.save(presenterBundle);
                bundle.putBundle(PRESENTER_KEY.concat(presenter.getClass().getSimpleName()), presenterBundle);

                ids[mPresenters.indexOf(presenter)] = PresenterStorage.INSTANCE.getId(presenter);
            }
            bundle.putStringArray(PRESENTER_ID_KEYS, ids);
        }
        return bundle;
    }

    public void onResume(a) {
        if(mPresenters ! =null&&! mPresenters.isEmpty() && ! mPresenterHasView) {for (MvpPresenter presenter : mPresenters) {
                presenter.resume();
            }
            mPresenterHasView = true; }}public void onPause(a) {
        if(mPresenters ! =null && !mPresenters.isEmpty() && mPresenterHasView) {
            for (MvpPresenter presenter : mPresenters) {
                presenter.pause();
            }
            mPresenterHasView = false; }}public void onStop(a) {
        if(mPresenters ! =null && !mPresenters.isEmpty()) {
            for(MvpPresenter presenter : mPresenters) { presenter.stop(); }}}public void onDestroy(boolean isFinal) {
        if(isFinal && mPresenters ! =null && !mPresenters.isEmpty()) {
            for (MvpPresenter presenter : mPresenters) {
                presenter.destroy();
            }
            mPresenters.clear();
            mPresenters = null; }}}Copy the code

0x03 is at the end

Presenterstorage.java implements presenterStorage.java. Presenterstorage.java implements presenterStorage.java

The Multi Presenter version of Damon is the 2.0.0-alpha version of the new variant, and has not undergone extensive testing. Damon, version 1.1.0 of Single Presenter, has been stable in development environments for a long time

Finally, the link to the project github.com/izyhang/Dam…

Welcome to point out the shortcomings, leave your opinion ~