細談Android應用架構
說真的,接觸Android編程很少,一般用visual studio的c#,vc++,c#應用架構熟悉,Java熟悉,Python熟悉,但對Android陌生,朋友說學Android很久啦,而且已經上班,他說,Android開發生態圈的節奏非常之快。每周都會有新的工具誕生,類庫的更新。我不得不學習它,其實支持Android的集成開發環境也多。
以Android Studio集成開發環境為例
2013年GoogleI/O大會首次發布了Android Studio IDE(Android平臺集成開發環境)。它基于Intellij IDEA開發環境,旨在取代Eclipse和ADT(Android開發者工具)為開發者提供更好的開發工具。既然Google一直在努力推廣,相信不久以后就有望趕上Eclipse。
- 相比Eclipse,Android Studio IDE有自己的特點:
- 對UI界面設計和編寫代碼有更好地支持,可以方便地調整設備上的多種分辨率。
- 同樣支持ProGuard工具和應用簽名。
- 不過,目前版本的Android Studio不能在同一窗口中管理多個項目。每個項目都會打開一個新窗口。我猜這是借鑒了Intellij IDEA的行為,而且Google近期不會對此做出調整。
- 支持Gradle自動化構建工具,這真是極好的,但對于剛從Eclipse平臺轉移過來的開發者來說還需要一段時間去學習和適應。
二、下載和安裝相關軟件
2.1、開發環境
物理機版本:Win 8(64位)
Java SDK版本:jdk-7u45-windows-x64(64位)
Android Studio版本:android-studio-bundle-135.1740770-windows
注:物理機版本和Java SDK版本必須要保持一致,即:同為64位或者同為32位。
2.2、下載JDK
我下載的是JDK1.7的版本,下載地址:http://www.oracle.com/technetwork/java/javase/downloads/jdk7-downloads-1880260.html
下載到本地電腦后雙擊進行安裝。JDK的安裝過程比較簡單,安裝過程基本上就是一路Next即可,做Java開發的人都會,在安裝的時候只需要注意將JDK和JRE安裝到同一個目錄即可,JDK默認安裝成功后,會在系統目錄下出現兩個文件夾,一個代表jdk,一個代表jre

- javac:Java編譯器,將源代碼轉成字節碼。
- jar:打包工具,將相關的類文件打包成一個文件。
- javadoc:文檔生成器,從源碼注釋中提取文檔。
- java:運行編譯后的java程序。
2.3、配置Windows上JDK的變量環境


2.4、Android Studio下載
下載地址:http://developer.android.com/sdk/index.html,注意,下載Android Studio要FQ才行
下載完成之后,得到一個如下圖所示的安裝包:
2.5、Android Studio安裝
下載完成后,就可以開始安裝了,用鼠標雙擊android-studio-bundle-135.1740770-windows.exe啟動安裝程序,Android Studio安裝過程如下圖所示:
AndroidStudio是集成了Android SDK的,所以在安裝的時候記得勾選上Android SDK
這個安裝過程有點久,需要一點時間。
2.6、啟動運行Android Studio
Android Studio啟動過程如下圖所示:
第一次啟動AndroidStudio時,需要設置一下SDK的安裝目錄,因此會彈出如下圖所示的對話框,
設置Android SDK的安裝目錄,如下圖所示:
打開AndroidStudio之后,默認會幫我們創建一個app的項目,如下圖所示:
運行這個默認創建好的項目,為了運行方便,我們直接使用真機作為模擬器運行,如下圖所示:
注意,使用真機調試時,手機必須開啟USB調試模式才行,如下圖所示:
在手機上面的運行結果如下圖所示:
手機上顯示應用的桌面上也顯示出了我們這個Android應用的圖標,如下圖所示:
到此,使用Android Studio搭建Android集成開發環境的工作就全部完成了,測試也通過了!
三、Android Studio的簡單使用
3.1查看安裝好的Android SDK
已經安裝好的Android SDK版本如下圖所示:
我們可以看到,截止到今天為止,Anddroid的版本已經更新到Android5.1(API22)了,更新速度真的很快啊,Android5.1(API22)的相關內容現在還是處于【Not installed】狀態,也就是還沒有下載和安裝,SDK Manager默認已經選中Android5.1(API22)的全部內容,我們如果想在Android5.1(API22)的平臺下開發Android應用,那么可以下載Android5.1(API22)的相關內容,如下圖所示:
默認下載和安裝好的Android5.0.1的開發包已經滿足我們開發Android應用的需求了,如下圖所示:
當然使用eclipse,visual studio也可以開發,具體略
先談談其Android的應用架構
可以大致表示為下圖。

