书山有路勤为径,学海无涯苦作舟 — 韩愈
写在前面
最近项目的第一阶段收尾了,不是很忙,改改遗留的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();
}
}
运行效果如下:
总结
总体来说,这套MVP设计的很巧妙,通过注解+反射得到Presenter,其中也使用了工厂模式和代理模式,代码量很少,收获却很大。
在学习过程中我发现这套MVP有一个不如意的地方,那就是不能在一个Activity/Fragment中使用多个不同的Presenter,所以将它进行了改良,下一篇文章会详细讲解,未完待续...