編輯:Android資訊
軟件代碼庫各個不同的部分應當彼此獨立,其整體卻猶如一部運轉良好的機器
Android的開發生態系統發展迅速,每周都有變化,人們不停地創建新工具、更新資源庫、撰寫博文、發表演講。只要享受一個月的假期,回來的時候支持庫和/或Play Services都更新換代了。
筆者與 ribot團隊 合作開發Android應用已有超過三年時間。在這段時間裡,我們用來構建Android應用的架構與技術一直在不斷進化。在本文中,我們將具體闡述這些架構變更背後的經驗、失誤還有推論。
早在2012年,我們的代碼庫總是采用基礎架構,並未使用任何網絡庫,還是用老一套的AsyncTasks。下面的圖表粗略地演示了這個架構。
初始架構
代碼共分兩層:控制從REST API檢索/保存數據的數據層(data layer),還有負責在UI上控制與展示數據的視圖層(view layer)。
APIProvider提供方法,讓Activities和Fragments能夠很容易地與REST API交互。運用URLConnection和AsyncTasks來執行單獨線程中的網絡調用,並通過回調向Activities返回結果。
類似地,CacheProvider包含了從SharedPreferences或SQLite數據庫檢索存儲數據的方式,通過回調將結果返回給Activities。
這個辦法的主要問題在於,視圖層責任過大。試想一個簡單的通用場景:應用程序在加載文章列表時,將其緩存到SQLite數據庫中,並最終展示在ListView中。具體執行如下:
這是個簡單的例子。在真實案例場景中,REST API可能不會按照浏覽所需的那樣返回數據,因此Activity會設法在展示數據之前對其進行轉換或過濾。另一個常見案例:在使用 loadPosts() 方法獲取需要從別處拿到的參數時,比如由Play Services SDK提供的電子郵件地址,很有可能SDK會通過回調異步返回郵件,也就是說我們現在有三層嵌套回調(nested callbacks)。如果復雜性繼續增加,這個方法會導致所謂的回調地獄(callback hell)。
總結:
差不多在兩年時間中,我們都在采用前面描述的那種架構。在那段時間裡,我們做了一些修正,但是解決問題時收效甚微。例如,我們增加了一些helper類,以減少Activities和Fragments中的代碼,並開始在APIProvider中使用 Volley 。盡管如此,在應用代碼測試時還是面臨測試友好性問題與回調地獄頻繁出現的問題。
直到2014年我們發現了 RxJava ,在嘗試了幾個樣例項目後,我們發現這可能是解決嵌套回調問題的終極解決辦法。如果對響應式編程不熟悉的話,可以參考 這篇簡介 。簡單來講,RxJava允許用戶通過異步流管理數據,並提供很多可用在事件流中的 operator ,方便用戶修改、篩選或合並數據。
考慮到前些年遭受的痛苦,我們開始考慮新應用的架構是什麼樣的,然後得出了這個。
與頭一個方法類似,這個架構也可以分為兩層,分別是數據層與視圖層。數據層包含DataManager,還有一系列helper。視圖層由諸如Fragments、Activities、ViewGroups等Android框架組件構成。
Helper類(圖表第三列)包含具體的職責,同時執行方式也很簡潔。例如大多項目包含訪問REST API的helper,從數據庫讀取數據的helper或者與第三方SDK交互的helper。不同的應用程序包含不同數量的helper,不過最常見的helper有:
大多數helper類中的公共方法會返回RxJava Observables。
DataManager是這個架構的核心,它廣泛運用了RxJava operator來合並、篩選與轉換從helper類中獲得的數據。DataManager的目標是通過提供准備顯示的數據,來減少Activities 和Fragments的工作量,而且這些數據一般無需任何轉換。
下面的代碼就是DataManager方法的實例。
public Observable<Post> loadTodayPosts() { return mRetrofitService.loadPosts() .concatMap(new Func1<List<Post>, Observable<Post>>() { @Override public Observable<Post> call(List<Post> apiPosts) { return mDatabaseHelper.savePosts(apiPosts); } }) .filter(new Func1<Post, Boolean>() { @Override public Boolean call(Post post) { return isToday(post.date); } }); }
像Activities或Fragments之類的視圖層組件會簡單調用這個方法,並訂閱返回的Observable。一旦訂閱完成,Observable所發出的不同文章就能直接加入到Adapter中,以便在RecyclerView或類似組件中顯示。
這個架構的最後一個元素是Eventbus(事件總線),它允許我們將數據層的事件進行廣播,因此視圖層的多個組件能夠訂閱這些事件。例 如,DataManager中的signOut()方法可以在Observable完成時發布一個事件,讓多個訂閱這個事件的Activities修改 UI,顯示為登出狀態。
為什麼這個方法更好?
RxJava Observables和operators使得嵌套回調不再有必要。
還有什麼問題呢?
在過去的一年中,像MVP、MVVM這樣的一些架構模型在Android社區受到了熱捧。在 樣例項目 與 文章 中研究過這些模型之後,我們發現MVP能夠對我們目前的方法帶來很有價值的改進。由於我們目前的架構分為兩層(視圖與數據層),加上MVP也很自然。我們 只需增加一個新的展示層(a new layer of presenters),將一部分代碼從視圖層移過去就可以了。
基於MVP的架構
數據層保持不變,不過現在改名為模型層(Model),以便名符其實。
展示層控制加載來自模型層的數據,並在結果准備好之後調用視圖層的正確方法來顯示。它訂閱DataManager返回的Observables,因此必須處理類似 調度 與 訂閱 之類的工作。此外,它可以分析錯誤代碼,或者在需要時在數據流中應用額外操作。例如,如果我們需要篩選一些數據,而這個篩選無法在其他地方復用,那麼用展示層來實現會比在DataManager實現要更好。
下面是在展示層中公共方法的案例。這部分代碼訂閱了從dataManager.loadTodayPosts()方法返回的Observable。
public void loadTodayPosts() { mMvpView.showProgressIndicator(true); mSubscription = mDataManager.loadTodayPosts().toList() .observeOn(AndroidSchedulers.mainThread()) .subscribeOn(Schedulers.io()) .subscribe(new Subscriber<List<Post>>() { @Override public void onCompleted() { mMvpView.showProgressIndicator(false); } @Override public void onError(Throwable e) { mMvpView.showProgressIndicator(false); mMvpView.showError(); } @Override public void onNext(List<Post> postsList) { mMvpView.showPosts(postsList); } }); }
mMvpView是這個展示層正在assist的視圖層組件。一般MVP視圖是Activity、Fragment或ViewGroup實例。
就像之前的架構那樣,視圖層包含像ViewGroups、Fragments或Activities這樣的標准框架組件。這些組件的主要區別在於 沒有直接訂閱Observables,而是執行MVP視圖,提供一系列類似showError() 或showProgressIndicator()之類的簡明方法。視圖組件還控制處理類似點擊事件之類的與用戶交互,並通過調用展示層的正確方法來執 行。例如,如果我們有一個加載文章列表的按鈕,Activity就會從onClick監聽那裡調用 presenter.loadTodayPosts()。
想要查看基於MVP的完整架構,請查看 Android Boilerplate project on GitHub 或者 ribot’s architecture guidelines 。
為什麼這個方法更好?
還有什麼問題?
需要注意的是,這個架構並不完美。事實上,認為它是唯一而且完美的架構,能夠一勞永逸的解決問題這樣的想法太過天真。Android的生態系統會繼續保持高速發展,我們必須持續探索、閱讀、實驗,才能找到構建優秀Android應用的更佳途徑。
首先我們來回憶一下傳統用Activity進行的頁面切換,activity之間切換,首先需要新建intent對象,給該對象設置一些必須的參數,然後調用startAc
本文由碼農網 – 小峰原創,轉載請看清文末的轉載要求,歡迎參與我們的付費投稿計劃! Android開發是目前最熱門的移動開發技術之一,隨著開發者的不斷努力
除了Android系統自帶的Button按鈕以外,還提供了帶圖標的按鈕ImageButton 要制作帶圖標的按鈕,首先要在布局文件中定義ImageButton,然
1. 功能介紹 AndroidEventBus是一個Android平台的事件總線庫, 它簡化了Activity、Fragment、Service等組件或者對象之間