我们从MVP开始聊起!
一.解决mvp中的内存泄漏问题
我们知道MVP的架构模式是为了解决mvc的中Activity中臃肿耦合问题,Present主要负责从model层拿数据,去通知view修改ui,解决了耦合问题,但是prensenter也有它的问题,比如ui销毁时的内存泄漏问题,当界面销毁时,present的才从model层拿到数据去通知view修改界面,一般情况下,我们让Activity在销毁时将present中Activity置空,并清除掉其他请求的操作,来解决这样的问题:
伪代码展示:
class PlayActivity : AppCompatActivity(), PlayView {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_play)
PlayPresent.instance.attach(this)
PlayPresent.instance.getMusic()
}
override fun onDestroy() {
super.onDestroy()
PlayPresent.instance.detachView()
}
override fun onPlayOrPause() {
println("mvp onPlayOrPause->>>>>>>>>>>>")
}
override fun onTitleChange(title: String) {
}
override fun onProgressChange(current: Int) {
}
override fun onPlaying() {
}
override fun onCoverChange(cover: String) {
}
}
interface PlayView {
fun onPlayOrPause()
fun onTitleChange(title: String)
fun onProgressChange(current: Int)
fun onPlaying()
fun onCoverChange(cover: String)
}
class PlayPresent private constructor(){
private val playModel = PlayModel()
private var currentPlayView: WeakReference<PlayView>? = null;
private var currentMusic: Music? = null;
companion object {
val instance by lazy {
PlayPresent()
}
}
fun attach(playView: PlayView) {
currentPlayView = WeakReference(playView);
}
fun getMusic() {
println("mvp getMusic->>>>>>>")
playModel.getMusicById(0, object : PlayModel.PlayModelCallBack {
override fun onSuccessMusic(music: Music) {
currentMusic = music
dispatchViewState()
}
})
}
private fun dispatchViewState() {
currentPlayView!!.get()?.let {
it.onPlayOrPause()
}
}
fun detachView() {
println("mvp detachView->>>>>>>")
currentPlayView = null
}
}
那有没有其他方式来解决呢?
jetPack为我们提供了Lifecycle,它让我们在present中可以轻而易举的感知Activity的生命周期
上面的Presenter就可以改造为:
class PlayPresent private constructor() : LifecycleEventObserver {
private val playModel = PlayModel()
private var currentPlayView:PlayView? = null;
private var currentMusic: Music? = null;
companion object {
val instance by lazy {
PlayPresent()
}
}
fun attach(playView: PlayView) {
currentPlayView = playView
if (playView is LifecycleOwner) {
playView.lifecycle.addObserver(this)
}
}
fun getMusic() {
println("mvp getMusic->>>>>>>")
playModel.getMusicById(0, object : PlayModel.PlayModelCallBack {
override fun onSuccessMusic(music: Music) {
currentMusic = music
dispatchViewState()
}
})
}
private fun dispatchViewState() {
currentPlayView!!.get()?.let {
it.onPlayOrPause()
}
}
fun detachView() {
println("mvp detachView->>>>>>>")
currentPlayView = null
}
/**
* 感知Activity的生命周期
*/
override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
println("mvp onStateChanged->>>>>$event")
if (event == Lifecycle.Event.ON_DESTROY) {
detachView()
}
}
我这里只是想引出LifeCycle,并没有对mvp这些进行封装,通过lifeCycle,我们可以在present中,添加Activity生命周期的观察者,就可以感知他的生命周期,解除present和Activity的绑定,从而避免了内存泄漏,并且不需要在Activity中主动调用。
二.解决mvp中的接口回调繁杂的问题
我们知道mvp帮助我们做到了充分解耦,它除了可能会导致上面的内存泄漏问题,还有一个问题就是接口回调繁杂的问题。例如上面的例子,我们需要加一个锁屏或者通知栏中的播放状态的Acvitiy,我们同样也使用这个Present,我们对PlayView进行下改造,增加几个方法
interface PlayView {
fun onTitleChange(title: String)
fun onProgressChange(current: Int)
fun onPlaying()
fun onPausePlay()
fun onCoverChange(cover: String)
}
class FlowPlayerActivity : AppCompatActivity(), PlayView {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_flow_player)
title = "FlowPlayerActivity"
PlayPresent.instance.attach(this)
PlayPresent.instance.getMusic()
initListener()
}
private fun initListener() {
playOrPauseBtn.setOnClickListener {
PlayPresent.instance.doPlayOrPause()
}
}
override fun onDestroy() {
super.onDestroy()
// PlayPresent.instance.detachView()
}
override fun onTitleChange(title: String) {
}
override fun onProgressChange(current: Int) {
}
override fun onPlaying() {
runOnUiThread {
println("mvp FlowPlayerActivity onPlaying->>>>>>>>>>>>")
playOrPauseBtn.text = "播放状态"
}
}
override fun onPausePlay() {
runOnUiThread {
println("mvp FlowPlayerActivity onPausePlay->>>>>>>>>>>>")
playOrPauseBtn.text = "暂停状态"
}
}
override fun onCoverChange(cover: String) {
}
}
我们在Flow中其实有些接口回调其实是用不上的,但是我们又不得不写上去,这个接口的颗粒度就不好掌握,我们试着从数据驱动UI的角度来思考,我们去写一个数据容器,这个容器可以包装任意类型,并且在present中获得或修改数据的状态,在view层中通过获取观察present中数据的状态,来修改ui,需要注意的是,这个数据容器也要监听view的生命周期,防止内存泄漏,那怎么定义这个数据容器呢?我们来看看下面的代码
class DataListenerContainer<T> {
private val blocks = arrayListOf<(T) -> Unit>()
private val viewLifeCycleOwner = hashMapOf<(T) -> Unit, LifecycleOwner>()
/**
*
* 方法执行时 会执行
* block的回调
*/
var value: T? = null
set(value: T?) {
blocks.forEach {
/**
* 判断一下当前owner的状态,可见状态才更新
**/
val lifecycleOwner = viewLifeCycleOwner[it]
if (null != lifecycleOwner &&
lifecycleOwner.lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)
) {
it.invoke(value!!)
}
}
field = value
}
/**
* 在同一个owner中,有可能有多个view进行了owner(Activity/fragment)的监听(block)
* 所以要对view的block的进行存储
*/
fun addListener(lifecycleOwner: LifecycleOwner, valueObserver: (T) -> Unit) {
viewLifeCycleOwner[valueObserver] = lifecycleOwner
/**
* 当owner的state为destroy时要移除
*/
lifecycleOwner.lifecycle.addObserver(ValueObserver(valueObserver))
if (!blocks.contains(valueObserver)) {
blocks.add(valueObserver)
}
}
fun removeListener(block: (T) -> Unit) {
blocks.remove(block)
}
inner class ValueObserver constructor(private val valueObserver: (T) -> Unit) : LifecycleEventObserver {
override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
if (event == Lifecycle.Event.ON_DESTROY) {
viewLifeCycleOwner.remove(valueObserver)
removeListener(valueObserver)
}
}
}
}
我们上面说到,这个数据容器内的数据是在present中修改或获取,在view层观察这个对象的状态,这样我们的view层的接口就可以移除了,我们只要在present中定义各种可被观察的数据,在UI层去根据数据的状态去修改UI就可以了,避免的接口浪费,比如我们在FlowPlayerActivity只需要观察播放状态的改变即可,关于view的接口回调直接可以移除,并且由于绑定了lifeCycleOwner,不会造成内存泄漏。
class FlowPlayerActivity : AppCompatActivity(){
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_flow_player)
title = "FlowPlayerActivity"
PlayPresent.instance.attach(null)
PlayPresent.instance.getMusic()
initListener()
}
private fun initListener() {
playOrPauseBtn.setOnClickListener {
PlayPresent.instance.doPlayOrPause()
}
PlayPresent.instance.dataListenerContainer.addListener(this){
when (it) {
PlayPresent.PlayerState.PLAYING -> {
runOnUiThread {
println("mvp FlowPlayerActivity onPlaying->>>>>>>>>>>>")
playOrPauseBtn.text = "播放状态"
}
}
PlayPresent.PlayerState.PAUSE->{
runOnUiThread {
println("mvp FlowPlayerActivity onPausePlay->>>>>>>>>>>>")
playOrPauseBtn.text = "暂停状态"
}
}
else -> {}
}
}
}
上面的架构差不多就是mvvm了,所谓的mvvm,它是基于数据驱动UI的,耦合度更低,例如这里并不需要持有FlowPlayerActivity(view层)的present,就相当于于mvvm中的viewmodel,而这里面的数据容器DataListenerContainers就相当于jetPack中的liveData,liveData是具有生命周期的可观察的数据持有类,关键点在于具有生命周期,可观察!!!我们写的DataListenerContainers是不是都具有以上特征。
总结:
本文从mvp的缺点出发,从内存泄漏和接口冗余的角度引出mvvm架构,从代码结构上来说它解耦程度更高,不过对于一些简单的页面,其实也没有必要用,毕竟杀鸡焉用牛刀。