Android进阶之路Android开发经验谈Android技术知识

进阶MVP设计模式之二

2019-07-06  本文已影响11人  Chase_stars

世上本没有路,走的人多了,便变成了路 — 鲁迅

写在前面

在上一篇文章《进阶MVP设计模式之一》中已经详细讲解了这套MVP的实现以及使用,本篇文章是基于上一篇文章的基础上进行扩展。为了能够更好的理解,建议先看上一篇文章加深印象后再来阅读,会更有收获哦。

在上一篇文章末尾说过:这套MVP设计巧妙,但是也有不如意的地方,就是不能在一个Activity/Fragment中使用多个不同的Presenter。

在实际项目中,我也遇到过此类情况,比如Activity A需要调用Presenter B中fun()函数,但是一个Activity只允许拥有一个Presenter,Activity A已经拥有Presenter A了,就不能拥有Presenter B,最终解决方案只能是将Presenter B中的fun()函数copy到Presenter A中,这样Activity A就可以调用Presenter A中的fun()函数了。如果Activity A中要大量使用其他不同Presenter中的接口呢,那么Presenter A中就要写一大坨代码,导致整个P层重复的代码太多。换一种角度思考,Activity A想要使用Presenter B,直接把Presenter B拿过来不就好了, 让一个Activity持有多个Presenter,需要谁就把谁拿过来,何乐而不为?

如何实现

划重点:接下来会以对比的方式讲解,所以一定要先看上一篇文章!!! 传送门>>>

1.RequestPresenter

不同之处是返回值类型,由Class变为Class数组,意味着可以放入多个类了。

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

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

public interface BaseView {}
3.BasePresenter

这个Presenter抽象类不变,这里的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.IPresenterStore

新增Presenter存储接口,提供了get(),put()等方法,具体是用来做什么的呢,请带着思考继续看。

public interface IPresenterStore<V extends BaseView, P extends BasePresenter<V>> {

    void put(String key, P presenter);

    P get(String key);

    Map<String, P> getStore();

    void clear();
}
5.PresenterStore

新增Presenter存储具体实现类,内部维护了一个Map,用来存储Presenter。

public class PresenterStore<V extends BaseView, P extends BasePresenter<V>> implements IPresenterStore<V, P> {

    private Map<String, P> mStore;

    @Override
    public void put(String key, P presenter) {
        if (mStore == null) {
            mStore = new HashMap<>();
        }
        mStore.put(key, presenter);
    }

    @Override
    public P get(String key) {
        return mStore.get(key);
    }

    @Override
    public Map<String, P> getStore() {
        return mStore;
    }

    @Override
    public void clear() {
        if (mStore != null) {
            mStore.clear();
        }
    }
}
6.IPresenterFactory

Presenter工厂接口,需要生产Presenter的工厂类来实现,依然只有一个函数。
不同之处是createPresenter()改为createPresenterStore(),返回值类型也从Presenter改为IPresenterStore,不过仍是创建Presenter的核心。

public interface IPresenterFactory<V extends BaseView, P extends BasePresenter<V>> {

   IPresenterStore<V, P> createPresenterStore();
}
7.PresenterFactoryImpl

Presenter工厂的具体实现类,通过一个静态函数createFactory()返回一个该Presenter工厂实现类,并在该函数中通过注解+反射得到全部Presenter类名;当外部调用createPresenterStore()时,会先创建一个新的PresenterStore,然后通过Presenter类名newInstance()一个新的实例添加到PresenterStore中,key为Presenter类的字符串,全部Presenter创建完成后将这个PresenterStore返回,PersenterStore就创建完成了。

不同之处在于从创建一个Presenter变成了创建多个Presenter,并将多个Presenter使用PresenterStore来维护。

