Android

您所在的位置:网站首页 mvp设计模式可以用于bs架构吗对吗 Android

Android

2024-07-17 19:55:05| 来源: 网络整理| 查看: 265

MVP模式的介绍

这里写图片描述

简单介绍

Android中的设计模式很多,MVP是目前比较流行的一种设计模式,全称为Model-View-Presenter。MVP模式能有效地降低View的复杂性,避免业务逻辑被塞入View中,使得View变得更为简单专一。MVP模式会解除View和Model的耦合,同时又带来良好的可拓展性、可测试性,保证系统整洁性、灵活性。对于一个复杂的应用来说,MVP模式是一种良好的架构模式,它可以非常好地组织应用结构,使得应用变得灵活,拥抱变化。

MVP中各自的职责

MVP模式解耦了View和Model,使用Presenter来处理业务逻辑。View层主要用Activity和Fragment来体现,View层主要负责用户交互部分,展示数据等,主要为UI部分,View中持有Presenter实例,通过该实例来调用Presenter层的方法来实现交互。Model层主要对数据的处理,包括获取数据和保存数据等,如:从网络获取数据,从数据库操作数据等。MVP模式中,Model层和View层没有直接联系,各自负责各自的职责。业务逻辑部分由Presenter来负责,如何获取数据,如何展示数据,都是放在Presenter中进行处理,Presenter中持有View和Model的实例。

MVP的好处

MVP模式下,项目中会多出很多类和接口,虽然多出了这些类和接口,但是这样便于我们维护,使用MVP模式来写项目,可以说是面向接口编程,调用的方法基本都是抽象的,直接使用接口来调用,提高可维护性。在MVP模式中,一般我们都会将要调用的方法抽象成接口,Model、View、Presenter接口,在各实现类中,直接对接口进行操作。讲到这里,好像没有说到MVP的好处,接下来说下我使用MVP后的一些感想。 1. 如果哪一天,APP要升级,界面要修改,但是,但是展示的数据源没有变化,这时候,我只需要修改View层的代码,其他代码不需要修改。假设把数据处理业务逻辑都融合到View中的话,修改起来会比较麻烦,改动的代码会很多。 2. 如果哪天,有更好到网络框架出现,你很想换掉原先的网络框架,那么,你原先写的Model层代码就要做相应的修改,这样,你只需要对Model部分进行修改,不需要对Presenter和View进行修改。 3. 假设业务也要该了,那只需要修改Presenter层就行,不需要修改其他两个模块。 总结:使用MVP模式,后面改动代码可以更有针对性、目的性。

MVP模式使用

上面大致提到了,在使用上,Activity和Fragment为主要的View层,在View中,View持有Presenter,并调用Presenter接口来实现一些业务操作。在Presenter中,将持有View和Model实例,从而实现业务逻辑的处理等。Model中主要实现数据处理,一般为异步从网络获取数据等。

那么问题来了!Presenter中持有View实例,也就是说,Presenter中持有Activity等实例,在Presenter中又会调用Model层去进行一些数据处理的耗时操作,有一种情况,当用户调起从网络中获取数据,但是数据还没加载出来,用户退出了该Activity,此时,Model还在进行异步操作,这是,由于Presenter对View是强引用,所以,当Activity结束的时候,该Activity实例还被Presenter持有,此刻将出现内存泄漏问题。所以,为了处理好这种情况,我们要可以对Presenter中对View进行弱引用。

有人可以会问,从上面的关系图中看出,Presenter直接访问Model层,但是Model层中没有对Presenter进行处理,那么怎样做数据交互呢? 一般的,我们自定义一个回调接口,通过回调接口来实现Presenter和Model的交互。

下来简单用一个例子来说明一下MVP模式的使用

为了不让Presenter对View强引用而出现内存泄漏,下面代码我抽象出BasePresenter类和BaseActivity类来实现Presenter对View的若应用,并在Activity的onDestroy()中解除引用。