代碼被劃分為兩層結構:Data Layer(數據層)負責從REST API或者持久數據存儲區檢索和存儲數據;View Layer(視圖層)的職責是處理并將數據展示在UI上。
APIProvider提供了一些方法,使Activity和Fragment能夠很容易的實現與REST API的數據交互。這些方法使用URLConnection和AsyncTask在一個單獨的線程內執行網絡請求,然后通過回調將結果返回給Activity。
按照同樣的方式,CacheProvider 所包含的方法負責從SharedPreferences和SQLite數據庫檢索和存儲數據。同樣使用回調的方式,將結果傳回Activity。
存在的問題:
使用這種結構,最主要的問題在于View Layer持有太多的職責。想象一個簡單且常見的場景,應用需要加載一個博客文章列表,然后緩存這些條目到SQLite數據庫,最后將他們展示到ListView等列表視圖上。Activity要做到以下幾個步驟:
- 通過APIProvider調用loadPosts方法(回調)
- 等待APIProvider的回調結果,然后調用CacheProvider中的savePosts方法(回調)
- 等待CacheProvider的回調結果,然后將這些文章展示到ListView等列表視圖上
- 分別處理APIProvider和CacheProvider回調中潛在的異常。
這是一個非常簡單的例子,在實際開發環境中REST API返回的數據可能并不是View直接需要的。因此,Activity在進行展示之前不得不通過某種方式將數據進行轉換或過濾。另一個常見的情況是,調用loadPosts( )所需要的參數,需要事先從其他地方獲取到,比如,需要Play Services SDK提供一個Email地址參數。就像SDK通過異步回調的方式返回Email地址,這就意味著現在我們至少有三層嵌套的回調。如果繼續添加復雜的業務邏輯,這種架構就會陷入眾所周知的Callback Hell(回調地獄)。
總結:
- Activitty和Fragment變得非常龐大并且難以維護。
- 太多的回調嵌套意味著丑陋的代碼結構而且不易讀懂和理解。如果在這個基礎上做更改或者添加新特性會感到很痛苦。
- 單元測試變得非常有挑戰性,如果有可能的話,因為很多邏輯都留在了Activity或者Fragment中,這樣進行單元測試是很艱難的。
RxJava驅動的新型架構
我們使用上文提到的組織架構差不多兩年的時間。在那段時間內,我們做了一些改進,稍微緩解了上述問題。例如,我們添加了一些Helper Class(幫助類)用來減少Activity和Fragment中的代碼,在APIProvider中使用了Volley。盡管做出了這些改變,我們應用程序的代碼還是不能進行友好的測試,并且Callback Hell(回調地獄)的問題還是經常發生。
直到2014年我們開始了解RxJava。在嘗試了幾個示例項目之后,我們意識到她可能最終幫助我們解決掉嵌套回調的問題。如果你還不熟悉響應式編程,可以閱讀本文(譯者注:譯文點這里那些年我們錯過的響應式編程)。簡而言之,RxJava允許通過異步流的方式處理數據,并且提供了很多操作符,你可以將這些操作符作用于流上從而實現轉換,過濾或者合并數據等操作。
考慮到經歷了前幾年的痛苦,我們開始考慮,一個新的應用程序體系架構看起來會是怎樣的。因此,我們想出了這個。

類似于第一種架構,這種體系架構同樣被劃分為Data Layer和View Layer。Data Layer持有DataManager和一系列的Helper classe 。View Layer由Android的Framework組件組成,例如,Fragment,Activity,ViewGroup等。
Helper classes(圖標中的第三列)有著非常特殊的職責以及簡潔的實現方式。例如,很多項目需要一些幫助類對REST API進行訪問,從數據庫讀取數據,或者與三方SDK進行交互等。不同的應用擁有不同數量的幫助類,但也存在著一些共性:
- PreferencesHelper:從SharedPreferences讀取和存儲數據。
- DatabaseHelper:處理操作SQLite數據庫。
- Retrofit services:執行訪問REST API,我們現在使用Retrofit來代替Volley,因為它天生支持RxJava。而且也更好用。
幫助類里面的大多數public方法都會返回RxJava的Observable。
DataManager是整個架構中的大腦。它廣泛的使用了RxJava的操作符用來合并,過濾和轉換從幫助類中返回的數據。DataManager旨在減少Activity和Fragment的工作量,它們(譯者注:指Activity和Fragment)要做的就是展示已經準備好的數據而不需要再進行轉換了。
下面這段代碼展示了一個DataManager方法可能的樣子。這個簡單的示例方法如下:
- 調用Retrofit service從REST API加載一個博客文章列表
- 使用DatabaseHelper保存文章到本地數據庫,達到緩存的目的
- 篩選出今天發表的博客,因為那才是View Layer想要展示的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
public Observable loadTodayPosts ( ) {
return mRetrofitService . loadPosts ( )
. concatMap ( new Func1 , Observable > ( ) {
@ Override
public Observable call ( List apiPosts ) {
return mDatabaseHelper . savePosts ( apiPosts ) ;
}
} )
. filter ( new Func1 ( ) {
@ Override
public Boolean call ( Post post ) {
return isToday ( post . date ) ;
}
} ) ;
}
|
在View Layer中諸如Activity或者Fragment等組件只需調用這個方法,然后訂閱返回的Observable即可。一旦訂閱完成,通過Observable發送的不同博客,就能夠立即被添加進Adapter從而展示到RecyclerView或其他類似控件上。
這個架構的最后元素就是Event Bus(事件總線)。它允許我們在Data Layer中發送事件,以便View Layer中的多個組件都能夠訂閱到這些事件。比如DataManager中的退出登錄方法可以發送一個事件,訂閱這個事件的多個Activity在接收到該事件后就能夠更改它們的UI視圖,從而顯示一個登出狀態。
為什么這種架構更好?
- RxJava的Observable和操作符避免了嵌套回調的出現。
- DataManager接管了以前View Layer的部分職責。因此,它使Activity和Fragment變得更輕量了。
- 將代碼從Activity和Fragment轉移到了DataManager和幫助類中,就意味著使寫單元測試變得更簡單。
- 明確的職責分離和DataManager作為唯一與Data Layer進行交互的點,使這個架構變得Test-Friendly。幫助類和DataManager能夠很容易的被模擬出來。
我們還存在什么問題?
- 對于龐大和復雜的項目來講,DataManager會變得非常的臃腫和難以維護。
- 盡管View Layer諸如Activity和Fragment等組件變得更輕量,它們讓然要處理大量的邏輯,如管理RxJava的訂閱,解析錯誤等方面。
集成MVP
在過去的一年中,幾個架構設計模式,如MVP或者MVVM在Android社區內已經越來越受歡迎了。通過在示例工程和文章中進行探索后,我們發現MVP,可能給我們現有的架構帶來非常價值的改進。因為當前我們的架構已經被劃分為兩個層(視圖層和數據層),添加MVP會更自然些。我們只需要添加一個新的presenter層,然后將View中的部分代碼轉移到presenter就行了。

