Kotlin 语言形式实现 Android 当中的 MVP

简介: MVP(Model View Presenter)模式是著名的 MVC(Model View Controller)的衍生物,并且是 Android 应用程序中管理表示层的最流行的模式之一。

MVP(Model View Presenter)模式是著名的 MVC(Model View Controller)的衍生物,并且是 Android 应用程序中管理表示层的最流行的模式之一。

这篇文章首次发表于 2014 年 4 月,从那以后就一直备受欢迎。所以我决定更新它来解决人们心中的大部分疑虑,并将代码转换为 Kotlin 语言形式。

自那时起,架构模式发生了重大变化,例如带有架构组件的 MVVM,但 MVP 仍然有效并且是一个值得考虑的选择。

什么是 MVP 模式?

MVP 模式将 Presenter 层从逻辑中分离出来,这样一来,就把所有关于 UI 如何工作与我们在屏幕上如何表示它分离了开来。理想情况下,MVP 模式将实现相同的逻辑可能具有完全不同且可交替的界面。

要明确的第一件事是 MVP 本身不是一个架构,它只负责表示层。这是一个有争议的说法,所以我想更深入地解释一下。

你可能会发现 MVP 被定义为架构模式,因为它可以成为你的应用程序架构的一部分。但你不应当这样认为,因为去掉 MVP 之后,你的架构依旧是完整的。MVP 仅仅塑造表示层,但如果你需要灵活且可扩展的应用程序,那么其余层仍需要良好的体系架构。

完整架构体系的一个示例可以是 Clean Architecture,但还有许多其他选择。

在任何情况下,在你从未使用 MVP 的架构中去使用它总是件好事。

为什么要使用 MVP?

在 Android 开发中,我们遇到一个严峻的问题:Activity 高度耦合了用户界面和数据存取机制。我们可以找到像 CursorAdapter 这样的极端例子,它将作为视图层一部分的 Adapter 和 属于数据访问层级的 Cursor 混合到了一起。

为了能够轻松地扩展和维护一个应用,我们需要使用可以相互分离的体系架构。如果我们不再从数据库获取数据,而是从 web 服务器获取,那么我接下来该怎么办呢?我们可能就要重新编写整个视图层了。

MVP 使视图独立于我们的数据源而存在。我们需要将应用程序划分为至少三个不同的层次,以便我们可以独立地测试它们。通过 MVP,我们可以将大部分有关业务逻辑的处理从 Activity 中移除,以便我们可以在不使用 Instrumentation Test 的情况下对其进行测试。

如何实现 Android 当中的 MVP?

好吧,这就是它开始产生分歧的地方。MVP 有很多变种,每个人都可以根据自己的需求和自己感觉更加舒适的方式来调整模式。这主要取决于我们委托给 Presenter 的任务数量。

到底是该由 View 层来负责启用或禁用一个进度条,还是该由 Presenter 来负责呢?又该由谁来决定 Action Bar 应该做出什么行为呢?这就是艰难决定的开始。我将展示我通常情况下是如何处理这种情况的,但我希望这篇文章更是一个适合讨论的地方,而不是严格的约束 MVP 该如何应用,因为根本没有“标准”的方式来实现它。

对于本文,我已经实现了一个非常简单的示例,你可以在我的 Github https://github.com/antoniolg/androidmvp找到 一个登录页面和主页面。为了简单起见,本文中的代码是使用 Kotlin 实现的,但你也可以在仓库中查看使用 Java 8 编写的代码。

Model 层

在具有完整分层体系结构的应用程序中,这里的 Model 仅仅是通往领域层或业务逻辑层的大门。如果我们使用 鲍勃大叔的 clean architecture 架构https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html,这里的 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 seconds
 postDelayed(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)
 }
 ...
}

MVP 模式存在一些风险,常常被我们忽略的最重要的问题是 Presenter 永远依附在 View 上面。并且 View 层一般为 Activity,这就意味着:

  • 我们可能会由于长时间的运行的任务而导致 Activity 的泄漏
  • 我们可能会在 Activity 已经被销毁的情况下去更新视图

首先,倘若你能够保证能够在合理的时间内完成你的后台任务,我将不会过于担心。将你的 Activity 泄漏 5-10 秒会让你的 App 变得很糟糕,并且解决方案通常很复杂。

第二点反而更让人担心。想象一下,你花费 10 秒钟时间向服务器发送一个请求,但用户却在 5 秒钟后关闭了 Activity。当回调方法正在被调用并且 UI 被更新时,App 将会崩溃,因为 Activity 正在销毁中

为了解决这个问题,我们可以在 Activity 中调用 onDestroy() 方法并清除 View:

fun onDestroy() {
 loginView = null
}

这样我们就可以避免在任务结束时间与活动销毁时间不一致的情况下调用 Activity 了。

总结

欢迎移动开发最新技术交流学习群;964557053。加群请备注csdn

在 Android 中将用户界面层与逻辑层分离并不简单,但 MVP 模式可以更加轻易地防止我们的 Activity 最终沦为高度耦合的、包含了成百上千行代码的类。在大型应用开发过程中,将代码管理好是很有必要的。否则,对代码的维护和扩展都会变得很困难。

