MVC
,MVP
和MVVM
都是常见的软件架构设计模式(Architectural Pattern
),它通过分离关注点来改进代码的组织方式。 不同于设计模式(Design Pattern
),只是为了解决一类问题而总结出的抽象方法,一种架构模式往往使用了多种设计模式。
architectural [ˌɑːrkɪˈtektʃərəl] 建筑学的,建筑上的 pattern [ˈpætərn] 模式,方式
MVC
、MVP
和MVVM
不同部分是C(Controller)
、P(Presenter)
、VM(View-Model)
,而相同的部分则是MV(Model-View)
。Model
层用于封装和应用程序的业务逻辑相关的数据以及对数据的处理方法。View
作为视图层,主要负责数据的展示。
MVC
Model
)层:用于封装与应用程序的业务逻辑相关的数据以及对数据的处理方法。Model
层有对数据直接访问的权利,例如访问数据库、获取网络数据。Model
不依赖View
和Controller
,也就是说,Model
不关心它是如何显示或者如何被操作。但是,Model
中数据的变化一般会通过一种刷新机制被公布。为了实现这种机制,那些用于监视此Model
的View
必须事先在此Model
上注册,从而,View
可以了解在数据Model
上发生的改变;View
)层:主要负责数据显示,在View
中一般没有程序上的逻辑。为了实现View
上的刷新功能,View
需要访问它监视的数据模型(Model
),因此应该事先在被它监视的数据那里注册;(Activity(View)/Fragment(View)
,Android
种的xml
文件转换成View
后加载到Activity
/Fragment
中);Controller
)器:用于控制应用程序的流程。它处理事件并作出相应,“事件”包括用户的行为和数据Model
上的改变。Android
控制层的重任往往落在Activity
/Fragment
的肩上;选用这种架构设计的优点是:View
和Model
是隔离的,View
层的变动不会影响到Model
。缺点是:在Android
开发中不适合MVC
模式,View
和Controller
都放在了Activity
/Fragment
中,这要会导致Activity
/Fragment
十分臃肿。
通过以下的Demo
来实现MVC
架构:
以下是Controller
层代码:
class LoginCActivity : AppCompatActivity(), LoginModel.OnDoLoginStateChange {
private val loginModel by lazy {
LoginModel()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_c_login)
initListener()
}
private fun initListener() {
loginBtn.setOnClickListener {
toLogin()
}
}
private fun toLogin() {
val account = accountInput.text.toString()
val password = passwordInput.text.toString()
if (TextUtils.isEmpty(account.trim())) {
ToastUtils.showShort("用户名不可以为空")
return
}
if (TextUtils.isEmpty(password.trim())) {
ToastUtils.showShort("密码不可以为空")
return
}
loginModel.checkAccountState(account) {
when (it) {
0 -> {
ToastUtils.showShort("该用户不存在")
}
1 -> {
loginModel.doLogin(this, account, password)
loginBtn.isEnabled = false
}
}
}
}
override fun onLoading() {
tipsTv.text = "登录中..."
}
override fun onLoginSuccess() {
tipsTv.text = "登录成功"
}
override fun onLoginFailed() {
tipsTv.text = "登录失败"
}
}
以下是Model
层代码:
class LoginModel {
private val random = Random()
fun doLogin(callback: OnDoLoginStateChange, account: String, password: String) {
callback.onLoading()
Thread.sleep(1000)
val randomValue = random.nextInt(2)
if (randomValue == 1) {
callback.onLoginSuccess()
} else {
callback.onLoginFailed()
}
}
fun checkAccountState(account:String,block: (Int) -> Unit) {
block.invoke(random.nextInt(2))
}
interface OnDoLoginStateChange {
fun onLoading()
fun onLoginSuccess()
fun onLoginFailed()
}
}
MVP
在MVC
的设计中,将View
和Controller
的代码放到一起了,这样会导致Activity/Fragment
中的代码越来越多,所以开始分离VC
中的代码。
将Activity/Fragment
中复杂的逻辑处理移至另外的一个类(Presenter
)中,此时,Activity
其实就是MVP
模式中的View
,负责UI
元素的初始化,建立UI
元素与Presenter
的关联(Listener
之类),同时自己也会处理一些简单的逻辑,复杂的逻辑交由Presenter
处理。
Model
)层:用于封装与应用程序的业务逻辑相关的数据以及对数据的处理方法。Model
层有对数据直接访问的权利,例如访问数据库、获取网络数据。Model
不依赖View
和Presenter
,也就是说,Model
不关心它是如何显示或者如何被操作;View
)层:负责显示数据,与用户进行交互。持有Presenter
层的引用。在Android
中体现为Activity/Fragment
;Presenter
)层:负责逻辑处理,通过Model
层获取数据,回显到UI
层,响应用户的行为;通过以下的Demo
来实现MVP
架构,以下是Model
层代码:
class LoginModel {
companion object {
const val STATE_LOADING = 1
const val STATE_SUCCESS = 2
const val STATE_FAILED = 0
}
private val random = Random()
fun doLogin(account: String, password: String, block: (Int) -> Unit) {
block.invoke(STATE_LOADING)
Thread.sleep(1000)
val randomValue = random.nextInt(2)
if (randomValue == 1) {
block.invoke(STATE_SUCCESS)
} else {
block.invoke(STATE_FAILED)
}
}
fun checkAccountState(account: String, block: (Int) -> Unit) {
block.invoke(random.nextInt(2))
}
}
以下是Presenter
层代码:
class LoginPresenter {
private val loginModel by lazy {
LoginModel()
}
fun checkAccountState(account: String, callback: OnCheckAccountStateResultCallback) {
loginModel.checkAccountState(account) {
when (it) {
0 -> {
callback.onNotAvailable()
}
1 -> {
callback.onAvailable()
}
}
}
}
fun doLogin(account: String, password: String, callback: OnDoLoginStateChange) {
if (TextUtils.isEmpty(account.trim())) {
callback.onAccountFormatError()
return
}
if (TextUtils.isEmpty(password.trim())) {
callback.onPasswordEmpty()
return
}
loginModel.doLogin(account, password) {
when (it) {
STATE_LOADING -> {
callback.onLoading()
}
STATE_SUCCESS -> {
callback.onLoginSuccess()
}
STATE_FAILED -> {
callback.onLoginFailed()
}
}
}
}
interface OnCheckAccountStateResultCallback {
fun onNotAvailable()
fun onAvailable()
}
interface OnDoLoginStateChange {
fun onAccountFormatError()
fun onPasswordEmpty()
fun onLoading()
fun onLoginSuccess()
fun onLoginFailed()
}
}
以下是View
层代码:
class LoginCActivity : AppCompatActivity(), LoginPresenter.OnDoLoginStateChange,
LoginPresenter.OnCheckAccountStateResultCallback {
private val loginPresenter by lazy {
LoginPresenter()
}
private var isAccountAvailable = true
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_c_login)
initListener()
}
private fun initListener() {
loginBtn.setOnClickListener {
toLogin()
}
accountInput.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
loginPresenter.checkAccountState(s.toString(), this@LoginCActivity)
}
override fun afterTextChanged(s: Editable?) {
}
})
}
private fun toLogin() {
val account = accountInput.text.toString()
val password = passwordInput.text.toString()
if (!isAccountAvailable) {
return
}
loginPresenter.doLogin(account, password, this)
}
override fun onAccountFormatError() {
tipsTv.text = "账号格式不正确"
this.isAccountAvailable = false
}
override fun onPasswordEmpty() {
tipsTv.text = "密码不可以为空"
}
override fun onLoading() {
tipsTv.text = "登录中..."
}
override fun onLoginSuccess() {
tipsTv.text = "登录成功"
}
override fun onLoginFailed() {
tipsTv.text = "登录失败"
}
override fun onNotAvailable() {
tipsTv.text = "当前用户不可用"
this.isAccountAvailable = false
}
override fun onAvailable() {
tipsTv.text = "当前用户可用..."
this.isAccountAvailable = true
}
}
优点:分离视图层和逻辑层,降低了耦合,模型层与视图层完全分离。解决了业务逻辑复杂时Activity
/Fragment
代码过于臃肿的问题。
缺点:
UI
的操作必须在Activity
与Fragment
的生命周期之内,否则容易出现各种异常,比如内存泄漏。Presenter
无法直接感知View
层的生命周期;Presenter
与View
之间的耦合度高。如果app
中很多界面都使用了同一个Presenter
,每个View
层都可能都会存在一些无关方法,一旦Presenter
层有所变动,相关的个View
都需要调整;以下通过一个例子来说明第二个缺点。以下是页面展示:
以下Presenter
&Model
层的代码:
class PlayerPresenter private constructor() {
companion object {
val instance by lazy {
PlayerPresenter()
}
}
enum class PlayState {
NONE, PLAYING, PAUSE, LOADING
}
private val callbacksList = arrayListOf<IPlayerCallback>()
private var currentPlayState = PlayState.NONE
fun registerCallback(callback: IPlayerCallback) {
if (!callbacksList.contains(callback)) {
callbacksList.add(callback)
}
}
fun unRegisterCallback(callback: IPlayerCallback) {
callbacksList.remove(callback)
}
fun doPlayOrPause() {
dispatchTitleChange("当前播放的歌曲标题")
dispatchCoverChange("当前播放的歌曲封面")
if (currentPlayState != PlayState.PLAYING) {
dispatchPlayingState()
currentPlayState = PlayState.PLAYING
} else {
dispatchPauseState()
currentPlayState = PlayState.PAUSE
}
}
fun playNext() {
dispatchTitleChange("切换到下一首,标题变化了")
dispatchCoverChange("切换到下一首,封面变化了")
currentPlayState = PlayState.PLAYING
}
fun playPre() {
dispatchTitleChange("切换到上一首,标题变化了")
dispatchCoverChange("切换到上一首,封面变化了")
currentPlayState = PlayState.PLAYING
}
private fun dispatchPauseState() {
callbacksList.forEach { it.onPlayingPause() }
}
private fun dispatchPlayingState() {
callbacksList.forEach { it.onPlaying() }
}
private fun dispatchCoverChange(cover: String) {
callbacksList.forEach { it.onCoverChange(cover) }
}
private fun dispatchTitleChange(title: String) {
callbacksList.forEach { it.onTitleChange(title) }
}
}
class PlayerModel {
// 数据处理
}
以下是View
层代码:
class PlayerActivity : AppCompatActivity(), IPlayerCallback {
private val playerPresenter by lazy {
PlayerPresenter.instance
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_player)
playerPresenter.registerCallback(this)
initListener()
}
private fun initListener() {
playerOrPauseBtn.setOnClickListener {
playerPresenter.doPlayOrPause()
}
playNextBtn.setOnClickListener {
playerPresenter.playNext()
}
playPreBtn.setOnClickListener {
playerPresenter.playPre()
}
}
override fun onDestroy() {
super.onDestroy()
playerPresenter.unRegisterCallback(this)
}
override fun onTitleChange(title: String) {
songTitleTv?.text = title
}
override fun onProgressChange(current: Int) {
}
override fun onPlaying() {
playerOrPauseBtn.text = "暂停"
}
override fun onPlayingPause() {
playerOrPauseBtn.text = "播放"
}
override fun onCoverChange(cover: String) {
println("封面更新 $cover")
}
}
class FloatPlayerActivity : AppCompatActivity(), IPlayerCallback {
private val playerPresenter by lazy {
PlayerPresenter.instance
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_float_player)
playerPresenter.registerCallback(this)
initListener()
}
override fun onDestroy() {
super.onDestroy()
playerPresenter.unRegisterCallback(this)
}
private fun initListener() {
playOrPauseBtn.setOnClickListener {
playerPresenter.doPlayOrPause()
}
}
override fun onTitleChange(title: String) {
}
override fun onProgressChange(current: Int) {
}
override fun onPlaying() {
playOrPauseBtn.text = "暂停"
}
override fun onPlayingPause() {
playOrPauseBtn.text = "播放"
}
override fun onCoverChange(cover: String) {
}
}
在FloatPlayerActivity
中没有用到onTitleChange/onProgressChange/onCoverChange
等方法,但是由于实现了IPlayerCallback
接口,所以必须进行方法重写。
进行优化,添加DataListenerController
类,做数据监听:
class DataListenerController<T> {
private val blocks = arrayListOf<(T?) -> Unit>()
var value: T? = null
set(value: T?) {
blocks.forEach { it.invoke(value) }
}
fun addListener(block: (T?) -> Unit) {
if (!blocks.contains(block)) {
blocks.add(block)
}
}
}
修改PlayerPresenter
中的代码:
class PlayerPresenter private constructor() {
private val playerModel by lazy {
PlayerModel()
}
private val player by lazy {
MusicPlayer()
}
var currentMusic = DataListenerController<Music>()
var currentPlayState = DataListenerController<PlayState>()
companion object {
val instance by lazy {
PlayerPresenter()
}
}
enum class PlayState {
NONE, PLAYING, PAUSE, LOADING
}
fun doPlayOrPause() {
if (currentMusic.value == null) {
currentMusic.value = playerModel.getMusicById("卡农")
}
player.play(currentMusic.value)
currentPlayState.value = if (currentPlayState.value != PlayState.PLAYING) {
PlayState.PLAYING
} else {
PlayState.PAUSE
}
}
fun playNext() {
currentMusic.value = playerModel.getMusicById("下一首:梦中的婚礼")
currentPlayState.value = PlayState.PLAYING
}
fun playPre() {
currentMusic.value = playerModel.getMusicById("上一首:一步之遥")
currentPlayState.value = PlayState.PLAYING
}
}
修改PlayerActivity
中的代码:
class PlayerActivity : AppCompatActivity() {
private val playerPresenter by lazy {
PlayerPresenter.instance
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_player)
initDataListener()
initListener()
}
private fun initListener() {
playerOrPauseBtn.setOnClickListener {
playerPresenter.doPlayOrPause()
}
playNextBtn.setOnClickListener {
playerPresenter.playNext()
}
playPreBtn.setOnClickListener {
playerPresenter.playPre()
}
}
private fun initDataListener() {
playerPresenter.currentMusic.addListener {
songTitleTv.text = it?.name
println("封面改变了... ${it?.cover}")
}
playerPresenter.currentPlayState.addListener {
when (it) {
PlayerPresenter.PlayState.PAUSE -> {
playerOrPauseBtn.text = "播放"
}
PlayerPresenter.PlayState.PLAYING -> {
playerOrPauseBtn.text = "暂停"
}
else -> {}
}
}
}
}
对于第一个缺点,如何让Presenter
层感知View
层的生命周期变化?
定义一个和生命周期有关的接口ILifecycle
interface ILifecycle {
fun onCreate()
fun onStart()
fun onResume()
fun onPause()
fun onStop()
fun onDestroy()
}
Presenter
实现这个接口:
class PlayerPresenter private constructor() : ILifecycle {
...
override fun onCreate() {
}
override fun onStart() {
}
override fun onResume() {
}
override fun onPause() {
}
override fun onStop() {
}
override fun onDestroy() {
}
}
在View
层的生命周期方法中调用:
class PlayerActivity : AppCompatActivity() {
...
override fun onStart() {
super.onStart()
playerPresenter.onStart()
}
override fun onResume() {
super.onResume()
playerPresenter.onResume()
}
override fun onPause() {
super.onPause()
playerPresenter.onPause()
}
override fun onStop() {
super.onStop()
playerPresenter.onStop()
}
override fun onDestroy() {
super.onDestroy()
playerPresenter.onDestroy()
}
}
进一步优化:抽取BaseActivity
让其它的Activity
也可以通知实现ILifecycle
的Presenter
生命周期状态变化:
open class BaseActivity : AppCompatActivity() {
private val lifecycleListener = arrayListOf<ILifecycle>()
open fun addLifeListener(listener: ILifecycle) {
if (!lifecycleListener.contains(listener)) {
lifecycleListener.add(listener)
}
}
open fun removeLifeListener(listener: ILifecycle) {
lifecycleListener.remove(listener)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
lifecycleListener.forEach {
it.onCreate()
}
}
override fun onStart() {
super.onStart()
lifecycleListener.forEach {
it.onStart()
}
}
override fun onResume() {
super.onResume()
lifecycleListener.forEach {
it.onResume()
}
}
override fun onPause() {
super.onPause()
lifecycleListener.forEach {
it.onPause()
}
}
override fun onStop() {
super.onStop()
lifecycleListener.forEach {
it.onStop()
}
}
override fun onDestroy() {
super.onDestroy()
lifecycleListener.forEach {
it.onDestroy()
}
}
}
Presenter
层实现ILifecycle
接口,并实现其中的方法:
class PlayerPresenter private constructor() : ILifecycle {
private val playerModel by lazy {
PlayerModel()
}
private val player by lazy {
MusicPlayer()
}
var currentMusic = DataListenerController<Music>()
var currentPlayState = DataListenerController<PlayState>()
companion object {
val instance by lazy {
PlayerPresenter()
}
}
enum class PlayState {
NONE, PLAYING, PAUSE, LOADING
}
fun doPlayOrPause() {
if (currentMusic.value == null) {
currentMusic.value = playerModel.getMusicById("卡农")
}
player.play(currentMusic.value)
currentPlayState.value = if (currentPlayState.value != PlayState.PLAYING) {
PlayState.PLAYING
} else {
PlayState.PAUSE
}
}
fun playNext() {
currentMusic.value = playerModel.getMusicById("下一首:梦中的婚礼")
currentPlayState.value = PlayState.PLAYING
}
fun playPre() {
currentMusic.value = playerModel.getMusicById("上一首:一步之遥")
currentPlayState.value = PlayState.PLAYING
}
override fun onCreate() {
println("开始监听网络变化")
}
override fun onStart() {
}
override fun onResume() {
}
override fun onPause() {
}
override fun onStop() {
println("结束监听网络变化")
}
override fun onDestroy() {
}
}
在View
层添加监听:
class PlayerActivity : BaseActivity() {
private val playerPresenter by lazy {
PlayerPresenter.instance
}
init {
addLifeListener(playerPresenter)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_player)
initDataListener()
initListener()
}
private fun initListener() {
playerOrPauseBtn.setOnClickListener {
playerPresenter.doPlayOrPause()
}
playNextBtn.setOnClickListener {
playerPresenter.playNext()
}
playPreBtn.setOnClickListener {
playerPresenter.playPre()
}
}
private fun initDataListener() {
playerPresenter.currentMusic.addListener {
songTitleTv.text = it?.name
println("封面改变了... ${it?.cover}")
}
playerPresenter.currentPlayState.addListener {
when (it) {
PlayerPresenter.PlayState.PAUSE -> {
playerOrPauseBtn.text = "播放"
}
PlayerPresenter.PlayState.PLAYING -> {
playerOrPauseBtn.text = "暂停"
}
else -> {}
}
}
}
}
MVP
如何设计避免内存泄漏?
MVP
模式在封装的时候会造成内存泄漏,因为Presenter
层,需要做网络请求,所以就需要考虑到网络请求的取消操作,如果不处理,Activity
销毁了,Presenter
层还在请求网络,就会造成内存泄漏。
如何解决MVP
模式造成的内存泄漏?只要Presenter
层能感知Activity
生命周期的变化,在Activity
销毁的时候,取消网络请求,就能解决这个问题。
MVC
与MVP
区别
View
与Model
并不直接交互,而是通过与Presenter
交互来与Model
间接交互。而在MVC
中View
可以与Model
直接交互。MVP
隔离了MVC
中的M
与V
的直接联系后,靠Presenter
来中转,所以使用MVP
时P
是直接调用View
的接口来实现对视图的操作的。
MVVM
Model
):即数据模型,用于获取和存储数据;ViewModel
):与Presenter
大致相同,都是负责处理数据和实现业务逻辑,但ViewModel
层不应该直接或者间接地持有View
层的任何引用;View
):包含布局,以及布局生命周期控制器(Activity/Fragment
);MVVM
使用了观察者模式,可以算是MVP
的升级版,ViewModel
层的功能和MVP
中的Presenter
层类似,都是用来进行逻辑处理的。在MVVM
中,ViewModel
层和View
层的交互是通过DataBinding
(数据双向绑定)来完成的,ViewModel
层可以监听视图产生的事件,同样的,当Model
层的数据发生变化时,也是通过ViewModel
层对视图上的内容进行更新的。因此,Data Binding
减轻了MVP
中的Presenter
层和View
层的互动职责,降低了耦合性。
MVVM
的优点:
Jetpack
,写出更优雅的代码;缺点:
ViewModel
与View
层的通信变得更加困难了,在一些比较简单的页面中要酌情使用,对于MVP
这个道理也依然适用。
https://blog.csdn.net/lihaoxiang123/article/details/78977181
https://blog.csdn.net/caijunfen/article/details/78478438