留下的Data Layer保持不變,只不過為了與這種模式保持一致性,它現在被叫做Model。
Presenter負責從Model中加載數據,然后當數據準備好之后調用View中相對應的方法。還負責訂閱DataManager返回的Observable。所以,他們還需要處理schedulers和subscriptions。此外,它們還能分析錯誤代碼或者在需要的情況下為數據流提供額外的操作。例如,如果我們需要過濾一些數據而且這個相同的過濾器是不可能被重用在其他地方的,這樣的話在Presenter中實現比在DataManager中或許更有意義。
下面你將看到在Presenter中一個public方法將是什么樣子。這段代碼訂閱我們在前一節中定義的dataManager.loadTodayPosts( )所返回的Observable。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
public void loadTodayPosts ( ) {
mMvpView . showProgressIndicator ( true ) ;
mSubscription = mDataManager . loadTodayPosts ( ) . toList ( )
. observeOn ( AndroidSchedulers . mainThread ( ) )
. subscribeOn ( Schedulers . io ( ) )
. subscribe ( new Subscriber > ( ) {
@ Override
public void onCompleted ( ) {
mMvpView . showProgressIndicator ( false ) ;
}
@ Override
public void onError ( Throwable e ) {
mMvpView . showProgressIndicator ( false ) ;
mMvpView . showError ( ) ;
}
@ Override
public void onNext ( List postsList ) {
mMvpView . showPosts ( postsList ) ;
}
} ) ;
}
|
mMvpView是與Presenter一起協助的View組件。通常情況下是一個Activity,Fragment或者ViewGroup的實例。
像之前的架構,View Layer持有標準的Framework組件,如ViewGroup,Fragment或者Activity。最主要的不同在于這些組件不再直接訂閱Observable。取而代之的是通過實現MvpView接口,然后提供一些列簡潔的方法函數,比如showError( )或者showProgressIndicator( )。這個View組件也負責處理用戶交互,如點擊事件和調用相應Presenter中的正確方法。例如,我有一個按鈕用來加載博客列表,Activity將會在點擊事件的監聽中調用presenter.loadTodayPosts( )
如果你想看到一個完整的運用MVP基本架構的工作示例,可以從Github檢出我們的Android Boilerplate project。也可以從這里閱讀關于它的更多信息Ribot的架構指導
為什么這種架構更好?
- Activity和Fragment變得非常輕量。他們唯一的職責就是建立/更新UI和處理用戶事件。因此,他們變得更容易維護。
- 現在我們通過模擬View Layer可以很容易的編寫出單元測試。之前這些代碼是View Layer的一部分,所以我們很難對它進行單元測試。整個架構變得測試友好。
- 如果DataManager變得臃腫,我們可以通過轉移一些代碼到Presenter來緩解這個問題。
我們依然存在哪些問題?
- 當代碼庫變得非常龐大和復雜時,單一的DataManager依然是一個問題。雖然我們還沒有走到這一步,但這是一個真正值得注意的問題,我們已經意識到了這一點,它可能發生。
值得一提的是它并不是一個完美的架構。事實上,不要天真的認為這是一個獨特且完美的方案,能夠解決你所有的問題。Android生態系統將保持快速發展的步伐,我們必須繼續探索。不斷地閱讀和嘗試,這樣我們才能找到更好的方法來繼續構建優秀的Android應用程序。
最后多實踐,發現秘密。
adiOS