1、前沿
时隔2年我又重新回来审视了我这篇当时入门的文章,也正如文下读者所说当时确实是在MVP的Activity中开辟了线程的,且不说在页面退出时线程仍然未结束且持有activity会造成内存泄漏,再有这完全违背了MVP的设计思想,我提一点在MVP中Activity仅仅作为View提供UI操作(2020-07-30)。
2、简介
这是一篇基础的mvp案例的demo,也是提供一个记录的作用,由于是一个小白,烦请写的不当的地方能指出来,共同进步,谢谢。mvp的概念啥的我就不再累赘了,网上资源很多,我以为 mvp能通过增加类的方式让程序结构清晰,活动充当View,仅仅就是用来显示数据的,Model层就是各种数据Bean,而核心的P层起承上启下的作用,View层通过调用P层方法,让P层去做复杂的逻辑处理,诸如网络请求和数据库读取,P层将数据处理完了之后让View层去更新操作。当活动或者Fragment结束时,解除与P层的绑定,那么即便此时P层有耗时操作,也不会影响活动的正常退出,那么也就规避了内存泄漏带来的问题。
如上简介当时写的比较笼统,这里我也不删除了,我需要做一些补充(2020-07-30)
在说明MVP之前我想得对MVC得有个充分的了解
MVC vs MVP
MVP相较MVC最大的一个特点就是 M与V完全解耦,M与V的交互完全通过P层来中介传递
MVC
在Android中
- M 包括网络请求、数据库操作、数据Bean
- V 对应xml
- C 对应Activity 当用户操作数据,view得到响应,通过C来执行M层数据变化;数据改变通过C来改变UI的显示
MVP
- M 包括网络请求、数据库操作、数据Bean
- V 对应Activity以及一些列的View
- P M与V的桥梁,P作为中间人协调M与V的工作
小结
如上可以看出,
在Android中Activity在MVC和MVP中承担的责任不同,在MVC中承担着C的职责,而在MVP中承担着V的职责;
在MVP中M与V完全解耦,我们把M与V更好的分离也更符合Android对于UI刷新的思想定义,我们需要在子线程中做网络请求而我们又需要在主线程中刷新UI;
3、案例
案例实现的是,点击图像然后更换图片进行提交,处理成功后显示的过程。
V层基类
// 因为不可避免的涉及到进度条,故而有必要抽离出来
public interface BaseView {
void showLoadingDialog();
void hideLoadingDialog();
}
P层基类
因为P层持有了活动的引用了,那么当活动主动结束时,可通过detachView解除V与P的绑定,那么活动即可正常的被回收,可以规避内存泄漏。
/**
* @author zxl
* @date 2018/4/17
*
* 关于Reference解释: 一个接口即引用
* 共计四个实现类: SoftReference;WeakReference;PhantomReference;FinalizerReference
*
* SoftReference: 软引用,引用的对象发生GC时,假若内存比较紧张,就会释放其所占的内存,充实的话便不会释放
* WeakReference: 弱引用,所引用的对象发生GC时,不管内存如何都会释放其所占用的内存
* PhantomReference: /['fæntəm]/ 虚引用,无法引用一个对象
*
*/
public class BasePresenter<V extends BaseView> {
public BaseActivity mContext;
public BasePresenter(BaseActivity context) {
mContext = context;
}
protected Reference<V> mViewRef;
public void attachView(V view){
this.mViewRef = new WeakReference<>(view);
}
public void detachView(){
if (mViewRef != null){
mViewRef.clear();
mViewRef = null;
}
}
public V getView(){
return mViewRef !=null?mViewRef.get():null;
}
}
BaseActivity
public abstract class BaseActivity<V extends BaseView,T extends BasePresenter<V>> extends SupportActivity {
protected T mPresenter;
protected Context mContext;
@SuppressWarnings("SpellCheckingInspection")
private Unbinder mUnbinder = null;
@SuppressWarnings("unchecked")
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
doBeforeSetContentView();
mContext = this;
init();
mPresenter = createPresenter();
if (mPresenter != null) {
//因为之后所有的子类都要实现对应的View接口
mPresenter.attachView((V) this);
}
setContentView(provideContentViewId());
mUnbinder = ButterKnife.bind(this);
initView(savedInstanceState);
initData();
initListener();
}
private void doBeforeSetContentView() {
}
//在setContentView()调用之前调用,可以设置WindowFeature(如:this.requestWindowFeature(Window.FEATURE_NO_TITLE);)
protected abstract void init();
//得到当前界面的布局文件id(由子类实现)
protected abstract int provideContentViewId();
//用于创建Presenter和判断是否使用MVP模式(由子类实现)
protected abstract T createPresenter();
protected abstract void initView(Bundle savedInstanceState);
protected abstract void initData();
protected abstract void initListener();
public void jumpToActivity(Intent intent) {
startActivity(intent);
}
public void jumpToActivity(Class activity) {
Intent intent = new Intent(this, activity);
startActivity(intent);
}
/**
* 含有bundle跳转
* */
public void jumpToActivity(Class cls,Bundle bundle){
Intent intent = new Intent(this,cls);
if (bundle != null){
intent.putExtras(bundle);
}
startActivity(intent);
}
public void jumpToActivityForResult(Class cls,int requestCode){
jumpToActivityForResult(cls,null,requestCode);
}
public void jumpToActivityForResult(Class cls,Bundle bundle,int requestCode){
Intent intent = new Intent(this,cls);
if (bundle != null){
intent.putExtras(bundle);
}
startActivityForResult(intent,requestCode);
}
public void jumpToActivityAndClearTask(Class activity) {
Intent intent = new Intent(this, activity);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
finish();
}
// 这里可以自行去封装
public void showLoadingDialog() {
PerfectLoader.showLoading(mContext, LoaderStyle.BallScaleRippleMultipleIndicator);
}
public void hideLoadingDialog() {
PerfectLoader.stopLoading();
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
isConfigChange=true;
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mPresenter != null) {
mPresenter.detachView();
}
if (mUnbinder != null) {
mUnbinder.unbind();
}
}
}
具体View层
/**
* @author crazyZhangxl on 2018/9/27.
* Describe: 个人中心的V层
*/
public interface IPersonalAtView extends BaseView {
void postHeadSuccess(String imagePath);
void postHeadFailure(String error);
}
具体的P层
/**
* @author crazyZhangxl on 2018/9/27.
* Describe: 个人中心的P层
*/
public class PersonalAtPresenter extends BasePresenter<IPersonalAtView> {
public PersonalAtPresenter(BaseActivity context) {
super(context);
}
@SuppressLint("CheckResult")
public void doPostHeadImage(String imagePath){
getView().showLoadingDialog();
ApiRetrofit.getInstance().postHeadImage(new File(imagePath))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(baseResponse -> {
getView().hideLoadingDialog();
if (baseResponse.getCode() == 1){
getView().postHeadSuccess(baseResponse.getData().getImage());
}else {
getView().postHeadFailure(baseResponse.getMsg());
}
}, throwable -> {
getView().hideLoadingDialog();
getView().postHeadFailure(throwable.getLocalizedMessage());
});
}
}
Activity
/**
* @author zxl on 2018/09/26.
* Describe: 个人中心活动,活动中不能有耗时操作
*/
public class PersonalActivity extends BaseActivity<IPersonalAtView,PersonalAtPresenter> implements IPersonalAtView {
@BindView(R.id.img_bg)
ImageView imgBg;
@BindView(R.id.wave_view)
WaveView waveView;
@BindView(R.id.img_avater)
CircleImageView imgAvater;
@BindView(R.id.tv_name)
TextView tvName;
@BindView(R.id.ivToolbarNavigation)
ImageView ivToolbarNavigation;
public List<LocalMedia> mSingleSelectList = new ArrayList<>();
@BindView(R.id.stv_about_cloud)
SuperTextView stvAboutCloud;
@BindView(R.id.stv_logout)
SuperTextView stvLogout;
private View mExitView;
private CustomDialog mExitDialog;
@Override
protected void init() {
}
@Override
protected int provideContentViewId() {
return R.layout.activity_personal;
}
@Override
protected PersonalAtPresenter createPresenter() {
return new PersonalAtPresenter(this);
}
@Override
protected void initView(Bundle savedInstanceState) {
}
@Override
protected void initData() {
}
@Override
protected void initListener() {
// 设置单张图片的点击事件
imgAvater.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
PictureSHelper.getInstance().
chooseSinglePictureEvent(PersonalActivity.this,mSingleSelectList,PictureConfig.SINGLE);
}
});
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == Activity.RESULT_OK) {
switch (requestCode) {
case PictureConfig.SINGLE:
mSingleSelectList = PictureSelector.obtainMultipleResult(data);
// 执行P层的上传图片
mPresenter.doPostHeadImage(mSingleSelectList.get(0).getPath());
break;
default:
break;
}
}
}
/**
* 上传头像
* @param imagePath
*/
@Override
public void postHeadSuccess(String imagePath) {
UserCache.setUserImage(imagePath);
mSingleSelectList.clear();
GlideLoaderUtils.display(this,imgAvater,imagePath);
}
@Override
public void postHeadFailure(String error) {
UIUtils.showToast(error);
}
}
好了,以上就是一个完整的mvp的小例子,由于是项目代码故而没法把源码给齐。后面也会考虑将以上的完善。前辈提示的是,活动中千万别做耗时操作,那么这和mvc就没区别了,要充分利用mvp的优势。