看了下当初转载过的一篇关于mvp模式的介绍文章,后来有人反馈没有介绍Model层,且一直想整一个简单的mvp模式+databinding框架代替公司比较源生的代码写法。于是打算重新整理一下mvp模式思想的代码写法总结。
介绍下MVP模式:
M:Model层,数据服务层,负责数据的增删改查。在服务端就包括mysql数据库操作、本地cache等;在客户端就包括调用服务器API、各种形式的数据缓存等。有些朋友将Model层理解为数据模型层,数据模型归根结底是数据,设计数据模型是为了在面向对象的程序设计中更好的表达数据。所以数据模型贯穿于整个应用程序,不应该将Model层单单理解为是数据模型。但Model层往往负责将接收到的数据转化为相应的数据模型供上层使用。
V:View层,视图界面层,负责UI的渲染、子视图的组织、UI事件、用户交互等。在有些网友看来,View层是比较轻的一层,大多数时候只需要调用系统的UI控件,绑定需要的UI事件就完事了,很多平台甚至拖拖控件就行。但是实际上是因为平台为我们做了大多数的事,我们不需要去考虑怎么有效的渲染界面,也不需要去考虑怎样去实现各种各样的交互事件(触摸),只需要关注应用本身就可以了。
P:Presenter层,有得朋友将其叫作发布者,百度翻译为主持人,笔者觉得后者更贴切些。 Presenter既是中间人,在View和Model之间起到桥梁的作用,又是一个独立的大模块,封装了业务的复杂度,将UI和业务逻辑拆分开来,使UI和业务都可以独立的进行变化。从整体的数据流向上看,Presenter从Model层获取数据,并通过接口发送给View层展示;View层将用户交互传递给Presenter,由Presenter完成相应的业务逻辑,这其中可能会有Model层的参与,比如Presenter调用Model层的接口来保存数据。
实现MVP模式的最低原则是
- View和Model之间不能直接进行交互,必须通过Presenter来交流数据;
- 尽量的将业务逻辑和UI展示分开;
- 尽量使用面向接口的方式来实现MVP三层,特别是对于View与Presenter之间的交互。
参考:https://www.jianshu.com/p/31c3909ce075
MVP模式的作用
MVP的好处都有啥
1.分离了视图逻辑和业务逻辑,降低了耦合
2.Activity只处理生命周期的任务,代码变得更加简洁
3.视图逻辑和业务逻辑分别抽象到了View和Presenter的接口中去,提高代码的可阅读性
4.Presenter被抽象成接口,可以有多种具体的实现,所以方便进行单元测试
5.把业务逻辑抽到Presenter中去,避免后台线程引用着Activity导致Activity的资源无法被系统回收从而引起内存泄露和OOM
强调一下,mvp模式是一种思想,并不是一种固定的写法,网上有很多例子。没有谁对谁错,更没有谁的是唯一标准。
下面我们通过一个登录例子来简单说明下:
首先写一个模拟网络请求的类:
companion object {
val instance by lazy(LazyThreadSafetyMode.NONE) {
NetUtil()
}
}
fun loginRequest(username: String, password: String?): Int {
Log.d(TAG, "$username $password")
SystemClock.sleep(3000)
return 1
}
Model
model层主要处理数据业务逻辑,所以我们将登录的网络请求放在model层处理
class LoginModel {
lateinit var loginListener: (Int) -> Unit
fun login(username: String, password: String, e: (Int) -> Unit) {
loginListener = e
LoginAsyncTask().execute(username, password)
}
inner class LoginAsyncTask : AsyncTask<String, Void, Int>() {
override fun doInBackground(vararg params: String?): Int {
return NetUtil.instance.loginRequest(params[0]!!, params[1]!!)
}
override fun onPostExecute(result: Int?) {
super.onPostExecute(result)
loginListener?.let {
loginListener(result!!)
}
}
}
}
写了一个简单的异步任务,将结果通过loginListener 暴露出去。这里,我们需要将数据反馈给Presenter层。
紧接着我们编写Presenter层,作为view层和model层的桥梁
Presenter
class LoginPresenter(iLoginView: ILoginView) {
private var weakReference: WeakReference<ILoginView>
private val loginModel: LoginModel?
init {
weakReference = WeakReference(iLoginView)
loginModel = LoginModel()
}
fun getLoginRespond(userName: String, paw: String) {
loginModel?.let {
weakReference.get()?.apply {
it.login(userName, paw) { label ->
weakReference.get()!!.onRespond(label)
}
}
}
}
}
View层
view层作为处理得到数据后ui的一些渲染更新等等,比较轻量级。用接口表示:
interface ILoginView {
fun onRespond(response: Int)
}
Activity的使用
class LoginActivity : AppCompatActivity(), ILoginView {
override fun onRespond(response: Int) {
tv_login_result.text = if (response == 1) "登录成功" else "登录失败"
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
var username = et_username.text.toString()
var password = et_paw.text.toString()
tv_login.setOnClickListener {
LoginPresenter(this@LoginActivity)
.getLoginRespond(username, password)
}
}
}
以上就是简单的一个mvp代码的框架,再次强调一遍,mvp模式只是一种思想,并没有固定的写法,所以网上很多例子没有对错,只有适不适合和你对他们是否认可。