BasePresenter的编写 import java.lang.ref.Reference; import java.lang.ref.WeakReference; /** * 对presenter进行抽象 * 提供View和Presenter进行绑定的操作,使得Presenter对View进行弱引用,避免发生内存泄漏 * Created by sunxuedian on 2017/9/3. */ public abstract class BasePresenter { private Reference mViewRef; public void attachView(T view){ mViewRef = new WeakReference(view);//建立关联 } protected T getView(){ return mViewRef.get(); } public boolean isViewAttached(){ return mViewRef != null && mViewRef.get()!= null; } public void detachView(){ if (mViewRef != null){ mViewRef.clear(); mViewRef = null; } } } BaseActivity的编写 import android.app.Activity; import android.os.Bundle; import android.support.annotation.Nullable; import com.sunxuedian.mvplearn.presenter.BasePresenter; /** * Created by sunxuedian on 2017/9/3. */ public abstract class BaseActivity extends Activity{ protected P mPresenter;//Presenter对象 @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); mPresenter = createPresenter();//创建Presenter mPresenter.attachView((View)this);//View和Presenter建立关联 } @Override protected void onDestroy() { super.onDestroy(); mPresenter.detachView();//解除View和Presenter的关联 } public abstract P createPresenter(); } 下面的项目我使用一个简单的例子,日记的简单管理(只提供添加和显示所有日记) 先看下项目的目录结构吧

这里写图片描述

Presenter部分

接口部分

/** * Created by sunxuedian on 2017/9/3. */ public interface IDiaryPresenter { void initPresenter();//初始化 void addDiary();//添加一条日记 void getAllDiaries();//获取所有日记 }

实现类

import android.text.TextUtils; import com.sunxuedian.mvplearn.bean.DiaryBean; import com.sunxuedian.mvplearn.model.IDiaryModel; import com.sunxuedian.mvplearn.model.impl.DiaryModelImpl; import com.sunxuedian.mvplearn.presenter.BasePresenter; import com.sunxuedian.mvplearn.presenter.IDiaryPresenter; import com.sunxuedian.mvplearn.presenter.callback.IPresenterCallBack; import com.sunxuedian.mvplearn.view.IDiaryView; import java.util.List; /** * Created by sunxuedian on 2017/9/3. */ public class DiaryPresenterImpl extends BasePresenter implements IDiaryPresenter { private IDiaryView mView; private IDiaryModel mModel; @Override public void initPresenter() { mView = getView();//调用BasePresenter中的方法获取到View mModel = new DiaryModelImpl(); } @Override public void addDiary() { if (TextUtils.isEmpty(mView.getNewDiaryTitle()) || TextUtils.isEmpty(mView.getNewDiaryContent())){ mView.showFailure("标题和内容不能为空!"); return; } final DiaryBean diaryBean = new DiaryBean(); diaryBean.setTitle(mView.getNewDiaryTitle()); diaryBean.setContent(mView.getNewDiaryContent()); diaryBean.setLastEditTime(mView.getNewDiaryCreateTime()); mView.showLoading(); mModel.addDiary(diaryBean, new IPresenterCallBack() { @Override public void onSuccess(String data) { mView.hideLoading(); mView.showAddDiarySuccess(diaryBean); } @Override public void onFailure(String msg) { mView.hideLoading(); mView.showFailure(msg); } }); } @Override public void getAllDiaries() { mView.showLoading(); mModel.getAllDiaries(new IPresenterCallBack() { @Override public void onSuccess(List data) { mView.hideLoading(); if (data != null && data.size() > 0){ mView.showDiaries(data); }else { mView.showFailure("日记为空!请先添加日记!"); } } @Override public void onFailure(String msg) { mView.hideLoading(); mView.showFailure(msg); } }); } } 这里说明一下,为什么不在构造函数中获取View,因为,在构造函数中获取View的时候,Presenter和View还没关联起来,Presenter和View的关联主要在View中的Activity中的onCreate()中关联,但是关联前,需要通过创建一个Presenter对象,所以这里不能在构造函数中进行获取View。 View层

View层定义的接口

import com.sunxuedian.mvplearn.bean.DiaryBean; import java.util.Date; import java.util.List; /** * Created by sunxuedian on 2017/9/3. */ public interface IDiaryView { void showLoading();//显示进度条 void hideLoading();//隐藏进度条 String getNewDiaryTitle();//获取新建日记的标题 String getNewDiaryContent();//获取日记内容 Date getNewDiaryCreateTime();//获取日记的创建时间 void showAddDiarySuccess(DiaryBean diaryBean);//显示添加日记成功 void showDiaries(List diaryBeanList);//显示日记列表 void showFailure(String msg);//显示操作失败信息 }

Activity实现View接口

import android.app.ProgressDialog; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.EditText; import android.widget.ListView; import android.widget.Toast; import com.sunxuedian.mvplearn.adapter.DiaryListAdapter; import com.sunxuedian.mvplearn.bean.DiaryBean; import com.sunxuedian.mvplearn.presenter.IDiaryPresenter; import com.sunxuedian.mvplearn.presenter.impl.DiaryPresenterImpl; import com.sunxuedian.mvplearn.view.BaseActivity; import com.sunxuedian.mvplearn.view.IDiaryView; import java.util.Date; import java.util.List; public class MainActivity extends BaseActivity implements IDiaryView { private DiaryListAdapter mAdapter; private ProgressDialog mProgressDialog; private EditText mEtTitle; private EditText mEtContent; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); mPresenter.initPresenter();//初始化presenter mPresenter.getAllDiaries();//调用presenter获取日记列表 } private void initView(){ //为按钮添加点击事件 findViewById(R.id.btn_add).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { mPresenter.addDiary();//调用presenter进行添加日记 } }); findViewById(R.id.btn_show).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { mPresenter.getAllDiaries();//获取日记列表 } }); //初始化编辑框 mEtTitle = findViewById(R.id.et_title); mEtContent = findViewById(R.id.et_content); //为listView绑定adapter mAdapter = new DiaryListAdapter(this); ListView listView = findViewById(R.id.lv_show); listView.setAdapter(mAdapter); //初始化进度条对话框 mProgressDialog = new ProgressDialog(this); mProgressDialog.setTitle(null); mProgressDialog.setMessage("进行中..."); mProgressDialog.setCancelable(false); } @Override public DiaryPresenterImpl createPresenter() { return new DiaryPresenterImpl(); } @Override public void showLoading() { if (!mProgressDialog.isShowing()){ mProgressDialog.show(); } } @Override public void hideLoading() { if (mProgressDialog.isShowing()){ mProgressDialog.dismiss(); } } @Override public String getNewDiaryTitle() { return mEtTitle.getText().toString().trim(); } @Override public String getNewDiaryContent() { return mEtContent.getText().toString().trim(); } @Override public Date getNewDiaryCreateTime() { return new Date(System.currentTimeMillis()); } @Override public void showAddDiarySuccess(DiaryBean diaryBean) { showToast("添加成功!"); //将新增的日记显示在列表中 mAdapter.addItem(diaryBean); mAdapter.notifyDataSetChanged(); } @Override public void showDiaries(List diaryBeanList) { //显示获取到的列表 mAdapter.setData(diaryBeanList); mAdapter.notifyDataSetChanged(); } @Override public void showFailure(String msg) { showToast(msg); } private void showToast(String msg){ Toast.makeText(this, msg, Toast.LENGTH_SHORT).show(); } } 可以看出,View层只负责用户交互,代码逻辑清晰 Model层

Model定义的接口

import com.sunxuedian.mvplearn.bean.DiaryBean; import com.sunxuedian.mvplearn.presenter.callback.IPresenterCallBack; import java.util.List; /** * Created by sunxuedian on 2017/9/3. */ public interface IDiaryModel { void addDiary(DiaryBean diaryBean, IPresenterCallBack presenterCallBack); void getAllDiaries(IPresenterCallBack presenterCallBack);//获取所有日记数据 boolean isDiaryExist(DiaryBean diaryBean);//判断日记是否存在 }

Model层的实现

import android.os.Handler; import com.sunxuedian.mvplearn.bean.DiaryBean; import com.sunxuedian.mvplearn.model.IDiaryModel; import com.sunxuedian.mvplearn.presenter.callback.IPresenterCallBack; import java.util.ArrayList; import java.util.List; /** * 为了方便演示,此处先将数据保存到内存中,不保存到网络或数据库 * Created by sunxuedian on 2017/9/3. */ public class DiaryModelImpl implements IDiaryModel { private ArrayList mData = new ArrayList(); private Handler mHandler = new Handler(); @Override public void addDiary(final DiaryBean diaryBean, final IPresenterCallBack presenterCallBack) { Thread thread = new Thread(new Runnable() { @Override public void run() { //模拟网络请求 try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } mHandler.post(new Runnable() { @Override public void run() { if (isDiaryExist(diaryBean)){ presenterCallBack.onFailure("该标题的日记已经存在!"); }else { mData.add(diaryBean); presenterCallBack.onSuccess("添加成功!"); } } }); } }); thread.start(); } @Override public void getAllDiaries(final IPresenterCallBack presenterCallBack) { //模拟网络请求 Thread thread = new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(1500); } catch (InterruptedException e) { e.printStackTrace(); } mHandler.post(new Runnable() { @Override public void run() { if (mData != null){ presenterCallBack.onSuccess(mData); }else { presenterCallBack.onFailure("系统错误"); } } }); } }); thread.start(); } @Override public boolean isDiaryExist(DiaryBean diaryBean) { return mData.contains(diaryBean); } } 再来看看Model和Presenter交互的接口吧 /** * Created by sunxuedian on 2017/9/3. */ public interface IPresenterCallBack { void onSuccess(T data); void onFailure(String msg); } 日记实体类 import java.util.Date; /** * 日记的实体类 * Created by sunxuedian on 2017/9/3. */ public class DiaryBean { private String title; private String content; private Date lastEditTime; public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } public Date getLastEditTime() { return lastEditTime; } public void setLastEditTime(Date lastEditTime) { this.lastEditTime = lastEditTime; } @Override public boolean equals(Object obj) { if (obj instanceof DiaryBean){ DiaryBean diaryBean = (DiaryBean) obj; return title.equals(diaryBean.getTitle()); } return false; } } 这样一个使用MVP设计模式编写的例子就写好啦,大概的逻辑我再讲解一下 添加日记,在MainActivity中调用IDiaryPresenter的addDiary()接口,然后如何实现就交给DiaryPresenterImp实体类中的addDiary去实现(通过获取标题内容等构造一个日记对象,然后调用IDiaryModel的addDiary(DiaryBean diaryBean, IPresenterCallBack presenterCallBack))来实现添加日记。显示日记列表,在MainActivity中调用IDiaryPresenter的getAllDiaries()接口,然后DiaryPresenterImp通过调用IDiaryModel的获取日记列表接口,但是获取到数据时,将数据传给View去展示。 若有讲得不对或不太理解的,欢迎留言讨论,以上就是我对MVP模式的一个理解。

项目代码:GitHub 项目地址



【本文地址】

公司简介

联系我们

今日新闻


点击排行

实验室常用的仪器、试剂和
说到实验室常用到的东西,主要就分为仪器、试剂和耗
不用再找了,全球10大实验
01、赛默飞世尔科技(热电)Thermo Fisher Scientif
三代水柜的量产巅峰T-72坦
作者:寞寒最近,西边闹腾挺大,本来小寞以为忙完这
通风柜跟实验室通风系统有
说到通风柜跟实验室通风,不少人都纠结二者到底是不
集消毒杀菌、烘干收纳为一
厨房是家里细菌较多的地方,潮湿的环境、没有完全密
实验室设备之全钢实验台如
全钢实验台是实验室家具中较为重要的家具之一,很多

推荐新闻


    图片新闻

    实验室药品柜的特性有哪些
    实验室药品柜是实验室家具的重要组成部分之一,主要
    小学科学实验中有哪些教学
    计算机 计算器 一般 打孔器 打气筒 仪器车 显微镜
    实验室各种仪器原理动图讲
    1.紫外分光光谱UV分析原理:吸收紫外光能量,引起分
    高中化学常见仪器及实验装
    1、可加热仪器:2、计量仪器:(1)仪器A的名称:量
    微生物操作主要设备和器具
    今天盘点一下微生物操作主要设备和器具,别嫌我啰嗦
    浅谈通风柜使用基本常识
     众所周知,通风柜功能中最主要的就是排气功能。在

    专题文章

      CopyRight 2018-2019 实验室设备网 版权所有 win10的实时保护怎么永久关闭