相关文章
|
1天前
|
安全 Java Android开发
构建高效Android应用:采用Kotlin进行内存优化的策略
【5月更文挑战第8天】 在移动开发领域,性能优化一直是开发者关注的焦点。特别是对于Android应用而言,合理管理内存资源是确保应用流畅运行的关键因素之一。近年来,Kotlin作为官方推荐的开发语言,以其简洁、安全和互操作性的特点受到开发者青睐。本文将深入探讨利用Kotlin语言特性,通过具体策略对Android应用的内存使用进行优化,旨在帮助开发者提高应用性能,减少内存消耗,避免常见的内存泄漏问题。
4 0
|
2天前
|
Android开发 Kotlin
Kotlin开发Android之基础问题记录
Kotlin开发Android之基础问题记录
13 1
|
2天前
|
移动开发 数据库 Android开发
构建高效Android应用:Kotlin协程的全面应用
【5月更文挑战第7天】 在移动开发领域,性能优化与流畅的用户体验是至关重要的。随着Kotlin语言的流行,其并发神器——协程,已成为提升Android应用性能的重要工具。本文将深入探讨如何在Android项目中利用Kotlin协程进行异步编程、网络请求和数据库操作,以及如何通过协程简化代码结构,增强应用的响应性和稳定性。我们的目标是为开发者提供一套实用的协程使用模式和最佳实践,以便构建更加高效的Android应用。
16 3
|
2天前
|
移动开发 Java Android开发
Android应用开发:Kotlin语言的优势与实践
【5月更文挑战第7天】 在移动开发的世界中,Android平台的Kotlin语言以其高效、简洁的语法和强大的功能吸引了众多开发者。本文将深入探讨Kotlin语言的核心优势,并通过实际案例展示如何在Android应用开发中有效地运用这一现代编程语言。我们将从语言特性出发,逐步分析其在提升开发效率、改善代码质量以及增强应用性能方面的具体表现,为读者提供一个全面而细致的Kotlin应用开发指南。
|
2天前
|
移动开发 数据库 Android开发
构建高效Android应用:Kotlin与协程的完美结合
【5月更文挑战第7天】 在移动开发领域,性能优化和资源管理始终是核心议题。随着Kotlin语言的普及,其提供的协程特性为Android开发者带来了异步编程的新范式。本文将深入探讨如何通过Kotlin协程来优化Android应用的性能,实现流畅的用户体验,并减少资源消耗。我们将分析协程的核心概念,并通过实际案例演示其在Android开发中的应用场景和优势。
|
5天前
|
移动开发 前端开发 Android开发
构建高效Android应用:探究Kotlin协程的优势
【5月更文挑战第4天】 在移动开发领域,尤其是对于Android开发者而言,编写响应迅速且高效的应用程序至关重要。Kotlin作为一种现代的编程语言,其提供的协程特性为异步编程带来了革命性的改变。本文将深入探讨Kotlin协程在Android开发中的应用优势,并通过实例代码展示如何利用协程简化异步任务处理,提高应用性能和用户体验。
|
5天前
|
移动开发 Java Android开发
构建高效Android应用:探究Kotlin与Java的性能对比
【5月更文挑战第4天】在移动开发的世界中,性能一直是衡量应用质量的重要指标。随着Kotlin的兴起,许多Android开发者开始考虑是否应该从传统的Java迁移到Kotlin。本文通过深入分析两者在Android平台上的性能差异,帮助开发者理解Kotlin在实际项目中的表现,并提供选择编程语言时的参考依据。
20 5
|
8天前
|
Java 编译器 Android开发
构建高效Android应用:探究Kotlin与Java的性能差异
【5月更文挑战第1天】 在移动开发的世界中,性能优化始终是开发者关注的焦点。随着Kotlin的兴起,许多团队和开发者面临着一个选择:是坚持传统的Java语言,还是转向现代化、更加简洁的Kotlin?本文通过深入分析和对比Kotlin与Java在Android应用开发中的性能表现,揭示两者在编译效率、运行速度和内存消耗等方面的差异。我们将探讨如何根据项目需求和团队熟悉度,选择最适合的语言,以确保应用的高性能和流畅体验。
|
8天前
|
缓存 安全 Android开发
构建高效Android应用:采用Kotlin进行内存优化
【5月更文挑战第1天】随着移动设备的普及,用户对应用程序的性能要求越来越高。特别是对于Android开发者来说,理解并优化应用的内存使用是提升性能的关键。本文将探讨使用Kotlin语言在Android开发中实现内存优化的策略和技术。我们将深入分析Kotlin特有的语言特性和工具,以及它们如何帮助开发者减少内存消耗,避免常见的内存泄漏问题,并提高整体应用性能。
|
6天前
|
存储 安全 Android开发
安卓应用开发:构建一个高效的用户登录系统
【5月更文挑战第3天】在移动应用开发中,用户登录系统的设计与实现是至关重要的一环。对于安卓平台而言,一个高效、安全且用户体验友好的登录系统能够显著提升应用的用户留存率和市场竞争力。本文将探讨在安卓平台上实现用户登录系统的最佳实践,包括对最新身份验证技术的应用、安全性考量以及性能优化策略。