public class PresenterFactoryImpl<V extends BaseView, P extends BasePresenter<V>>
        implements IPresenterFactory<V, P> {

    private Class<P>[] mPresenters;

    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>[] pClasses = (Class<P>[]) requestPresenter.presenter();
        return pClasses == null ? null : new PresenterFactoryImpl(pClasses);
    }

    private PresenterFactoryImpl(Class<P>[] pClasses) {
        mPresenters = pClasses;
    }

    @Override
    public IPresenterStore<V, P> createPresenterStore() {
        if (mPresenters == null || mPresenters.length < 1) {
            throw new RuntimeException("Presenters is null");
        }

        IPresenterStore<V, P> presenterStore = new PresenterStore<>();

        try {
            for (Class<P> pClass : mPresenters) {
                presenterStore.put(pClass.toString(), pClass.newInstance());
            }
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
        return presenterStore;
    }
8.IPresenterProxy

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

public interface IPresenterProxy<V extends BaseView, P extends BasePresenter<V>> {

    void setFactory(IPresenterFactory<V, P> factory);

    IPresenterFactory<V, P> getFactory();

    P getPresenter(String key);
}
9.PresenterProxyImpl

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

不同之处在于构造函数或设置工厂时就去创建PresenterStore,而不是在getPresenter()时去创建;由于多个Presenter是通过PresenterStore维护的,这里的getPresenter()需要传入参数key得到对应的Presenter。

public class PresenterProxyImpl<V extends BaseView, P extends BasePresenter<V>> implements IPresenterProxy<V, P> {

    private IPresenterFactory<V, P> mFactory;

    private IPresenterStore<V, P> mPresenterStore;

    // 通过构造函数传入默认工厂
    public PresenterProxyImpl(IPresenterFactory<V, P> factory) {
        mFactory = factory;
        mPresenterStore = mFactory.createPresenterStore();
    }

    // 设置工厂(默认工厂不能满足需求可以设置)
    @Override
    public void setFactory(IPresenterFactory<V, P> factory) {
        mFactory = factory;
        mPresenterStore = mFactory.createPresenterStore();
    }

    // 获取当前工厂
    @Override
    public IPresenterFactory<V, P> getFactory() {
        return mFactory;
    }

    // 获取Presenter
    @Override
    public P getPresenter(String key) {
        return mPresenterStore.get(key);
    }

    public void attachView(Context context, V view) {
        for (Map.Entry<String, P> entry : mPresenterStore.getStore().entrySet()) {
            P presenter = entry.getValue();
            presenter.attachView(context, view);
        }
    }

    public void detachView() {
        for (Map.Entry<String, P> entry : mPresenterStore.getStore().entrySet()) {
            P presenter = entry.getValue();
            presenter.detachView();
        }
    }

    public void release() {
        mPresenterStore.clear();
    }
10.BaseActivity

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

不同之处是getPresneter()需要传入key,在Activity销毁时,要调用Presenter代理类的release()函数释放PresenterStore。

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(String key) {
        return mPresenterProxy.getPresenter(key);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mPresenterProxy.detachView();
        mPresenterProxy.release();
    }

    protected abstract int getLayoutId();

    protected abstract void initView();

    protected abstract void initData();
}

以上就是经过改良的代码,本质上就是通过一个集合来维护多个Presenter,以此达到一个Activity/Fragment持有多个Presenter的目的。

如何使用

接下来还是以一个小Demo介绍使用方式。

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;
    }
}
public class RegisterModel {

    public boolean isRegisterSuccess(String username, String password) {
        return true;
    }
}
2. 编写MVP的View
public interface ILoginView extends BaseView {

    void onLoginSuccess();

    void onLoginFail();
}
public interface IRegisterView extends BaseView {

    void onRegisterSuccess();

    void onRegisterFail();
}
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();
        }
    }
public class RegisterPresenter extends BasePresenter<IRegisterView> {

    private RegisterModel mRegisterModel;

    public RegisterPresenter() {
        mRegisterModel = new RegisterModel();
    }

    public void register(String username, String password) {
        if (!isAttachView()) {
            return;
        }

        boolean isSuccess = mRegisterModel.isRegisterSuccess(username, password);

        if (isSuccess) {
            getView().onRegisterSuccess();
        } else {
            getView().onRegisterFail();
        }
    }
}
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_constraintTop_toBottomOf="@id/edit_password" />

    <android.support.v7.widget.AppCompatButton
        android:id="@+id/btn_register"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:text="Register"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@id/edit_password" />

</android.support.constraint.ConstraintLayout>
5. 编写MainActivity
@RequestPresenter(presenter = {LoginPresenter.class, RegisterPresenter.class})
public class MainActivity extends BaseActivity implements ILoginView, IRegisterView {

    private AppCompatEditText mUsernameEdit;
    private AppCompatEditText mPasswordEdit;
    private AppCompatButton mLoginBtn;
    private AppCompatButton mRegisterBtn;

    @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);
        mRegisterBtn = findViewById(R.id.btn_register);

        // 点击按钮时执行登录
        mLoginBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String username = mUsernameEdit.getText().toString();
                String password = mPasswordEdit.getText().toString();
                getLoginPresenter().login(username, password);
            }
        });

        // 点击按钮时执行注册
        mRegisterBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String username = mUsernameEdit.getText().toString();
                String password = mPasswordEdit.getText().toString();
                getRegisterPresenter().register(username, password);
            }
        });
    }

    // 获取LoginPresenter
    private LoginPresenter getLoginPresenter() {
        return (LoginPresenter) getPresenter(LoginPresenter.class.toString());
    }

    // 获取RegisterPresenter
    private RegisterPresenter getRegisterPresenter() {
        return (RegisterPresenter) getPresenter(RegisterPresenter.class.toString());
    }

    @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();
    }

    @Override
    public void onRegisterSuccess() {
        Toast.makeText(this, "注册成功!!!", Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onRegisterFail() {
        Toast.makeText(this, "注册失败!!!", Toast.LENGTH_SHORT).show();
    }
}

运行效果如下:

Login.png Register.png

完结

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

上一篇下一篇

猜你喜欢

热点阅读