进阶MVP设计模式之一

作者: Chase_stars | 来源:发表于2019-07-05 23:41 被阅读3次

    书山有路勤为径,学海无涯苦作舟 — 韩愈

    写在前面

    最近项目的第一阶段收尾了,不是很忙,改改遗留的Bug,搞搞第二阶段的需求,算是缓冲期吧,准时下班,周末双休,如果能一直这样美滋滋就更好了,生活乐无忧。

    昨天听成哥吹了一个特别牛逼的MVP架构,通过注解得到Presenter。好牛逼,既然写不出来,那么我就抄一个,哈哈哈哈!

    如何实现

    请睁大双眼,认真思考,我要开车了...

    1.RequestPresenter

    既然是通过注解实现MVP,那么一定要写一个注解类。

    // 注解的生命周期
    @Retention(RetentionPolicy.RUNTIME)
    // 注解的使用范围
    @Target(ElementType.TYPE)
    public @interface RequestPresenter {
        Class<?> presenter();
    }
    
    2.BaseView

    一个无函数的View接口,需要结合实际需求进行扩展。

    public interface BaseView {}
    
    3.BasePresenter

    这里的View使用了弱引用,防止内存泄漏。

    public abstract class BasePresenter<V extends BaseView> {
    
        protected Context mContext;
    
        private WeakReference<V> mView;
    
        protected void attachView(Context context, V view) {
            mContext = context;
            mView = new WeakReference<>(view);
        }
    
        protected void detachView() {
            if (mView != null && mView.get() != null) {
                mView.clear();
            }
        }
    
        protected boolean isAttachView() {
            return mView != null && mView.get() != null;
        }
    
        protected V getView() {
            return mView == null ? null : mView.get();
        }
    }
    
    4.IPresenterFactory

    Presenter工厂接口,需要生产Presenter的工厂类来实现,它只有一个函数createPresenter(),它就是创建Presenter的核心,后面会讲到。

    public interface IPresenterFactory<V extends BaseView, P extends BasePresenter<V>> {
    
        P createPresenter();
    }
    
    5.PresenterFactoryImpl

    Presenter工厂的具体实现类,通过一个静态函数createFactory()返回一个该Presenter工厂实现类,并在该函数中通过注解+反射得到Presenter类名;当外部调用createPresenter()时,就会通过Presenter类名newInstance()一个新的实例返回,Presenter就创建完成了。

    public class PresenterFactoryImpl<V extends BaseView, P extends BasePresenter<V>> implements IPresenterFactory<V, P> {
    
        private Class<P> mPresenter;
    
        public static <V extends BaseView, P extends BasePresenter<V>>PresenterFactoryImpl createFactory(Class<?> clazz) {
            if (!clazz.isAnnotationPresent(RequestPresenter.class)) {
                throw new RuntimeException("Do not find RequestPresenter");
            }
            RequestPresenter requestPresenter = clazz.getAnnotation(RequestPresenter.class);
            Class<P> pClass = (Class<P>) requestPresenter.presenter();
            return pClass == null ? null : new PresenterFactoryImpl(pClass);
        }
    
        private PresenterFactoryImpl(Class<P> pClass) {
            mPresenter = pClass;
        }
    
        @Override
        public P createPresenter() {
            if (mPresenter == null) {
                throw new RuntimeException("Presenter is null");
            }
    
            P presenter = null;
    
            try {
                presenter = mPresenter.newInstance();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            }
            return presenter;
        }
    
    6.IPresenterProxy

    Presenter代理接口,其中有三个函数,可根据实际需求编写。

    public interface IPresenterProxy<V extends BaseView, P extends BasePresenter<V>> {
    
        void setFactory(IPresenterFactory<V, P> factory);
    
        IPresenterFactory<V, P> getFactory();
    
        P getPresenter();
    }
    
    7.PresenterProxyImpl

    因为Presenter工厂只有createFactory()和createPresenter(),而实际项目中不仅仅满足于createFactory()和createPresenter(),还会有其他的需要;所以使用了一个代理类来满足其他需要,所代理的是Presenter工厂和Presenter,可以进行扩展,比如attachView(),detachView()等。

    public class PresenterProxyImpl<V extends BaseView, P extends BasePresenter<V>> implements IPresenterProxy<V, P> {
    
        private IPresenterFactory<V, P> mFactory;
    
        private P mPresenter;
    
        // 通过构造函数传入默认工厂
        public PresenterProxyImpl(IPresenterFactory<V, P> factory) {
            mFactory = factory;
        }
    
        // 设置工厂(默认工厂不能满足需求可以设置)
        @Override
        public void setFactory(IPresenterFactory<V, P> factory) {
            mFactory = factory;
        }
    
        // 获取当前工厂
        @Override
        public IPresenterFactory<V, P> getFactory() {
            return mFactory;
        }
        
        // 获取Presenter
        @Override
        public P getPresenter() {
            if (mPresenter != null) {
                return mPresenter;
            }
    
            if (mFactory != null) {
                mPresenter = mFactory.createPresenter();
            }
    
            return mPresenter;
        }
    
        public void attachView(Context context, V view) {
            getPresenter().attachView(context, view);
        }
    
        public void detachView() {
            getPresenter().detachView();
        }
    }
    

    8.BaseActivity

    因为有了Presenter代理类,BaseActivity只需要关注Presenter代理类就可以了,无需关注Presenter工厂和Presenter等。

    public abstract class BaseActivity<V extends BaseView, P extends BasePresenter<V>> extends AppCompatActivity
            implements IPresenterProxy<V, P> {
    
        private PresenterProxyImpl<V, P> mPresenterProxy;
    
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(getLayoutId());
            getWindow().getDecorView().post(new Runnable() {
                @Override
                public void run() {
                    initPresenterProxy();
                    initView();
                    initData();
                }
            });
        }
    
        // 初始化Presenter代理类
        private void initPresenterProxy() {
            mPresenterProxy = new PresenterProxyImpl<>(PresenterFactoryImpl.<V, P>createFactory(getClass()));
            mPresenterProxy.attachView(this, (V) this);
        }
    
        @Override
        public void setFactory(IPresenterFactory<V, P> factory) {
            mPresenterProxy.setFactory(factory);
        }
    
        @Override
        public IPresenterFactory<V, P> getFactory() {
            return mPresenterProxy.getFactory();
        }
    
        @Override
        public P getPresenter() {
            return mPresenterProxy.getPresenter();
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            mPresenterProxy.detachView();
        }
    
        protected abstract int getLayoutId();
    
        protected abstract void initView();
    
        protected abstract void initData();
    }
    

    以上就是通过注解P来实现与Activity/Fragment绑定的全部核心代码,代码量不是很多,不过仔细研究会很有意思的。

    • BaseActivity在onCreate时会去创建Presenter代理类,而Presenter代理类的构造函数传入的参数是通过Presenter工厂类createFactory()返回新的Presenter工厂实现类。
    • createFactory()传入的参数是Activity/Fragment类,通过注解+反射得到注解类名。
    • 当Presenter代理类的getPresenter()被调用时,若还没有Presenter,就会执行Presenter工厂类的createPresenter(),通过注解类名创建一个新的Presenter返回给Presenter代理类,Presenter代理类就会一直使用这个Presenter来进行代理。
    • Presenter代理类有一个默认Presenter工厂类,当默认Presenter工厂类满足不了需求时,可以重新设置Presenter工厂类。

    如何使用

    下面我来写一个简单的小Demo介绍一下该如何使用这套MVP

    1.编写MVP的Model
    public class LoginModel {
    
        private static final String USERNAME = "12345";
        private static final String PASSWORD = "67890";
    
        public boolean isLoginSuccess(String username, String password) {
            if (USERNAME.equals(username) && PASSWORD.equals(password)) {
                return true;
            }
            return false;
        }
    }
    
    2. 编写MVP的View
    public interface ILoginView extends BaseView {
    
        void onLoginSuccess();
    
        void onLoginFail();
    }
    
    3.编写MVP的Presenter
    public class LoginPresenter extends BasePresenter<ILoginView> {
    
        private LoginModel mLoginModel;
    
        public LoginPresenter() {
            mLoginModel = new LoginModel();
        }
    
        public void login(String username, String password) {
            if (!isAttachView()) {
                return;
            }
    
            boolean isSuccess = mLoginModel.isLoginSuccess(username, password);
    
            if (isSuccess) {
                getView().onLoginSuccess();
            } else {
                getView().onLoginFail();
            }
        }
    
    4.编写xml布局文件
    <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
    
        <android.support.v7.widget.AppCompatEditText
            android:id="@+id/edit_username"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="50dp"
            app:layout_constraintTop_toTopOf="parent" />
    
        <android.support.v7.widget.AppCompatEditText
            android:id="@+id/edit_password"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            app:layout_constraintTop_toBottomOf="@id/edit_username" />
    
        <android.support.v7.widget.AppCompatButton
            android:id="@+id/btn_login"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="20dp"
            android:text="Login"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toBottomOf="@id/edit_password" />
    
    </android.support.constraint.ConstraintLayout>
    
    5. 编写MainActivity
    @RequestPresenter(presenter = LoginPresenter.class)
    public class MainActivity extends BaseActivity<ILoginView, LoginPresenter> implements ILoginView {
    
        private AppCompatEditText mUsernameEdit;
        private AppCompatEditText mPasswordEdit;
        private AppCompatButton mLoginBtn;
    
        @Override
        protected int getLayoutId() {
            return R.layout.activity_main;
        }
    
        @Override
        protected void initView() {
            mUsernameEdit = findViewById(R.id.edit_username);
            mPasswordEdit = findViewById(R.id.edit_password);
            mLoginBtn = findViewById(R.id.btn_login);
    
            // 点击按钮时执行登录
            mLoginBtn.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    String username = mUsernameEdit.getText().toString();
                    String password = mPasswordEdit.getText().toString();
                    getPresenter().login(username, password);
                }
            });
        }
    
        @Override
        protected void initData() {
    
        }
    
        @Override
        public void onLoginSuccess() {
            Toast.makeText(this, "登录成功!!!", Toast.LENGTH_SHORT).show();
        }
    
        @Override
        public void onLoginFail() {
            Toast.makeText(this, "登录失败!!!", Toast.LENGTH_SHORT).show();
        }
    }
    

    运行效果如下:

    Success.png Fail.png

    总结

    总体来说,这套MVP设计的很巧妙,通过注解+反射得到Presenter,其中也使用了工厂模式和代理模式,代码量很少,收获却很大。

    在学习过程中我发现这套MVP有一个不如意的地方,那就是不能在一个Activity/Fragment中使用多个不同的Presenter,所以将它进行了改良,下一篇文章会详细讲解,未完待续...

    项目地址:https://github.com/zhangjunxiang1995/Application/tree/master/mvp1

    相关文章

      网友评论

        本文标题:进阶MVP设计模式之一

        本文链接:https://www.haomeiwen.com/subject/qhuyhctx.html