开启成长之旅!这是我参与「日新方案 12 月更文挑战」的第1天
Google在2016年推出了官方的Android MVP架构Demo,本文主要剖析一下官方的MVP Demo,并且借由自己的一些经验,提出一些学习过程中,遇到的问题和自己的改进、封装办法。
1.什么是MVP?
MVP架构现已推出许多年了,现在现已非常普及了,我在这儿就不过多介绍,简单的说,它分为以下三个层次:
- Model:数据模型层,主要用来数据处理,获取数据;
- View:显示界面元素,和用户进行界面交互;
- Presenter: 是Model和View沟通的桥梁,不关怀具体的View显示和Model的数据处理。View层中所有的逻辑操作都经过Presenter去告诉Model层去完结,Model中获取的数据经过Presenter层去告诉View层显示。
MVP架构最大的好处,便是
把传统MVC架构中View层和Control层的复杂关系完全解耦,View层只关怀界面显示相关的作业即可,Model层仅获取数据,处理逻辑运算即可,各司其职,而不必关怀其他作业
。并且咱们发现没有,这样规划的话,很好写单元测试代码,针关于View、Presenter、Model层咱们能够别离选用适合的单元测试结构
,不必再像MVC/MVVM相同,由于代码混在或许别离在多处,处处无法“单一职责”的去完结每个类的单元测试代码编写。
在刚刚触摸android的时分,或许说,现在仍然有很大一部分的APP开发者,在开发过程中,总是习气在一个Activity、Fragment中几乎完结了所有的功用。例如网络恳求、数据加载、事务逻辑处理、界面加载、界面动画。 后来渐渐的咱们触摸到了MVC、MVP各种官方的结构,懂得了模块的别离、解耦(MVC),懂得了经过依赖于笼统去别离各个模块完全解耦(MVP)。 可是官方的MVP结构的确现已帮咱们做了许多,可是仍然不够,接下来,咱们根据官方给的MVP结构,结合六大基本原则,去封装愈加适宜于项目的MVP结构。 整体规划形式Demo代码
2.Google官方的MVP
官方Demo怎样去做的?
咱们为了便利去剖析,我这儿简化代码,咱们逐步剖析,BaseView 和 BasePresenter,BaseView谷歌是这么写的(其实便是view的接口,展示view),以下样例为了理解,我简化处理了部分代码
BaseView.java
package com.itbird.design.principle.mvp.google;
/**
* Google Demo
* Created by itbird on 2022/2/25
*/
public interface BaseView<T> {
//View中,设置presenter目标,使View能够持有Presenter目标引用
void setPresenter(T presenter);
}
BasePresenter
package com.itbird.design.principle.mvp.google;
/**
* Google Demo
* Created by itbird on 2022/2/25
*/
public interface BasePresenter {
}
TaskDetailContract
package com.itbird.design.principle.mvp.google;
/**
* Google Demo
* Created by itbird on 2022/2/25
*/
public interface TaskDetailContract {
interface View extends BaseView<Presenter> {
//界面UI改写办法
void updateTextView(String s);
}
interface Presenter extends BasePresenter {
void loadDataFromModel();
}
}
TaskGoogleActivity
package com.itbird.design.principle.mvp.google;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import com.itbird.design.R;
public class TaskGoogleActivity extends AppCompatActivity implements TaskDetailContract.View {
private static final String TAG = TaskGoogleActivity.class.getSimpleName();
private TaskDetailContract.Presenter mPresenter;
private TextView mTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.textview);
Log.e(TAG, TAG + " onCreate");
new TaskGooglePresenter(this);
}
@Override
public void setPresenter(TaskDetailContract.Presenter presenter) {
mPresenter = presenter;
}
@Override
public void updateTextView(String s) {
mTextView.setText(s);
}
}
TaskGooglePresenter
package com.itbird.design.principle.mvp.google;
public class TaskGooglePresenter implements TaskDetailContract.Presenter {
private static final String TAG = TaskGooglePresenter.class.getSimpleName();
TaskDetailContract.View mView;
public TaskGooglePresenter(TaskDetailContract.View view) {
mView = view;
mView.setPresenter(this);
}
@Override
public void loadDataFromModel() {
//TODO :loaddata,此处能够用model、或许进行事务操作
//调用界面办法,进行数据改写
mView.updateTextView("loaddata success!!!");
}
}
小结 咱们单从规划来说, Google MVP Demo的确向咱们展示了MVP的优点: 1)事务、数据、视图别离、解耦,从六大基本原则上来说,开闭、单一职责、里氏代换、依赖倒置、接口阻隔、最少知道,基本都做到了。 2)由于完全别离,所以咱们很便利针关于每层去选取对应的单元测试形式,例如针关于数据层能够运用(Junit+Mockito)、视图层能够运用(AndroidJunitRunner+Espresso)、事务层能够运用(Junit+Mockito) 3)经过Contract 契约类,完全将视图、事务相关的接口,封装放在了一个当地,简单明了,针关于开发者来说,不容易忘掉和辅佐养成习气
可是,可是关于咱们实践开发运用来说,仍然有以下几点问题:
1)setPresenter接口完全没有必要存在,由于Presenter目标一定是在View类中new出来的,我既然都有它自己的目标了,我干嘛还要在Presenter内部,去调用mView.setPresenter(this);,再回来View中,再去保存一个引用,代码看着很怪 2)咱们知道MVP的精华在与,P、V必定要相互交互,他们互相要持有对方的引用,经过上面一点,咱们知道Presenter目标一定是在View类中new出来的,所以View必定有Presenter目标的引用,这个没问题了。可是Presenter要想具有View的引用,只能经过Presenter结构办法、或许Presenter内部有一个setView办法,让开发者自己去调用setView或许实现带参结构办法,从规划视点来说,这显着是一个坑,由于一个结构的规划,是为了愈加便利开发,而不是让开发人员设置这个、设置那个、有必要调用某个办法。既然是有必要调用的办法,咱们应该经过结构去内部消化掉,而不是给开发者制作费事
。 3)Presenter中具有View的引用,如果activity、fragment销毁,可是presenter仍然在履行某些使命,这样会导致activity、fragment无法GC收回,导致内存走漏,乃至与崩溃,所以这也是一个结构有必要处理的问题。
3.V1.1 My MVP V1
根据上面提出的三点,咱们去优化Google的MVP结构。
咱们首先将第一点和第二点经过笼统来处理一下。
IPresenter
package com.itbird.design.principle.mvp.v1;
/**
* 自定义MVP结构,BasePresenter
* Created by itbird on 2022/2/25
*/
public interface IPresenter {
/**
* 与view班定
*
* @param view
*/
void onAttach(IView view);
/**
* 与view解绑
*/
void onDetach();
/**
* 是否与view现已班定成功
*
* @return
*/
boolean isViewAttached();
/**
* 获取view
* @return
*/
IView getView();
}
IView
package com.itbird.design.principle.mvp.v1;
/**
* 自定义MVP结构,BaseView
* Created by itbird on 2022/2/25
*/
public interface IView {
}
接下来是借助activity生命周期,对presenter的初始化进行封装
BaseActivity
package com.itbird.design.principle.mvp.v1;
import android.app.Activity;
import android.os.Bundle;
import androidx.annotation.Nullable;
/**
* Created by itbird on 2022/3/29
*/
public abstract class BaseActivity extends Activity implements IView {
IPresenter mPresenter;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mPresenter = createPresenter();
if (mPresenter != null) {
mPresenter.onAttach(this);
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mPresenter != null) {
mPresenter.onDetach();
mPresenter = null;
}
}
abstract IPresenter createPresenter();
}
BasePresenter
package com.itbird.design.principle.mvp.v1;
import java.lang.ref.WeakReference;
/**
* Created by itbird on 2022/3/29
*/
public class BasePresenter<V extends IView> implements IPresenter {
WeakReference<V> mIView;
@Override
public void onAttach(IView iView) {
mIView = new WeakReference<>((V) iView);
}
@Override
public void onDetach() {
mIView = null;
}
@Override
public V getView() {
if (mIView != null) {
mIView.get();
}
return null;
}
@Override
public boolean isViewAttached() {
return mIView != null && mIView.get() != null;
}
}
4.V1.2 My MVP V2
看上图类图,咱们仍然发现有一些不满足的点: 1)activity中,仍然需求初始化mPresenter
@Override
IPresenter createPresenter() {
mTaskPresenter = new TaskMyPresenter();
return mTaskPresenter;
}
是否能够做到在activity中,自己像presenter中调用view相同,自己一句话getView就能够搞定 2)调查类图,其实IView、IPresenter没有必要存在,由于究竟只是baseActivity、basePresenter的行为
所以改造如下:
BasePresenter
package com.itbird.design.principle.mvp.v2;
import java.lang.ref.WeakReference;
/**
* Created by itbird on 2022/3/29
*/
public abstract class BasePresenter<V> {
WeakReference<V> mIView;
public void onAttach(V iView) {
mIView = new WeakReference<>(iView);
}
public void onDetach() {
mIView = null;
}
public V getView() {
if (mIView != null) {
mIView.get();
}
return null;
}
public boolean isViewAttached() {
return mIView != null && mIView.get() != null;
}
}
BaseActivity
package com.itbird.design.principle.mvp.v2;
import android.app.Activity;
import android.os.Bundle;
import androidx.annotation.Nullable;
/**
* Created by itbird on 2022/3/29
*/
public abstract class BaseActivity<V, T extends BasePresenter<V>> extends Activity {
T mPresenter;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mPresenter = initPresenter();
if (mPresenter != null) {
mPresenter.onAttach((V) this);
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mPresenter != null) {
mPresenter.onDetach();
mPresenter = null;
}
}
public T getPresenter() {
return mPresenter;
}
abstract T initPresenter();
}
此刻,契约类,不再需求依赖BasePresenter/BaseView相关接口,并且View中也能够自己获取到presenter的引用了。
package com.itbird.design.principle.mvp.v2;
/**
* my Demo
* Created by itbird on 2022/2/25
*/
public interface TaskMyContract {
interface View {
//界面UI改写办法
void updateTextView(String s);
}
interface Presenter {
void loadDataFromModel();
}
}
package com.itbird.design.principle.mvp.v2;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;
import com.itbird.design.R;
public class TaskMyActivity extends BaseActivity<TaskMyContract.View, TaskMyPresenter> implements TaskMyContract.View {
private static final String TAG = TaskMyActivity.class.getSimpleName();
TextView mTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextView = findViewById(R.id.textview);
Log.e(TAG, TAG + " onCreate");
mPresenter.loadDataFromModel();
}
@Override
TaskMyPresenter initPresenter() {
return new TaskMyPresenter();
}
@Override
public void updateTextView(String s) {
mTextView.setText(s);
}
}
此刻的类图,是否愈加清晰,并且上面各个问题都现已得到了处理。
整体规划形式Demo代码