李 想 ,特日根 ,3
(1.長光衛(wèi)星技術(shù)有限公司,吉林 長春130000;2.吉林省衛(wèi)星遙感應(yīng)用技術(shù)重點(diǎn)實(shí)驗(yàn)室,吉林 長春130000;3.中國科學(xué)院長春光學(xué)精密機(jī)械與物理研究所,吉林 長春130000)
在當(dāng)今社會(huì),移動(dòng)端因其便攜性、低功耗以及無線網(wǎng)的快速接入等優(yōu)勢(shì),使得人們與外部世界進(jìn)行網(wǎng)絡(luò)連接更加方便而舒適。 正因如此,移動(dòng)端編程成為了當(dāng)下最熱門的計(jì)算機(jī)編程領(lǐng)域之一。 2019 年第二季度移動(dòng)端操作系統(tǒng)市場(chǎng)份額表明,Android 系統(tǒng)占比77.14%,iOS系統(tǒng)占比22.83%,其余系統(tǒng)不及1%,由此可知Android在當(dāng)今手機(jī)行業(yè)起著舉足輕重的作用。隨著每一款應(yīng)用承載的功能不斷增多,其代碼管理也變得更為復(fù)雜。 對(duì)于Android 應(yīng)用開發(fā)來說,用Android Studio 編譯器生成Android 項(xiàng)目時(shí),其生成的XML 文件和Activity 文件已經(jīng)對(duì)應(yīng)傳統(tǒng)MVC(Model-View-Controller)架構(gòu)模式的View層和Controller 層, 同時(shí)XML 文件不能實(shí)現(xiàn)全部布局功能,因此部分View 層內(nèi)容需交付給Activity 文件完成。Activity 文件隨著頁面和業(yè)務(wù)邏輯的不斷增加也會(huì)不斷增大,代碼間耦合度明顯提高[1-3],將對(duì)項(xiàng)目的升級(jí)和維護(hù)帶來障礙。 因此,對(duì)于大中型項(xiàng)目來說,MVC 架構(gòu)并不可取。
對(duì)于一個(gè)常規(guī)項(xiàng)目,其網(wǎng)絡(luò)請(qǐng)求必不可少,雖然官方提供了諸如HttpURLConnection 類等HTTP 請(qǐng)求方式,但該類在大量網(wǎng)絡(luò)請(qǐng)求時(shí),其性能較差。
針對(duì)此問題,MVP(Model-View-Presenter)+Retrofit+OkHttp+RxJava 的架構(gòu)應(yīng)運(yùn)而生,該架構(gòu)能有效降低代碼耦合度,使Activity 文件的內(nèi)容更加單一,網(wǎng)絡(luò)請(qǐng)求和布局更新更加高效。 對(duì)于整個(gè)項(xiàng)目而言,整體結(jié)構(gòu)更加清晰,代碼可維護(hù)性也得到大幅度提升。
本文通過對(duì)MVP+Retrofit+OkHttp+RxJava 的研究分析,以《長光衛(wèi)星云極視》項(xiàng)目的登錄模塊為應(yīng)用案例,設(shè)計(jì)并驗(yàn)證Android 應(yīng)用開發(fā)中MVP 模式和Retrofit2+OkHttp3+RxJava2 的響應(yīng)式網(wǎng)絡(luò)請(qǐng)求框架結(jié)合的方法及可行性。
MVP 最早出現(xiàn)在IBM 公司的項(xiàng)目研發(fā)中,由Model 層(負(fù)責(zé)數(shù)據(jù)修改和操作的部分)、View 層(包含所有的UI組件,并負(fù)責(zé)與Presenter 層進(jìn)行所有交互操作)、Presenter層(負(fù)責(zé)所有項(xiàng)目的邏輯)組成[4-5]。
MVP 模式在多名開發(fā)人員協(xié)同開發(fā)及測(cè)試時(shí)體現(xiàn)出了更大的解耦性。 使用MVP 模式的優(yōu)勢(shì)包括:
(1)能將Activity/Fragment 中的任務(wù)獨(dú)立出來;
(2)能將單個(gè)復(fù)雜的多任務(wù)分割成多個(gè)簡單的任務(wù);
(3)可以分離頁面與數(shù)據(jù);
(4)促進(jìn)自動(dòng)化單元測(cè)試[6]。
應(yīng)用程序中MVP 模式的順序圖如圖1 所示。 在一個(gè)常規(guī)的Android 應(yīng)用中會(huì)有兩個(gè)參與者:用戶(使用該程序的人)和數(shù)據(jù)(存儲(chǔ)的信息實(shí)體),以登錄操作為例:
(1)用戶點(diǎn)擊按鈕獲取驗(yàn)證碼;
(2)View 對(duì)象接收到用戶動(dòng)作,并向Presenter 層發(fā)送委派動(dòng)作,即執(zhí)行actionSendMessage()函數(shù);
(3)如果Presenter 層需要接口或數(shù)據(jù)庫中的數(shù)據(jù),則會(huì)向Model 層通過getMessage()函數(shù)發(fā)送一條檢索數(shù)據(jù)的消息,在這個(gè)過程中,Presenter 層與Model 層屬于觀察者與被觀察者的關(guān)系;
(4)當(dāng)Model 層獲取到數(shù)據(jù)時(shí),Presenter 層將觀察到Model 層發(fā)送的事件,同時(shí)執(zhí)行returnSendMessage()函數(shù),向View 層發(fā)送一條消息,并將獲得的驗(yàn)證碼返回給用戶,至此完成獲取驗(yàn)證碼的一次操作。
圖1 MVP 模式的順序圖
Retrofit2+OkHttp3+RxJava2 是當(dāng)下最流行的Android網(wǎng)絡(luò)請(qǐng)求框架之一。 在Android6.0 之前,官方推薦用HttpClient 接口來進(jìn)行網(wǎng)絡(luò)請(qǐng)求,后續(xù)則更改為Java.net下的HttpUrlConnection 接口,但該方式僅支持HTTP/1.0和HTTP/1.1,不支持HTTP/2.0,也不支持多路復(fù)用。 當(dāng)遇到大量網(wǎng)絡(luò)請(qǐng)求時(shí)性能較差。 與HttpUrlConnection 相比,OkHttp3 底層基于Okio 開源庫,使用了比阻塞式IO效率更高的Java NIO(Non-Blocking I/O)。 Retrofit2 作為基于OkHttp 封裝的RESTful 網(wǎng)絡(luò)請(qǐng)求框架, 是當(dāng)前耦合度最低、功能最強(qiáng)大的網(wǎng)絡(luò)請(qǐng)求框架。 Retrofit2 與RxJava2結(jié)合使用時(shí),前者把請(qǐng)求封裝進(jìn)Observable 對(duì)象,在新線程中執(zhí)行HTTP 請(qǐng)求,請(qǐng)求結(jié)束后切換到IO 線程中執(zhí)行用戶的后續(xù)動(dòng)作。總體來說,Retrofit2 用接口的方式進(jìn)行HTTP 網(wǎng)絡(luò)請(qǐng)求,并負(fù)責(zé)請(qǐng)求數(shù)據(jù)以及接收返回結(jié)果,OkHttp3 負(fù)責(zé)HTTP 請(qǐng)求的過程,RxJava2 負(fù)責(zé)異步以及多線程的切換。
OkHttp 是Square 公司的一款開源的網(wǎng)絡(luò)請(qǐng)求庫,使用OkHttp3 進(jìn)行網(wǎng)絡(luò)請(qǐng)求能提高HTTP 請(qǐng)求的加載速度,同時(shí)更節(jié)省帶寬。 OkHttp3 的高效性體現(xiàn)在以下三方面:
(1)允許連接同一主機(jī)的所有請(qǐng)求分享同一個(gè)socket;
(2)通過響應(yīng)式緩存來避免重復(fù)請(qǐng)求;
(3)當(dāng)服務(wù)端存在多個(gè)IP 地址時(shí),若第一個(gè)地址連接失敗,OkHttp3 會(huì)通過嘗試備用IP 地址進(jìn)行靜默恢復(fù)[7]。
與OkHttp 相 同,Retrofit 也 出 自Square 公 司,Retrofit2可以理解為一個(gè)HTTP 網(wǎng)絡(luò)請(qǐng)求的適配器,它將一個(gè)HTTP請(qǐng)求通過Java/Kotlin 接口動(dòng)態(tài)代理的方式來表達(dá),并通過OkHttp3 發(fā)送HTTP 請(qǐng)求。 Retrofit 和OkHttp 的關(guān)系可以總結(jié)為:OkHttp 純粹是一個(gè)HTTP/SPDY 客戶端;Retrofit 是基于HTTP 的高級(jí)REST 抽象。
Retrofit 框架的優(yōu)勢(shì)在于:
(1)使用清晰的注解方式,在最大程度上簡化URL的拼寫形式;
(2)自由度大,支持自定義Converters 及其他業(yè)務(wù)邏輯;
(3)同時(shí)支持同步執(zhí)行和異步執(zhí)行;
(4)支持多種文件解析,如GSON、JSON 和XML 等;
(5)支持RxJava。
RxJava2 是一個(gè)在JAVA 虛擬機(jī)上使用可觀測(cè)的序列組成的基于事件的異步程序庫。它可以理解為是一種觀察者模式,用觀察者和被觀察者來實(shí)現(xiàn)異步操作。 與Android 官方的異步操作方法相比,RxJava 的優(yōu)勢(shì)是簡潔,當(dāng)項(xiàng)目隨著迭代變得繁瑣時(shí),RxJava 仍保持了其簡潔性[8]。
基于Retrofit2+OkHttp3+RxJava2 的響應(yīng)式網(wǎng)絡(luò)請(qǐng)求框架整體流程如圖2 所示。
圖2 網(wǎng)絡(luò)請(qǐng)求流程圖
(1)導(dǎo)入相關(guān)的依賴,將域名傳入一個(gè)Retrofit 構(gòu)造器中;
(2)通過Retrofit.create()方法傳入Java 接口并返回一個(gè)Call 對(duì)象 (該對(duì)象默認(rèn)使用OkHttp3 作為HTTP 請(qǐng)求的Client);
(3)RxJava2 在訂閱時(shí)調(diào)用Call.enqueue()方法來進(jìn)行HTTP 的異步請(qǐng)求;
(4)在網(wǎng)絡(luò)請(qǐng)求獲得響應(yīng)后,Call 對(duì)象對(duì)返回的信息根據(jù)設(shè)置的轉(zhuǎn)置模式進(jìn)行轉(zhuǎn)換;
(5)返回結(jié)果數(shù)據(jù)。
《長光衛(wèi)星云極視》項(xiàng)目采取了MVP 模式以及響應(yīng)式網(wǎng)絡(luò)框架,以獲取登錄驗(yàn)證碼為例,實(shí)現(xiàn)的目錄結(jié)構(gòu)如圖3 所示。
Model 層主要的功能是從服務(wù)端獲取數(shù)據(jù), 由LoginRepository、LoginService 和 LoginServiceimpl 組成。
(1)LoginRepository
獲取用戶驗(yàn)證碼是通過LoginRepository 類中的send-Message()方法來實(shí)現(xiàn)的,通過調(diào)用Retrofit2 的send-Message()的方法,實(shí)現(xiàn)HTTP 請(qǐng)求,在請(qǐng)求成功后,數(shù)據(jù)格式轉(zhuǎn)置為SendMessageBean 實(shí)體類對(duì)象返回,關(guān)鍵代碼如下:
圖3 項(xiàng)目目錄結(jié)構(gòu)圖
View 層主要對(duì)應(yīng)登錄界面LoginFragment,同時(shí)View層定義了LoginView 接口,接口中包括獲取短信成功及失敗的回調(diào)方法,LoginPresenter 通過接口returnSendMessage()和returnSendMessageError()方法與LoginFragment 進(jìn)行交互,關(guān)鍵代碼如下:
Presenter 層是View 層調(diào)用Model 層的橋梁,Login-Presenter 持有LoginView 的引用,在方法中通過用戶在LoginFragment 中觸發(fā)按鍵時(shí),調(diào)用LoginPresenter 的對(duì)應(yīng)方法, 通過LoginService 對(duì)象進(jìn)行對(duì)Model 層的訪問,同時(shí)獲取HTTP 返回值的觀察者對(duì)象,并將值傳遞給LoginView 對(duì) 象,在LoginFragment 中 進(jìn) 行UI 更 新。 關(guān) 鍵代碼如下:
為了快速開發(fā)和提高代碼質(zhì)量, 在使用MVP+Retrofit2+OkHttp3+RxJava2 模式時(shí),應(yīng)采取JUnit 單元測(cè)試的方式,對(duì)單個(gè)Presenter 文件和Service 文件編譯運(yùn)行。 該方式無需在真機(jī)或模擬器上全局調(diào)試,同時(shí)也無需考慮其他類文件的影響。通過JUnit 單元測(cè)試,可以快速定位到問題。
本文中通過對(duì)MVC 模式以及HTTP 網(wǎng)絡(luò)請(qǐng)求進(jìn)行系統(tǒng)的分析,發(fā)現(xiàn)在一個(gè)中大型項(xiàng)目中,采用MVC 模式,會(huì)使得充當(dāng)Controller 層的Activity/Fragment 同時(shí)充當(dāng)View 層的角色,使得文件代碼量臃腫,增加了業(yè)務(wù)邏輯的耦合度,在項(xiàng)目開發(fā)上乃至后期維護(hù)上都造成了很大的問題。 與此同時(shí),當(dāng)項(xiàng)目進(jìn)行復(fù)雜多次的網(wǎng)絡(luò)請(qǐng)求場(chǎng)景時(shí),官方提供的HttpURLConnection 類表現(xiàn)出了性能低下、邏輯復(fù)雜的缺陷。
根據(jù)MVP 模式的設(shè)計(jì)思想,將業(yè)務(wù)事件交付給Presenter 層進(jìn)行處理,使得Model 層和View 層做到了完全解耦,在整體項(xiàng)目中開發(fā)模塊職責(zé)劃分更明顯,使邏輯代碼的耦合度降低,便于后期的維護(hù)和二次開發(fā)。 與此同時(shí)基于Retrofit+OkHttp+RxJava 的響應(yīng)式網(wǎng)絡(luò)框架,在HTTP 請(qǐng)求上更為高效,在業(yè)務(wù)處理上相比HttpURLConnection 更簡化。 RxJava 的使用,會(huì)隨著網(wǎng)絡(luò)請(qǐng)求邏輯變得越來越復(fù)雜,依然保持簡潔。
本文通過上述思想設(shè)計(jì)并實(shí)現(xiàn)了《長光衛(wèi)星云極視》用戶登錄模塊,驗(yàn)證了MVP 模式與Retrofit+OkHttp+Rx-Java 的網(wǎng)絡(luò)框架在Android 應(yīng)用開發(fā)中結(jié)合的可行性,同時(shí)給出了設(shè)計(jì)思路及關(guān)鍵程序,并最終達(dá)到預(yù)期效果。