[译] Android 中的 MVP:如何使 Presenter 层系统化?
在任何情况下,在你从未使用 MVP 的架构中去使用它总是件好事。
为什么要使用 MVP?
在 Android 开发中,我们遇到一个严峻的问题:Activity 高度耦合了用户界面和数据存取机制。我们可以找到像 CursorAdapter 这样的极端例子,它将作为视图层一部分的 Adapter 和 属于数据访问层级的 Cursor 混合到了一起。
为了能够轻松地扩展和维护一个应用,我们需要使用可以相互分离的体系架构。如果我们不再从数据库获取数据,而是从 web 服务器获取,那么我接下来该怎么办呢?我们可能就要重新编写整个视图层了。
MVP 使视图独立于我们的数据源而存在。我们需要将应用程序划分为至少三个不同的层次,以便我们可以独立地测试它们。通过 MVP,我们可以将大部分有关业务逻辑的处理从 Activity 中移除,以便我们可以在不使用 Instrumentation Test 的情况下对其进行测试。
如何实现 Android 当中的 MVP?
--
好吧,这就是它开始产生分歧的地方。MVP 有很多变种,每个人都可以根据自己的需求和自己感觉更加舒适的方式来调整模式。这主要取决于我们委托给 Presenter 的任务数量。
到底是该由 View 层来负责启用或禁用一个进度条,还是该由 Presenter 来负责呢?又该由谁来决定 Action Bar 应该做出什么行为呢?这就是艰难决定的开始。我将展示我通常情况下是如何处理这种情况的,但我希望这篇文章更是一个适合讨论的地方,而不是严格的约束 MVP 该如何应用,因为根本没有“标准”的方式来实现它。
对于本文,我已经实现了一个非常简单的示例,你可以在我的 Github 找到 一个登录页面和主页面。为了简单起见,本文中的代码是使用 Kotlin 实现的,但你也可以在仓库中查看使用 Java 8 编写的代码。
Model 层
在具有完整分层体系结构的应用程序中,这里的 Model 仅仅是通往领域层或业务逻辑层的大门。如果我们使用 鲍勃大叔的 clean architecture 架构,这里的 Model 可能是一个实现了一个用例的 Interactor(交互器)。但就本文而言,将 Model 看做是一个给 View 层显示数据的提供者就足够了。
如果你检查代码,你将看到我创建了两个带有人为延迟操作的 Interactor 来模拟对服务器的请求情况。其中一个 Interactor 的结构:
class LoginInteractor {
...
fun login(username: String, password: String, listener: OnLoginFinishedListener) {// Mock login. I'm creating a handler to delay the answer a couple of secondspostDelayed(2000) {when {username.isEmpty() -> listener.onUsernameError()password.isEmpty() -> listener.onPasswordError()else -> listener.onSuccess()}}}}
这是一个简单的方法,它接收用户名和密码,并进行一些验证操作。
View 层
View 层通常是由一个 Activity(也可以是一个 Fragment,一个 View,这取决于 App 的结构),它包含了一个对 Presenter 的引用。理想情况下,Presenter 是通过依赖注入的方式提供的(比如 Dagger),但如果你没有使用这类工具,也可以直接创建一个 Presenter 对象。View 需要做的唯一一件事就是:当有用户操作发生时(比如一个按钮被点击了),就调用 Presenter 中的相应方法。
由于 View 必须与 Presenter 层无关,因此它就需要实现一个接口。下面是示例中使用到的接口:
interface LoginView {fun showProgress()fun hideProgress()fun setUsernameError()fun setPasswordError()fun navigateToHome()}
接口中有一些有效的方法来显示或隐藏进度条,显示错误信息,跳转到下一个页面等等。正如上面所提到的,有很多方式去实现这些功能,但我更喜欢罗列出最简单直观的方法。
然后,Activity 可以实现这些方法。这里我向你展示了一些用法,以便你对其用法有所了解:
class LoginActivity : AppCompatActivity(), LoginView {...
override fun showProgress() {progress.visibility = View.VISIBLE}
override fun hideProgress() {progress.visibility = View.GONE}
override fun setUsernameError() {username.error = getString(R.string.username_error)}}
但是如果你还记得,我还告诉过你,View 层使用 Presenter 来通知用户交互操作。下面就是它的用法:
class LoginActivity : AppCompatActivity(), LoginView {
private val presenter = LoginPresenter(this, LoginInteractor())
override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_login)
button.setOnClickListener { validateCredentials() }}
private fun validateCredentials() {presenter.validateCredentials(username.text.toString(), password.text.toString())}
override fun onDestroy() {presenter.onDestroy()super.onDestroy()}...}
Presenter 被定义为 Activity 的属性,当点击按钮时,它会调用 validateCredentials()
方法,该方法将会通知 Presenter。
onDestroy()
方法亦是如此。我们稍后将会看到为什么在这种情况下需要通知 Presenter。
Presenter 层
Presenter 充当着 View 层和 Model 层的中间人。它从 Model 层获取收据并将格式化后数据返回给 View 层。
此外,与典型的 MVC 模式不同的是,Presenter 决定了当你在与 View 层交互时会做何响应。因此,它将为用户每个可执行的操作提供一种方法。我们在 View 层中看到了它,这里是代码实现:
class LoginPresenter(var loginView: LoginView?, val loginInteractor: LoginInteractor) :LoginInteractor.OnLoginFinishedListener {
fun validateCredentials(username: String, password: String) {loginView?.showProgress()loginInteractor.login(username, password, this)}...
评论