Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> Android開發 >> 關於android開發 >> 【騰訊Bugly干貨分享】基於RxJava的一種MVP實現,buglyrxjava

【騰訊Bugly干貨分享】基於RxJava的一種MVP實現,buglyrxjava

編輯:關於android開發

【騰訊Bugly干貨分享】基於RxJava的一種MVP實現,buglyrxjava


本文來自於騰訊bugly開發者社區,非經作者同意,請勿轉載,原文地址:http://dev.qq.com/topic/57bfef673c1174283d60bac0

Dev Club 是一個交流移動開發技術,結交朋友,擴展人脈的社群,成員都是經過審核的移動開發工程師。每周都會舉行嘉賓分享,話題討論等活動。

本期,我們邀請了騰訊IEG Android 開發工程師——戴俊,為大家分享《基於RxJava的一種MVP實現》。

分享內容簡介:

RxJava是一個實現Java響應式編程的庫,讓異步事件以序列的形式組織。MVP則通常用來將View業務層與Model層分離開來,兩者結合起來可輕松實現業務解耦、線程控制、單元測試等等強大功能

內容大體框架:

下面是本期分享內容整理


Hello,大家好,我是戴俊。目前在IEG騰訊動漫主要負責Android端的開發工作。

第一次進行這種微信群的分享,如果有任何疑問,歡迎大家在分享結束後提問。下面開始我們今天的分享。

1. Android開發框架的選擇

我們知道原生Android開發已經是一個基礎的MVC框架,所以在項目剛開始開發的時候並沒有遇到太多問題。

對一個經典的Android MVC框架來講,它的結構大概是下面這樣(圖片來自參考文獻)

這樣的結構下,Activity層既承擔了View層的一部分工作(因為XML作為View層的一部分功能實在太弱了),又承擔Controller層的工作,因此當業務變化時,Activity層會極劇膨脹。

拿我們項目早期的例子,一個Activity曾經最多達到了2000到3000行,重構的時候極其痛苦。

要解決這個問題,主要的辦法有兩種:

  • 第一種是分層
  • 第二種是模塊化。

兩個方法最終要實現的都是解耦。分層講的是縱向層面上的解耦,模塊化則是橫向上的解耦。

我們今天要討論的MVP就是一種通過分層來進行解耦的框架。

2. 如何一步步搭建MVP分層框架

如果你是個習慣了讀文檔的老司機,可以直接參考下面幾篇文章

當然如果覺得看官方的示例太麻煩,那麼下面我們就來講解一下如何實現一個簡單的MVP構架。

這是一個比較典型的MVP結構圖(圖片來自參考文獻),相比於第一張圖,多了兩個層,一個是Presenter和DataManager層。

多出這兩個層到底有什麼作用,下面我們來用代碼說明。

首先我們假設有一個從服務端獲取字符串並顯示的手機上的簡單功能。下面是主界面的代碼

Activity裡面包含了幾個文件,一個是View層的對外接口MainView,一個是P層的Presenter。

首先看View層的對外接口文件

因為這個功能比較簡單,只需要在設備上顯示一個字符串,所以只有一個接口方法onShowString(),再看P層代碼

從上面三個文件可以看到,View層通過注冊Listener將自己的接口MainView交給了Presenter, 而Presenter層持有Model層的也只是一個接口。通過Presenter層將業務層與展現層隔離了開來,這樣的好處是什麼?

我們知道接口的一個作用通常是用來抽象行為,對外部屏蔽實現細節。所以對於View層來說,業務細節被屏蔽了,對業務層來說,展示細節被屏蔽了。而對於處於中間的Presenter層來說,它就像一個接口拼裝器,把View層發出的請求傳遞給業務層,把業務層返回的數據又送還給View層展示,至於前後兩端怎麼實現的,它才不用關心。

接口的第二個作用是可以用來切換實現。我們先看下面的代碼。

從上面三個文件可以看到,業務層對外的只有一個接口,實現卻有兩個(DataSourceImpl和DataSourceTestImpl)。從名字大家就能看出來有什麼作用了,一個是正常環境的業務層實現,一個是測試環境的業務層實現。

這裡我們設想一個場景:

開發同學接到一個新的需求,設計稿也輸出完成了,然而後台的接口卻遲遲沒到,怎麼辦? 現在通過MVP,我們把業務層實現切換到DataSourceTestImpl,是不是可以先自己假寫數據,調好一切前端和交互,然後泡一杯咖啡等後台同學把接口寫完聯調?或者有時候為了重現一個bug,要在線上寫一條髒數據,測試完再刪除?

類似的應用場景其實有非常非常多,這裡我們就看到了使用接口解耦的一個好處了,不僅把業務層和展示層解耦開來,還把Android開發同其它的一切的外部數據依賴都解耦開來。

這裡我想提到之前討論過的單元測試問題,很多同學反饋項目開發過程中沒有做過,或者沒有時間精力去做單元測試,或者因為業務變化太大導致無法做單元測試。其實在我們項目中也遇到過樣的問題,但其實通過這樣分層之後,才發現單元測試其實是完全可以推進的,也完全不用再擔心測試的時候會把髒數據寫到線上的問題了。

