深入浅出了解MVP
开始之前,先来个需求。打开App后,通过耗时操作获取到数据,在列表中进行展示。
class MainActivity : AppCompatActivity() {
val singerList = ArrayList<Singer>()
lateinit var handler: Handler
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
handler = @SuppressLint("HandlerLeak") object : Handler() {
override fun handleMessage(msg: Message?) {
when (msg!!.what) {
0 -> {
rv.layoutManager = GridLayoutManager([email protected], 3)
rv.adapter = SingerAdapter(singerList)
}
}
}
}
NetworkThread().start()
}
inner class NetworkThread : Thread() {
/**
*模拟一次耗时操作获取数据
*/
override fun run() {
singerList.let {
it.add(Singer(R.drawable.singer_1, "薛之谦", "超人气多面体创作才子"))
it.add(Singer(R.drawable.singer_2, "伍佰", "华语区知名的摇滚歌手"))
it.add(Singer(R.drawable.singer_3, "霍尊", "国色天香年度总冠军"))
it.add(Singer(R.drawable.singer_4, "云飞", "星光大道2012年度亚军"))
it.add(Singer(R.drawable.singer_5, "周杰伦", "华语乐坛流行天王"))
it.add(Singer(R.drawable.singer_6, "杨坤", "神秘而强大的杨32郎"))
it.add(Singer(R.drawable.singer_7, "那英", "东方的惠特妮休斯顿"))
it.add(Singer(R.drawable.singer_8, "李宗盛", "鲍家街43号乐队发起人"))
it.add(Singer(R.drawable.singer_9, "汪峰", "中国内地摇滚音乐歌手"))
it.add(Singer(R.drawable.singer_10, "胡夏", "90后内地男歌手"))
it.add(Singer(R.drawable.singer_11, "周深", "备受赞誉的天籁歌手"))
it.add(Singer(R.drawable.singer_12, "张国荣", "**的巨星永远的哥哥"))
}
val msg = Message.obtain()
msg.what = 0
handler.sendMessage(msg)
}
}
}
效果图:
OK,功能实现了,这大概就是前端的魅力吧,依着那一张张精美的图片或者一页页简陋的原型,实现出来自己总能第一眼瞧到程序跑起来的样子!随着这样的代码铺展开来,渐渐地,你会发现程序越来越卡,直至OOM了。瞧瞧下面这张图:
按Back键将程序退到后台后,再按了1次GC,Profilter的内存快照中依然存在着MainActivity的实例,这是内存无法回收的警示。在 Java 中,非静态匿名内部类会持有其外部类的隐式引用,即NetworkThread中持有MainActivity的强引用,导致这块内存不容易被回收。
MVC
MVC,即Model-View-Controller。虽是软件的一种经典设计典范,但在Android应用的开发结构中,大量的业务逻辑充斥在众多的Activity中,导致其不只充当了View角色,还充当了Controller角色。虽然Model不依赖于View,但View却依赖着Model,这就导致代码的高耦合度,也不便于项目后期的维护。有没有更好的优化方式呢,有!MVP
MVP
MVP,即Model-View-Presenter,Model提供数据,View负责显示,Presenter负责逻辑的处理。MVP与MVC有着一个重大的区别:在MVP中View并不直接使用Model,它们之间的通信是通过Presenter 来进行的,所有的交互都发生在Presenter内部,而在MVC中View会直接从Model中读取数据而不是通过 Controller。
interface IListView {
fun loading()
/**
* 显示数据
*/
fun showList(singerList: List<Singer>)
}
interface IListModel {
fun getData(getDataListener: GetDataListener)
interface GetDataListener {
fun onComplete(data: List<Singer>)
fun onFailed(error: String)
}
}
class SingerImpl : IListModel {
/**
*模拟一次耗时操作获取数据
*/
override fun getData(getDataListener: IListModel.GetDataListener) {
Thread {
// Thread.sleep(3000)
val singerList = ArrayList<Singer>()
singerList.let {
it.add(Singer(R.drawable.singer_1, "薛之谦", "超人气多面体创作才子"))
...
}
getDataListener.onComplete(singerList)
}.start()
}
}
class SingerPresenter(iListView: IListView) {
var weakReference: WeakReference<IListView>? = WeakReference(iListView)
var iListModel: IListModel = SingerImpl()
/**
* 获取数据,交给IListModel子类实例来实现
*/
fun fetch() {
if (weakReference != null) {
weakReference?.get()?.loading()
iListModel.getData(object : IListModel.GetDataListener {
override fun onComplete(data: List<Singer>) {
weakReference?.get()?.showList(data)
}
override fun onFailed(error: String) {
...
}
})
}
}
}
class MainActivity : AppCompatActivity(), IListView {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val presenter = SingerPresenter(this)
presenter.fetch()
}
override fun loading() {
...
}
...
}
简单解释一下上面的代码,首先创建一个 IListView 接口,即 View 层,有加载中以及显示这2个方法,通过让Activity来实现它以充当Presenter层的桥梁。Model 层包含了IListModel及它的子类SingerImpl,分别负责获取数据逻辑的回调以及真实场景中获取数据的实现。那么持有View及Model引用的Presenter仅需要通过fetch方法执行数据获取逻辑并让View层按需执行回调即可。
MVP的进一步封装
open class BaseListPresent<T> {
var weakReference: WeakReference<T>? = null
fun attachView(view: T) {
weakReference = WeakReference(view)
}
fun detachView() {
weakReference?.clear()
}
}
abstract class BaseActivity<V, T : BaseListPresent<V>> : AppCompatActivity() {
var baseListPresent: T? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
baseListPresent = createPresenter()
baseListPresent!!.attachView(this as V)
}
override fun onDestroy() {
super.onDestroy()
baseListPresent?.detachView()
}
abstract fun createPresenter(): T
}
class MainActivity : BaseActivity<IListView, BaseListPresent<IListView>>(), IListView {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
(baseListPresent as SingerPresenter).fetch()
}
override fun createPresenter(): SingerPresenter<IListView> = SingerPresenter()
...
}
因为在BasePresenter中无法确定V层及M层对象的类型,所以在这里的对采用了泛型。而只要定义好 View 和 Model 的类型,就能灵活引用它们的对象了,而其中detachView方法,就是用来防止内存泄漏。
MVP较MPC的优点:
1.Activity职责更加趋于明确单一;
2.代码结构更清楚,更容易维护;
3.避免内存泄漏;
4.方便单元测试;