In our daily development, we all know that the development frameworks of Android end include MVC, MVP and MVVM. Speaking of these frameworks, everyone must have their own views, and even many students have encapsulated them.
Here's the question: aren't they all mvvms now? Why do you still write MVP? Is it useful? There are so many wheels on the Internet. Just find one with a high star.
Use and do it yourself packaging are completely two processes, which need to consider many aspects, including many pits. At present, this framework has been applied to the company project I wrote. Taking advantage of the recent time, the problems and defects existing in the framework have been repaired after this actual battle, and some JetPack components have been added at the same time. Hope to help you
First, let's briefly introduce what MVP mode is.

The simple understanding is:
Layer P is equivalent to a middleman, shouting xxx every day without making price difference... (in daily development, P inevitably involves some logical operations, but it does not affect anything. It cannot be done for the sake of design patterns. What must be done) Floor M is an honest worker who handles all kinds of hard work Layer V is equivalent to a little sister, responsible for beauty, so it is only responsible for displaying UI To sum up the process: the little sister (layer V) is going out and lacks a lipstick. Find layer p to buy it. The middleman (layer P) receives the order and finds the worker Pony (layer M) to run errands to buy it. After layer M buys it, tell the middleman that it is ready, and the middleman will inform the little sister. Then the little sister gets her lipstick and goes out to see her little brother happily.
Let's start our code process:
Many comments have been added to the code. If you don't understand anything, please leave a message at any time. Finally, remember our principles for interface oriented development.
View interface:
The first is the V-layer interface. Note that these can be changed. The framework encapsulation does not necessarily need to follow the dead rules
public interface IView { /** * When refreshing UI */ default void updateView() { } /** * Get Context * * @return */ Context context(); //Start Loader void showLoader(); //Stop loader void stopLoader(); /** * Destroy */ default void onDetachView() { } /** * turn off keyboard */ void hidekey(); }
View superclass
Knife joined me here.
/** * Fragment Base class * @author by Petterp * @date 2019-08-03 */ public abstract class BaseFragment<P extends IPresenter> extends Fragment implements IView{ private P presenter = null; private Unbinder unbinder = null; private View rootView = null; /** * Set view * * @return view */ public abstract Object setLayout(); /** * Create view * * @param savedInstanceState * @param rootView */ public abstract void onBindView(@Nullable Bundle savedInstanceState, @NonNull View rootView); @RequiresApi(api = Build.VERSION_CODES.KITKAT) @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { if (setLayout() instanceof Integer) { rootView = inflater.inflate((Integer) setLayout(), container, false); } else if (setLayout() instanceof View) { rootView = (View) setLayout(); } else { throw new ClassCastException("setLayout() must be int or View Error!"); } if (presenter == null) { //Get P object through annotation factory presenter = (P) PresenterFactoryImpl.createFactory(getClass()); } if (presenter != null) { //Set View presenter.setView(this); //Give life cycle getLifecycle().addObserver(presenter); } //Bind ButterKnife unbinder = ButterKnife.bind(this, rootView); //Fragment reclaims retained data setRetainInstance(true); //Add lifecycle onBindView(savedInstanceState, rootView); return rootView; } @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); setTitleToolbar(); } /** * Immersive status bar */ private void setTitleToolbar() { ImmersionBar.with(this) .titleBar(setToolbar()) .autoDarkModeEnable(true) .init(); } @Override public Context context() { return getContext(); } /** * Set Toolbar * * @return */ public View setToolbar() { return null; } /** * Return to P * * @return Presenter */ public P getPresenter() { return presenter; } /** * Return subclass View * * @return view */ protected View getRootView() { return rootView; } @Override public void onDestroyView() { super.onDestroyView(); if (unbinder != null) { unbinder.unbind(); unbinder = null; } presenter = null; rootView = null; } @Override public void hidekey() { } @Override public void showLoader() { } @Override public void stopLoader() { } }
Model interface
Note: RxJava+RxAndroid are integrated into the framework. RxModelinit here is used to handle some data loading tasks during initialization.
public interface IModel<P extends IPresenter> { /** * Some initialization behavior */ default void initData() { } /** * Set P * @param p */ void setPresenter(P p); /** * Initialization time consuming behavior */ default void RxModelinit() { } /** * Get P-layer object * * @return P */ P getPresenter(); }
Model superclass
There is nothing to note here, routine operation.
public abstract class BaseModel<P extends IPresenter> implements IModel { /** * P Layer interface */ private P p; /** * Set P * @param iPresenter */ @Override public void setPresenter(IPresenter iPresenter) { this.p= (P) iPresenter; } /** * Get P-layer interface * @return */ @Override public P getPresenter() { return p; } }
Presenter interface
Note: the superclass inherits DefaultLifecycleObserver because it is given a life cycle **Note: * * rx time-consuming task processing scheme has been integrated in the framework, which can be adjusted dynamically according to usage habits
public interface IPresenter<V extends IView, M extends IModel> extends DefaultLifecycleObserver { /** * Some initialization operations */ default void initPresenter() { } /** * Do you want livebus - > to be EventBus temporarily * * @return */ default boolean isLiveBus() { return false; } /** * Set View * * @param v */ void setView(V v); /** * Get V * * @return V */ V getView(); /** * Get M * * @return M */ M getModel(); /** * Start Rx initialization time-consuming task */ void rxStartInitData(); /** * Rx At the end of the task */ default void rxEndInitData() { } /** * rx During initialization */ default void rxSpecificData() { } /** * Is Rx required * * @return mode */ default boolean rxMode() { return false; } }
Presenter superclass
Note: dynamic proxy is used here. The purpose of dynamic proxy is to avoid null pointer of View, so as to reduce multiple null judgment of View. Note: the framework inherits the status bar processing tool immersionbar. When using, you only need to implement setToolbar() and then pass in the corresponding view. Note: the JetPack life cycle component is used here to give the P-layer life cycle. Because it is oriented to interface development, our P-layer interface needs to inherit from DefaultLifecycleObserver.
public abstract class BasePresenter<V extends IView, M extends IModel> implements IPresenter<V, M>, InvocationHandler { private SoftReference mView; private Disposable subscribe = null; private M model; private V proxyView; @SuppressWarnings("unchecked") @RequiresApi(api = Build.VERSION_CODES.KITKAT) @Override public void setView(V v) { //Soft reference this.mView = new SoftReference(v); //Dynamic agent proxyView = (V) Proxy.newProxyInstance(v.getClass().getClassLoader(), v.getClass().getInterfaces(), this); //M object obtained by annotation factory model = (M) ModelFactoryImpl.createFactory(getClass()); if (model != null) { model.setPresenter(this); } } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return isAttached() ? method.invoke(mView.get(), args) : null; } private boolean isAttached() { return mView.get() != null && proxyView != null; } @Override public V getView() { return proxyView; } @Override public M getModel() { return model; } @Override public void rxStartInitData() { subscribe = Observable .create(emitter -> { rxSpecificData(); emitter.onComplete(); }) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .doOnComplete(this::rxEndInitData) .subscribe(); } @Override public void onStart(@NonNull LifecycleOwner owner) { } @Override public void onResume(@NonNull LifecycleOwner owner) { //Initialize some operations, basic operations initPresenter(); if (model != null) { getModel().initData(); } } @Override public void onPause(@NonNull LifecycleOwner owner) { } @Override public void onStop(@NonNull LifecycleOwner owner) { //Close the keyboard. It is recommended to add a global Activity. Here, call the hidekey method of the public view layer proxyView.hidekey(); } @Override public void onDestroy(@NonNull LifecycleOwner owner) { //Note that do not set the proxy View to null here proxyView.onDetachView(); if (mView != null) { mView.clear(); mView = null; } //If the EventBus has been turned on, turn it off if (isLiveBus()) { EventBus.clearCaches(); EventBus.getDefault().unregister(this); } //Cancel Rx subscription if (subscribe != null && !subscribe.isDisposed()) { subscribe.dispose(); } //Cancel lifecycle owner.getLifecycle().removeObserver(this); } }
Annotation related
@CreateModel - generate layer M
@Inherited //Repeatable @Retention(RetentionPolicy.RUNTIME) //Runtime public @interface CreateModel { Class<? extends BaseModel> value(); }
public class ModelFactoryImpl { /** * Create the factory implementation class of Presenter according to the annotation * * @param viewClazz You need to create the V-layer implementation class of Presenter * @param <M> Type of Model currently to be created * @return Factory class */ @SuppressWarnings("unchecked") @RequiresApi(api = Build.VERSION_CODES.KITKAT) public static <M extends BaseModel> M createFactory(Class<?> viewClazz) { CreateModel annotation = viewClazz.getAnnotation(CreateModel.class); Class<M> aClass = null; if (annotation != null) { aClass = (Class<M>) annotation.value(); } try { return aClass != null ? aClass.newInstance() : null; } catch (IllegalAccessException | InstantiationException e) { throw new RuntimeException("Presenter Creation failed!,Check whether it is declared@CreatePresenter(xx.class)annotation"); } } }
@CreatePresenter - generate P layer
@Inherited //Repeatable @Retention(RetentionPolicy.RUNTIME) //Runtime public @interface CreatePresenter { Class<? extends BasePresenter> value(); }
public class PresenterFactoryImpl { /** * Create the factory implementation class of Presenter according to the annotation * * @param viewClazz You need to create the V-layer implementation class of Presenter * @param <P> The type of Presenter you are currently creating * @return Factory class */ @RequiresApi(api = Build.VERSION_CODES.KITKAT) public static <P extends BasePresenter> P createFactory(Class<?> viewClazz) { CreatePresenter annotation = viewClazz.getAnnotation(CreatePresenter.class); Class<P> aClass = null; if (annotation != null) { aClass = (Class<P>) annotation.value(); } try { return aClass != null ? aClass.newInstance() : null; } catch (IllegalAccessException | InstantiationException e) { throw new RuntimeException("Presenter Creation failed!,Check whether it is declared@CreatePresenter(xx.class)annotation"); } } }
The specific use process is as follows:
//Presenter class @CreateModel(TestModels.class) public class TestPresenter extends BasePresenter<TestControl.testView, TestControl.testModel> implements TestControl.testPresenter { }
//Model public class TestModels extends BaseModel<TestControl.testPresenter> implements TestControl.testModel{ }
//View @CreatePresenter(TestPresenter.class) public class TestFragment extends BaseFragment<TestControl.testPresenter> implements TestControl.testView { @Override public Object setLayout() { return R.layout.test_fragment; } @Override public void onBindView(@Nullable Bundle savedInstanceState, @NonNull View rootView) { } }
//Control contract interface public interface TestControl { interface TestView extends IView { } interface TestPresenter extends IPresenter<testView, testModel> { } interface TestModel extends IModel { } }
It's a great honor to see here to prove that this article is helpful to you.
Let me briefly talk about my idea of mobile terminal framework, some understanding in the packaging process and the pit in the actual project.
In MVP architecture, M and P are not related to each other in some pictures on the Internet. Why do you choose to be related to each other?
In the previous encapsulation, I didn't associate M with P, but in the actual development, I gradually found that P is needed in many places. If it is not associated, many logical operations must be placed in the p layer, which weakens the real role of the M layer. In fact, in MVP, many people think that p layer is the most important. In fact, it is not. P is just an intermediary. Its role is to coordinate M and V, so as to decouple M and V. Of course, you can handle some logic. Even you can put the logic on the p layer. You can't say it wrong. You can only say personal understanding.
Many people are using weak references to P objects or View objects. Is this really useful and practical?
First of all, it is useful because P holds View. Secondly, there are certain risks. There are four kinds of Java references. You can Baidu for details. If you directly use weak references, there is a risk of being recycled, so a better way is to use the reference queue (when the object is recycled, it will be put into the queue), but at the same time, you should also note that after you get(), your reference has become a strong reference.
What if I have some modules that need to be reused?
I personally recommend using the strategy mode for transformation. It is to separate the same method into an interface. Your main Fragment or Activity holds the interface object and provides the set method. Different subclasses implement the interface. Then, when using, set into the corresponding subclass instance, and then use the interface object to call the common method. Simple understanding is actually polymorphism. I originally added a public policy superclass to the framework, but the intrusion was serious, so I gave up. Of course, there are many design patterns. Sometimes no pattern is the best pattern.
Should I choose MVP or MVVM?
There is no best, only the most suitable. I think everyone wants to curse when they hear this sentence. What is it? As a passer-by, I need to tell you not to contact mature MVVM or MVP framework at the beginning, because the author considers a lot and may not be very friendly to beginners, so start from the basic tutorial. Finally, in fact, MVP and MVVM are not very different. How to use them depends on your project. If it's just learning, it's recommended to use them for actual development. If you have the same learning time, I prefer MVVM. After all, it is actually easier than MVP. This sentence may be ambiguous. Some students think that MVVM is very difficult. It sounds like the cost of learning ViewModel, DataBing and livedata is very high. But this is not the case. The more complex things look, they are often very simple after learning. Apart from DataBing, it's very fast to learn ViewModel+LiveData. Especially when you encapsulate MVP yourself, you will have the same feeling as before.
If you have any questions in use, please leave a message and hope to make progress and grow with you.