到現在為止一個基於MVP簡單框架就搭建完成了,但其實還遺留了一個比較大的問題。

很多同學可能已經發現了,Presenter層在調用業務層的時候是直接調用的,而Android規定,主線程是無法直接進行網絡請求,會拋出NetworkOnMainThreadException異常。

所以在presenter層,我們需要進行一項線程切換的工作,這樣才能保證“所有的IO操作都應當在線程中完成,主線程只負責頁面渲染的工作”這一優化准則。

當然,Android本身提供一些方案,比如下面這種:

通過新建子線程進行IO讀寫獲取數據,然後通過主線程的Looper將結果通過傳回主線程進行展示,這種方案是勉強也行得通的。

但問題也有,一是線程需要額外管理,不可能每次發請求都要開啟一個線程;二是適應性差,假如數據請求有先後依賴,有並行的情況,這樣的寫法變得髒亂無比。

好在有了RxJava ,可以比較方便的解決這個問題。

3. 使用RxJava來進行線程控制

RxJava是一個天生用來做異步的工具,相比AsyncTask,Handler等,它的優點就是簡潔,無比的簡潔。在Android中使用RxJava需要加入下面兩個依賴。

 compile 'io.reactivex:rxjava:1.0.14' 
 compile 'io.reactivex:rxandroid:1.0.1'

這裡我們直接介紹如何使用RxJava解決這個問題,在presenter中修改方法getData()。

簡單解釋一下,dataAction是我們的數據業務邏輯,viewAction是界面的顯示邏輯,通過RxJava的傳遞和變換,dataAction會在由RxJava管理的IO線程—Schedulers.io() 中執行,而viewAction則會在UI線程—AndroidSchedulers.mainThread()中執行。

RxJava當然不止這麼簡單,還有別的玩法,比方說進入一個界面的時候,需要先加載緩存的數據,然後再從網絡獲取更新的數據進行刷新。有的時候,可能還需要處理IO過程中的異常情況,加入RxJava的異常處理參數。

RxJava的使用場景遠不止這些,線程變換、數據變換、接口順序依賴、接口並發請求這些要求對它來說都是小菜一碟。當然,有些同學可能覺得RxJava入手有些困難,代碼也會變得不那麼直觀,但相信只要大家慢慢熟悉它之後,它就會變得無比討人喜歡。

下面列出了一些常見的RxJava的常用場景,其實還有更多的其它功能等待著大家去挖掘。

上面這些功能都可以通過RxJava來輕松完成。具體的使用就不再多講了,大家可以參考下面的文章:(Google文章名就可以了)

1.給 Android 開發者的 RxJava 詳解
2.RxJava 與 Retrofit 結合的最佳實踐
3.RxJava使用場景小結
4.How To Use RxJava

結語

至此為止,通過MVP+RxJava的組合,我們已經構建出一個比MVC更靈活的Android項目開發框架,好處大概有以下幾點:

以上就是我今天的分享,內容上可能還有不足甚至不夠好的地方,歡迎大家指出,一起討論學習。

這裡也順便打個廣告,歡迎大家下載騰訊動漫App,這裡有最新最熱的國漫日漫,支持正版,你我共享。

問答環節

Q1:對於這樣的一個MVP項目,它裡面的包結構怎樣規劃比較清晰合理呢?

包結構的通常分法有兩種:一種是按功能模塊分,把某一個功能的presenter, activity,view層接口放到一起;一種是按類型分,P層M層和V層分成三個包。實際項目應用,我個人傾向於第一種,這種無論是開發過程,還是排查問題都會方便很多。當然,不同的項目還是有不同的分法的,不一而論。

Q2:耗時操作可能引起的內存洩露問題,請問是如何處理的。
Q3:用mvp時,請問你們在哪裡釋放一些引用,防止內存洩露的
Q4:p持有v的引用,請問怎麼解決Activity的內存洩露問題?
Q5:網特別慢的時候,應用退出,但網絡請求還沒結束,p層回調持有上下文造成內存洩露,一般怎麼解決啊。

這幾個問題其實比較類似,我們在實際項目中,presenter會隨著activity的生命周期進行銷毀,比如在onDestroy方法中對presenter進行置空和引用解綁, 當然我們可以給所有的Presenter寫一個共有父類BasePresenter,專門來處理這個問題。

Q6:需求包含列表頁的時候,列表項也是按照mvp的思想來分層,還是封裝成模塊比較合適

目前我們的做法是直接封裝成模塊,簡單的問題不宜過度設計

Q7:想問一下騰訊動漫這個app目前用的就是您講的這個架構嗎,在實際用的過程中有遇到什麼問題嗎

是的,我們已經使用了這個架構。實際使用過程中,經常會糾結的問題是業務邏輯層要不要再次獨立分層。

