android 适合mvp模式,Android中的MVP:如何使Presenter层系统化?
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 找到 一個登錄頁面和主頁面。為了簡單起見,本文中的代碼是使用 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?todelay?the?answer?a?coupleofseconds
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 了。
總結
在 Android 中將用戶界面層與邏輯層分離并不簡單,但 MVP 模式可以更加輕易地防止我們的 Activity 最終淪為高度耦合的、包含了成百上千行代碼的類。在大型應用開發過程中,將代碼管理好是很有必要的。否則,對代碼的維護和擴展都會變得很困難。
如今,還有其他的代替方案比如 MVVM,我將會創作新的文章來對 MVVM 和 MVP 做比較,并幫助開發者遷移。所以請繼續關注我的博客!
【編輯推薦】
【責任編輯:未麗燕 TEL:(010)68476606】
點贊 0
總結
以上是生活随笔為你收集整理的android 适合mvp模式,Android中的MVP:如何使Presenter层系统化?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: com.android.dazhihui
- 下一篇: 鸿蒙系统替代安卓,华为鸿蒙2.0可以替代