Android中的设计模式很多,常用的有 MVC、MVP和MVVM,MVP是目前比较流行的一种设计模式,全称为Model-View-Presenter。从上图可以看到,MVP 设计模式解除了Model和View的耦合。这种设计的优点是有效地降低View的复杂性,避免业务逻辑被塞入View中,使得View变得更为简单专一。不仅如此,MVP 还带来了良好的可拓展性、可测试性,保证系统整洁性、灵活性。对于一个复杂的应用来说,MVP模式是一种良好的架构模式,它可以非常好地组织应用结构,使得应用变得灵活,拥抱变化。
MVP模式下,项目中会多出很多类和接口,这是为了实现Model和View的解耦,View和数据的交互通过接口来实现。虽然多出了这些类和接口,但是这样便于我们维护,使用MVP模式来写项目,可以说是面向接口编程,调用的方法基本都是抽象的,直接使用接口来调用,提高可维护性。在MVP模式中,一般我们都会将要调用的方法抽象成接口,Model、View、Presenter接口,在各实现类中,直接对接口进行操作。
MVP设计模式的好处有以下几点
- Activity只处理生命周期的任务,代码变得更加简洁
- 视图逻辑和业务逻辑分别抽象到了View和Presenter的接口中去,提高代码的可阅读性
- Presenter 可以复用,一个 Presenter 可以用于多个 View,不用去改 Presenter
- 利于单元测试。模块分明,那么我们编写单元测试就变得很方便了,而不用特别是特别搭平台,人工模拟用户操作等等耗时耗力的事情。
总之,使用了 MVP 设计模式能让代码逻辑更清晰,改动代码的时候更具目的性,而不是改动一片代码
MVP基本框架搭建
使用方法上,View 是 Activity 和 Fragment,同时 View 持有 Presenter 实例,可以调用 Presenter 实现业务逻辑。而 Presenter 中持有 Model 和 View 实例,业务逻辑就在 Presenter 中实现。Model 中做数据处理,例如操作数据库或者从网络获取数据,一般采用异步实现。
定义 Presenter 基类,这里有个问题要注意,因为 Presenter中持有 View 实例,也就是 Presenter 持有 Activity 实例,当我们在 Presenter 中调用 Model 的数据请求时,某些情况下,在我们的数据请求还没有完成时,我们就退出了这个Activitiy,但是 Model 还在进行着数据请求,这是因为 Presenter 对 View 是强引用,所以,当 Activity 结束的时候,该Activity 实例还被 Presenter 持有,此刻将出现内存泄漏问题。为此,我们要在 View 的生命周期结束时及时释放这个引用。
首先,我们来编写 Presenter 基类,在这里面要编写解除对 View 引用的方法
1 2 3 4 5 6 7 8 9 10 11
| open class BasePresenter<T> { protected var mView: T? = null
fun attachView(view: T) { mView = view }
fun detachView() { mView = null } }
|
编写 View 基类,在 View 的生命周期结束的时候,要解除 Presenter 对 View 的引用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| abstract class BaseMvpActivity<V, T : BasePresenter<V>> : AppCompatActivity() { protected lateinit var mPresenter: T
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(getContentId()) processLogic() } open fun initWidget() {}
open fun processLogic() { mPresenter = createPresenter() mPresenter.attachView(this as V) }
override fun onDestroy() { super.onDestroy() mPresenter.detachView() }
protected abstract fun getContentId(): Int protected abstract fun createPresenter(): T }
|
Model 基类与 Presenter 的交互通过回调实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| interface IModel { interface OnCompletedListener {
fun onSuccess()
fun onFail(msg: String) } }
|
MVP 简单例子
这是一个简单的例子,仅仅是作为如何使用 MVP ,实际项目中的使用应该根据需求做定制或者使用成熟的开发框架。
实现效果:
通过点击注册按钮,可以向数据库中插入用户注册信息,点击查询按钮,可以显示用户数据。
准备工作:
Application name |
MVPDemo |
Company domain |
Use your website name |
Kotlin support |
Yes |
Form factor |
Phone and Tablet only |
Minimum SDK |
API 24 Nougat |
Type of activity |
Empty |
Activity name |
MainActivity |
Layout name |
activity_main |
Backward compatibility |
Yes.AppCompat |
目录结构:
1、编写契约类UserContract,规定 View 和 Presenter 接口
1 2 3 4 5 6 7 8 9 10
| interface UserContract { interface View { fun showDialog(msg: String) }
interface Presenter { fun load() fun register(username: String, password: String) } }
|
2、Model层实现,为了简单,使用 List 存储数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| class UserModel : IModel {
fun load(listener: IModel.OnCompletedListener<List<User>>) { try { listener.onSuccess(UserDao.getAllUser()) } catch (e: Exception) { listener.onFail("${e.stackTrace}") } }
fun register(username: String, password: String, listener: IModel.OnCompletedListener<*>) { try { UserDao.saveUser(User(username, password)) listener.onSuccess() } catch (e: Exception) { listener.onFail("${e.stackTrace}") }
} }
|
3、View层实现,实现类是 MainActivity,初始化点击事件还有实现契约类中的 View 接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| class MainActivity : BaseMvpActivity<UserContract.View, UserPresenter>(), UserContract.View { private var mUsername: EditText? = null private var mPassword: EditText? = null
private var mRegisterBtn: Button? = null private var mLoadBtn: Button? = null
override fun getContentId(): Int { return R.layout.activity_main }
override fun createPresenter(): UserPresenter { return UserPresenter() }
override fun initWidget() { super.initWidget() mUsername = username_et mPassword = password_et
mRegisterBtn = bn_register mLoadBtn = bn_query }
override fun processLogic() { super.processLogic() mRegisterBtn?.setOnClickListener { mPresenter.register(mUsername?.text.toString(), mPassword?.text.toString())
}
mLoadBtn?.setOnClickListener { mPresenter.load() } }
override fun showDialog(msg: String) { AlertDialog.Builder(this) .setTitle("Result") .setMessage(msg) .setPositiveButton("确定", null) .setNegativeButton("取消", null) .create() .show() } }
|
4、Presenter层实现,实现契约类中的 Presenter 接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| class UserPresenter : BasePresenter<UserContract.View>(), UserContract.Presenter { private val model = UserModel()
override fun load() { model.load(object : IModel.OnCompletedListener<List<User>> { override fun onSuccess(data: List<User>) { var result = "" data.forEach { result += "${it.username} : ${it.password}\n" } mView?.showDialog(result) }
override fun onSuccess() {}
override fun onFail(msg: String) { mView?.showDialog("获取数据失败") } }) }
override fun register(username: String, password: String) {
model.register(username, password, object : IModel.OnCompletedListener<Any> { override fun onSuccess(data: Any) { }
override fun onSuccess() { mView?.showDialog("Save Data Success") }
override fun onFail(msg: String) { mView?.showDialog("Save Data Failed") } }) }
}
|
最后
大概讲讲这个实现逻辑,添加新用户信息时,在MainActivity中调用 UserPresenter 的 register 接口,然后实现过程就在 UserPresenter 中的 register 方法里(通过传入的用户名和密码构建一个 User 对象,然后调用 UserModel 中的 register 方法完成数据的存储,最后通过回调 OnCompletedListener 告诉 UserPresenter 存储是否成功)来实现添加用户。 显示用户数据的过程也是类似的。
这里用一个极简单的例子展示了MVP设计模式的使用,通过它,我们可以简单地理解MVP设计模式的使用。实际项目中还是要灵活运用,具体情况具体分析,例如可以结合 RxKolin 使我们的回调更加优雅。