Q8:項目中做測試是好事,但我覺得建議去掉TestImpl測試文件。如果項目打包時,打到包裡,會導致包變大,這種測試建議用node寫個簡單的服務,不知道嘉賓你咋看?

是的。正式項目中,可以通過注解,或者proguard或者gradle的配置將這些測試文件不打到包裡。Node寫服務的話是不是又要搭環境,這裡的做法就是不使用任何外部環境依賴。

Q9:mvp一般都是activity和Fragment加入presenter層,那麼列表adapter裡的邏輯是否也要加上presenter層呢

Adapter其實跟View更接近的一個東西,它是用來處理重復顯示問題。一般來說,我們傳給adapter的數據完好能直接顯示的,建議在業務邏輯層將數據拼裝好再傳進去。
答:Adapter其實跟View更接近的一個東西,它是用來處理重復顯示問題。一般來說,我們傳給adapter的數據完好能直接顯示的,建議在業務邏輯層將數據拼裝好再傳進去。

Q10:我們項目中采用了MVP但是沒有用RxJava,m與p層采用回調方式,這樣m通過回調間接引用p,p層有v的引用。如果在網絡情況不好頻繁打開關閉頁面在網絡請求結束前是否會有內存洩漏問題。rx是否能解決這個問題。還有當網絡結束回調時v對應activity destory了怎麼辦。每次都去判斷activity狀態嗎?

Rx不能解決內存洩漏的問題,前面2.3.7問題都提到了,通常的做法是在activity層銷毀的時候進行解綁。回調時activity destory的話,我們現在的做法是對view層接口進行一次空值判定。如果有更好的辦法,也歡迎大家提出來討論

Q11:有時候例如自定義view依賴於服務器返回的model,裡面也有很多根據model屬性去繪制的過程,這種情況怎麼處理?在P層拋出一個model的get方法嗎?

自定義的View跟Activity一樣,我們統稱為View層。上面的例子中View層只有一個接口MainView,實際項目中,View層可能會實現好幾個接口。對一個經常會被利用的自定義View,會額外給它新建一個接口。

Q12:你的例子中p層實現中getDate()方法對數據進行了處理,是否m層只是單純的獲取原始數據,對於數據上的業務也放入到p層中處理,有沒有好的方式能夠復用有關數據業務的這塊邏輯

嗯,這個問題我們確實也遇到了。在項目實際操作過程中,如果有比較復雜業務流程,我會單獨再分離出一層業務層,業務層再去調用dataSource取數據。如果只是單純的取數據展示,現在這樣就夠了,盡量避免過度設計。

Q13:為了更好的解偶每一層,你們用MVP時 是否每層都有自己的數據結構,如果有的話,層與層之間的數據結構轉換開銷大不大?

目前來講,大部分的業務都是一個數據結構穿透使用的,偶爾會有數據結構重新封裝, 影響不大。我個人判斷的話,相比IO處理,數據結構的轉換開銷還是小的,而且,如果有很多復雜轉換的話,保證不要在UI線程中做,也不會太大問題。

Q14:activity與p層用接口的方式銜接的價值在哪?另外如何界定展現方法在哪調用?比如頁面需要顯示一個標題,內容是從之前頁面傳過來的,那是在activity接收後就直接顯示?還是先傳遞到p層再回調activity的顯示方法?感謝

價值在於,把presenter 與activity解耦之後,我可以在別的activity使用這個presenter層邏輯,也可以在這個activity 裡調用其它頁面的presenter方法。如果是前頁傳過來的,直接顯示就好,不做過度設計。

Q15:rxJava使用lamaba的語法格式的話貌似會將代碼縮減很多,請問嘉賓有試過這種方式嗎?這個對項目的性能會有什麼影響嗎?因為我試用過幾次後一直出現oom的問題

lambda表達式會讓語法看起來更簡潔,非常推薦使用。但我們的項目目前只能使用jdk 7,悲傷。如果後面我們有機會切換的話,可以再一起分享一下。

Q16:rxjava怎麼實現隊列像handler message那樣,就是隊列執行,不是並發執行?

rxJava中的just方法和from方法都是以隊列形式發出事件。我猜你想問的問題可能是:一個接口的請求依賴另一個API請求返回的數據,這就是嵌套回調問題。可以找下大頭鬼Bruce的一篇文章,《RxJava使用場景小結》,裡面有介紹的,這裡不詳細討論了。

更多精彩內容歡迎關注bugly的微信公眾賬號:

騰訊 Bugly是一款專為移動開發者打造的質量監控工具,幫助開發者快速,便捷的定位線上應用崩潰的情況以及解決方案。智能合並功能幫助開發同學把每天上報的數千條 Crash 根據根因合並分類,每日日報會列出影響用戶數最多的崩潰,精准定位功能幫助開發同學定位到出問題的代碼行,實時上報可以在發布後快速的了解應用的質量情況,適配最新的 iOS, Android 官方操作系統,鵝廠的工程師都在使用,快來加入我們吧!

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved