RxJava2+Retrofit2+RxLifecycle2使用MVP模式構建項目

前言

眼下Retrofit+RxJava搭配的網絡請求框架非常流行,本着學習的態度,寫了一個相關的demo。寫着寫着就想朝着搭建一個項目框架的方向走。因而使用了一下MVP模式。git

RxJava 確實挺好用,我的特別喜歡這種「流式」的代碼風格,邏輯很清晰,起碼提供了一種相對的規範,開發者按照對應的流程寫代碼,後期的維護和拓展會簡單不少。github

MVP模式簡單說就是爲了解耦,各行各職,閱讀代碼,拓展功能代價不會那麼大(或許有些人認爲不必用MVP,直接在activity/fragment中寫代碼就行了,那隻能說你沒遇到到過相對大一點的項目,或者沒遇到「實習生」寫代碼,那酸爽,看代碼會看得你懷疑人生)服務器

MVP

在使用MVC開發Android應用的時候,原理上網絡

  • View:對應於佈局文件xml
  • Model:業務邏輯和實體模型
  • Controllor:對應於Activity

可是寫代碼的時候,你就會發現,好多跟view相關的操做都在activity中實現完成了,致使activity既是Controllor又是View,網上有人稱爲是MV模式,也所以致使activity的代碼量特別大,1000+的代碼很常見 
後來,Presenter的出現,將Actvity,xml 視爲View層,Model不變,Presenter負責完成View層與Model層的交互。因而MVP是這樣的:架構

  • View 對應於Activity,xml 負責View的繪製以及與用戶交互
  • Model 依然是業務邏輯和實體模型
  • Presenter 負責完成View於Model間的交互

在網上看了一下MVP的使用demo,挺多人將一個頁面須要完成的操做,以及須要用到的控件所有定義到相關的View接口中,示例:框架

 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

我的以爲,這個有點蛋疼 
1.這樣view接口須要的方法太多了,有些實現(clearUserName())能夠放在activity中操做,第一不需將控件經過接口傳到Presenter中,第二我認爲這種算是對View的操做,仍是能夠看做View相關的。 
2.咱們很難知道一個界面都要實現些什麼方法(若是包括對某個控件內容清空等),可是咱們不難知道一個activity須要實現哪些主要的功能,好比登陸頁面就一個登陸功能,或者再加多一個第三方登陸咯。函數

因此我以爲View接口中定義一些經常使用的方法,以及一些須要實現的方法就能夠了,經過回調內容,把控件賦值,數據展現等仍是放回在activity中操做,presenter只須要將對應的實體或者數據給activity就行了,activity怎麼展現,不用管,不關個人事情,作到各行各職。工具

或許有人會說,咦~你這都不是MVP,網上的MVP這些操做要放在presenter中的,這時我上去就給你一巴掌,咱們使用優秀的框架/架構是爲了學習它優秀的模式或者編碼風格,若是一味的循序漸進照着用,那將毫無心義!同時,咱們只有不斷改進,不斷推成出新才能使得技術不斷進步,若是前人不對MCV提出質疑,就不會有今天的MVP。佈局

因此我以爲View的接口應該這樣 
一個BaseView,定義經常使用的方法,其餘頁面View接口繼承基類單元測試

 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

這個基類怎麼寫看項目須要,按照開發者各自需求編寫。

如今寫一個登陸的LoginView,繼承BaseView同時添加特定的接口

 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

這裏定義一個showResult(UserBean bean) 將User實體類傳給activity,用於展現用戶信息。

寫到這裏的時候的我突然更加堅決我所理解的MVP是對的,解耦嘛 
Presenter:負責獲取或者構建UserBean 
Activity:負責展現Presenter給過來的數據

以前看到過有人經過View接口將activity的控件幾乎「拷貝」到了presenter中,雖然實現了邏輯處理在Presenter,可是若是Presenter邏輯改動仍是會牽一髮動全身,要改動不少 
如今這種方式挺好的,負責構建數據,不參與展現,也方便單元測試。對,就是這樣的。

Retrofit2+RxJava2+RxLifecycle2

Retrofit+RxJava確實是一種很不錯的搭配,RxJava能夠指定運行的線程,在網絡請求時,開啓線程耗時操做,響應結果時切換爲主線程操做UI。很是漂亮,代碼風格也贊,我我的稱爲流式操做,從上到下一步步表明操做的主要邏輯,比起傳統的迷之嵌套,迷之縮進好多了。

咱們知道RxJava使用訂閱模式,若是沒有及時取消訂閱,會致使內存泄漏,這個是很是糟糕的行爲,固然解決方式也很簡單,在對應的生命週期取消訂閱就好,不過我仍是懷着好奇之心Github一下,果真已經有人對此做出了貢獻RxLifecycle 經過綁定生命週期能夠很方便的管理訂閱與取消訂閱。

Github: https://github.com/trello/RxLifecycle

Retrofit+RxJava的使用仍是挺簡單的,不過相對於你們已經用在項目的網絡請求框架,它仍是須要進行加工的。好比說錯誤處理,配合RxLifecycle使用,以及不少人會問的,在MVP中使用的時候怎麼取消訂閱。

先看下最簡單的使用 
接口代碼

 
  • 1
  • 2
  • 3
  • 4

使用代碼

 
  • 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

代碼簡介,清晰,構建請求參數,構建被觀察者對象,以及傳入一個觀察者對象實現 
訂閱回調onSubscribe 也能夠是開始的操做 
成功回調onNext 
失敗回調onError 
監聽完成onComplete

或許開發者一眼就看出了onError 回調的對象是Throwable 這個不能忍啊,投入使用的框架確定得封裝,那就從這裏開始

錯誤處理

在常見的網絡請求框架中通常會有兩個回調函數

 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

定義onError回調函數觸發的場景是:1.異常2.錯誤 
1.異常:請求異常,解析數據出錯,網絡異常等等 
2.錯誤:某一次請求邏輯錯誤,(例如:登陸錯誤) 
將上述兩種狀況交給onError回調函數處理 
在請求邏輯成功的時候觸發一個onSuccess函數。這樣監聽者就只須要兩個函數,一個失敗,一個成功,失敗提示給用戶,成功負責展現數據,跳轉頁面等

定義一個異常處理類ExceptionEngine

 
  • 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

異常處理類中,都是常見的錯誤類型,咱們經過解析Throwable轉換成統一的錯誤類ApiException

 
  • 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

這個類很是簡單,一個狀態碼,一個錯誤信息,方便咱們開發調試。

不過仔細看代碼的同窗會發現,ServerException這個是什麼鬼?「服務器返回的錯誤」?Throwable怎麼知道這個錯誤類型是ServerException

其實這個ServerException是咱們自定義的錯誤類型,通常咱們開發中都會跟服務器約定一種接口請求返回的數據。好比:

  • int code:表示接口請求狀態,0表示成功,-101表示密碼錯誤等等
  • String msg:表示接口請求返回的描述。success,」token過時」等等
  • Object result:成功是返回的數據

那麼咱們就能夠在解析服務端返回數據的時候,當code!=0,就拋出ServerException

 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

OK,到這裏咱們常見的錯誤類型,異常等都處理完了,經過ExceptionEngine轉化爲統一的錯誤類型ApiException,在訂閱者回調onError(ApiException e)就能夠很方便知道錯誤的狀態碼以及對應的描述信息。

 
  • 1
  • 2
  • 3
  • 4
  • 5

咱們使用onErrorResumeNext(new HttpResultFunction<>())操做符對Retrofit網絡請求拋出的Exception進行處理,咱們定義HttpResultFunction處理Retrofit拋出的Exception,經過ExceptionEngine轉化爲統一的錯誤類型ApiException

 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

這一步是對錯誤,異常等的處理,若是某一個http請求沒有發生異常,或者網絡錯誤,就會走onNext回調。

前面咱們約定,將服務器返回的邏輯錯誤也歸類到onError。因此咱們在.map(new ServerResultFunction())操做符中處理服務器返回的結果是否正確(這裏指邏輯正確,即code==0),若是code!=0,就拋出ServerException

 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

解析服務器返回結果 HttpResponse,這個類由開發者自行設置,根據實際狀況來定義字段,這裏我假設後臺返回的字段有

String msg; int retCode; Object result;

 
  • 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
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66

這裏Result咱們使用Object由於接口時通用的,服務端返回的接口類型也是多樣的,多是列表,也多是JSON對象,或者String字符串,因此這裏咱們使用Object,在數據解析的時候在轉化成爲具體的類型

嗯,錯誤處理搞定了,那就是簡單的封裝一下Retrofit 和RxJava以及使用RxLifecycle 
RxJava使用訂閱模式,那咱們就須要封裝一個被訂閱者,一個訂閱者,以及使用RxLifecycle自動管理訂閱的生命週期

構建Api接口類

 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

構建Retrofit工具類獲取retrofit實例

 
  • 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
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63

構建網絡請求(被訂閱對象)

 
  • 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

HttpRxObservable咱們構建一個被訂閱者Observable而且設置了錯誤處理,同時添加了生命週期管理。

處理訂閱者Observer

 
  • 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
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87

使用網絡請求的過程當中咱們確定會遇到取消請求的場景,這裏咱們實現一個HttpRequestListener,爲每個請求添加惟一的TAG用來標識具體的每個請求,開始請求時保存TAG,請求成功/失敗移除標誌,同時TAG也用作取消請求的標誌。

編寫一個測試類,示例如何使用Retrofit

 
  • 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
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67

使用RxLifecycle 管理訂閱生命週期activity須要繼承RxActivity。開篇也提到MVP的時候,接下來就說說在MVP中如何使用。 
定義一個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
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147

JAVA弱引用,管理View的引用,以及activity的引用,避免強引用致使資源沒法釋放而形成的內存溢出, 
寫代碼的時候想到了一個很巧妙的方式:Presenter中傳如一個activity,同時實現Activity生命週期監聽,在onDestroy中移除View和Activity的引用,我的以爲這個很是不錯。看官能夠客觀評價一下優劣。

登陸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
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73

LoginActivity

 
  • 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
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66

以上是我對MVP的一點理解,以及對RxJava2+Retrofit2+RxLifecycle2進行的封裝,因爲編寫demo的時候是朝着構建一個項目框架走的,因此沒有貼完的代碼就請移步到個人Github克隆完整版本,盡情的擼吧。 
我的認爲這個搭配仍是可行的,而且已經投入都項目中。好產品必定要本身先使用,承認。哈哈哈

Github地址

相關文章
相關標籤/搜索