內容摘要
本節開始講解Android開發的技術細節,我們首先講解界面展示部分,這主要是為了滿足產品經理做原型的需要,我相信不管多么強大的產品設計軟件,都沒法把你的想法落實到手機上執行,并且能夠控制界面的按鈕和跳轉邏輯。
但是如果掌握Android開發中的兩大控件, 足以滿足你初期的產品原型開發,把界面邏輯和原型制作成一個簡易APP,展示效果會非常不一樣。
Android控件--Activity
Android控件-- Service
Activity
首先來感性認識一下Activity。
這是我手機上《今日頭條》的兩個界面,這兩個界面就是通過Activity展示的,在Activity里面你可以添加圖片,圖片的組件叫做ImageView,所有內容羅列在一個表單中,表單的組件叫做ListView。這些叫做View的組件可以互相嵌套布局,他們最后都展示在Activity的布局中。
這些組件名稱都是Android環境下的名稱,在IOS環境中也有相應的組件,只是名稱有所不同,用法也稍有差異,比如列表在IOS被稱作TableView,而用法上,Android利用XML的布局文件說明ListView的樣式或者直接用代碼實現ListView的樣式,而IOS則用StoryBoard直接編輯布局或者用代碼實現,
最新的Android Studio雖說也可以直接圖形化編輯頁面布局,但是現在的階段還不夠成熟,建議首先使用XML文件編輯布局,或許再過幾個版本的迭代,Android Studio也可以像IOS的Xcode那樣直接圖形化編輯頁面布局。
實際上這2個界面還用到了另一個比較有名的組件: Fragments。這個組件其實不是所有的Android版本都支持,這是在Android 3.0之后引入的, Fragments被用作多個組件的組織單元,是各種小組件的一個集合管理對象。
Fragments相當于輕量級的Activity,他內嵌在Activity的布局中,管理具有相同業務邏輯意義的組件合集。 Fragments與Activity有很多相似的地方。
我們在講解完Activity之后會再講解 Fragments,之所以沒有單獨把它單獨列出來講,是因為這2個控件幾乎有相同的用法,我們只關注他們的不同點。
Tasks and Back Stack
Activity需要重點講解的一個知識點是Tasks and Back Stack。所有的APP是由一個或者多個Activity構成的,當你在某個頁面(Activity A)上點擊某個按鈕并跳轉到下一個頁面(Activity B)的時候,系統需要保持A的狀態,這樣當我們按回退按鈕的時候,以便能夠再次顯示A的內容和數據。這個過程中系統要把A的信息和狀態保持到一個數據結構中,這個結構就叫做Back Stack 而為了完成某個功能,Activity A和Activity B這樣一個組合就叫做一個Task。
上圖描述了一個Task過程中Back Stack管理Activity的。接下來我們舉個例子:
當我們點擊 ”今日頭條”-->“我的”-->“商城”按鈕的時候,會彈出一個新的界面,此時“我的”就是示意圖中Activity1,而彈出的“商城”界面就是Activity2,當我再次點擊“淘寶特賣”的時候,會彈出“特賣頻道”界面,這個界面就是上圖中的Activity3,當我按“回退” 按鈕的時候,Activity3就會從Back Stack中彈出并且銷毀,Activity2就會處于激活狀態,同理再次按下“回退”按鈕,Activity2就會銷毀而Activity1就會處在或者月狀態。
因此Task就是在打開一個app并且點擊各個界面時候,系統為了管理這一些列操作和狀態而創建的管理對象。而Back Stack是為了管理各個節目的數據結構。實際上,一般情況下一個Task就是一個linux進程,而Back Stack就是這個進程管理APP用的數據結構。
Activity Lifecycle
Activity需要重點講解的第二知識點就是 Lifecycle。我們講過Activity3在用戶按下“回退”鍵的時候,會銷毀并且Activity2處于激活狀態。這個過程中Activity3是被系統釋放了資源的。Android有一套資源管理方式,為了在有限的手機內存上展示盡可能豐富的內容,Android會為每一個正在展示的界面創建多個狀態,并為不同的狀態給予不同的資源權限。
首先我首先看一下Activity的狀態圖,圖片來自官方網站,希望大家養成閱讀原版官方文檔的資料,本書大部分內容是在官方文檔基礎上,進行的總結和解讀。
總的來看Activity流程稍微有點復雜,但是如果結合上面的例子,你會對這些狀態有清晰的認識,首先是“Created”,當你點擊“商城”按鈕之后會彈出“商城”界面,在這個界面顯示之前,你需要首先解析Activity的布局文件,以便知道界面上有哪些元素需要顯示,以及他們的位置,我們例子中,一個列表需要顯示,列表中有三項內容:“今日特賣”,“今日電影”,“淘寶特賣”。這個解析布局文件并設置顯示內容的過程就是”Create“的過程,結束之后Activity就處于“Created”。而這個過程是在onCreated()函數中執行的。
創建結束之后就要顯示這個界面了,完成顯示之后Activity就處于“Started”狀態了,在創建之后,顯示之前,如果你還要做些什么事情,那就調用onStart()函數。
界面顯示之后,你還有機會做一些事情,這個動作可以在onResume()中進行,執行完成這個函數之后,Activity就會處于“Resumed”狀態了。
接下來2個狀態是“Paused”和“Stopped”,這兩個狀態一般情況下是連續同時出現的,如果從當前界面點擊了一個按鈕,按鈕的功能是彈出一個新的Activity,那么當前的Actvity就會首先處于“Paused”的狀態,進入此狀態前會首先調用onPause()方法,然后再調用onStop()方法,進入Stopped的狀態,而新的Activity要重新走一遍Created到Resumed的流程。
在這個過程中,如果點擊按鈕之后不是顯示一個新的Activity,而是彈出一個確認對話框,那么當前的Activity只會進入Paused的狀態,而不會進入Stopped,并且只有onPause()方法會被執行到。因為此時當前的Activity只是部分可見,而不是完全被覆蓋掉。這個地方是容易被開發者混淆的知識點。
當確認按鈕被點擊并重新返回當前Activity之后,Activity又重新進入了Resumed狀態。此時onResume()函數會被執行到。
如果當前的Activity不是被新的Activity覆蓋掉,而是用戶點擊“回退”按鈕,此時的Activity就會從Back Stack中彈出,然后它使用的資源會被釋放掉,進入“Destroyed”狀態,再此之前會先后進入Paused和Stopped狀態,在進入Stopped狀態之后,Destroy之前,系統會調用onDestroy()方法,你可以在此方法中做一些存儲數據的動作,以便下次再顯示次界面時使用。
在IOS中,也有類似的狀態變遷,只不過狀態發生變遷時調用的方法名稱不相同而已,比如下列四個方法:
- (void)viewWillAppear:(BOOL)animated;? ? --界面將要顯示
- (void)viewDidAppear:(BOOL)animated;? --界面已經顯示
- (void)viewWillDisappear:(BOOL)animated;--界面將要消失
(void)viewDidDisappear:(BOOL)animated;? --界面已經消失
以上四個函數只是狀態變遷時,系統可以調用的函數的一部分,用戶可以在界面狀態將要或者已經發生變化時,做相應的邏輯處理。
不同點是Android在系統內存不夠的情況下,會釋放掉處于Stopped狀態的Activity以騰出資源給當前正在顯示的界面,而IOS則是先發送告警到用戶的回調函數,由用戶來處理這個問題。IOS處理內存告警的方法是:
-(void)didReceiveMemoryWarning;
Activity Layout
Layout 是需要重點說明的第三點。在Activity的onCreate()方法中,我們提到需要加載界面的布局文件,然后設置他們的屬性。每個Activity都需要一個XML文件來描述起界面各個元素的位置和屬性。這就是設計模式中的MVC。Activity就是MVC中的C即控制層,而XML描述文件就是V,即展示層,而M數據層我們會在后面的章節中講解。在下一節的講解中,我們會詳細講解布局文件。
Fragment
Activity就講解這些,需要額外說明的是與Activity非常類似的一個控件: Fragment。我只講解一下 Fragment與Activity的不同之處,相同地方不再贅述。
首先要說明的是 Fragment與Activity的關系就像操作系統中線程與進程的區別,如果你了解這方面的知識,那么這兩大控件的區別就非常容易理解了。如果你熟悉面向對象編程,那么Fragment與Activity的關系就像子類與父類之間的關系,這個比喻不是很恰當,但是可以暫時這么理解。
再就是狀態Fragment要比Activity多,但是它的狀態依賴于Activity的狀態,也就是說不存在Fragment處于Resumed而它所屬的Activity卻處于Stopped的這情況。具體的狀態和狀體依賴關系見圖。這里不再詳細解釋,相信如果你看懂Activity的狀態變遷,就能很好的理解Fragment的狀態圖。
其次是Fragment的布局文件可以是Activity的一部分,即Activity的xml文件中,有一個控件叫做Fragment。Fragment設計之初就是為了將復雜的Activity文件中的控件,分組進行管理和展示,這個分組就是Fragment。
Fragment還有一些其它特殊屬性,不過最重要的特點就是,Fragment是為了在同一個Activity中展示復雜的界面并且節省了Activity在start和destroy的時候所消耗的CPU資源。
Service
我們同樣首先對Service有個感性的認識,見下圖:
通過手機 設置->應用程序管理->運行中,你可以看到很多在后臺運行進程和服務,點擊其中一個,你會進入一個更詳細的界面,注意“NotifyService”這個服務,如果沒有猜錯的話,他是用來發送“PUSH”消息的,就是當我們的APP進程被殺死之后,依然能夠收到該APP的推送提醒,比如有新的消息或者有新的內容可以查看。點擊微信的圖標,試著停止里面的服務,當有朋友發送消息給你的時候,看看有什么不同。
關于Service,我們主要記住的就是它的作用,當我們的APP沒有處于激活狀態的時候,我們需要APP與我們的業務服務器進行一些交互功能,此時需要一個不同于Activity的控件來完成此功能,Service就是為此而設計的。
關于Service我們需要講解幾個要點。
Service有2種類型,Started和Bound。
這2種類型的的Service從創建到銷毀以及用途都有所不同,對于Started類型的Service,如果要啟動一個Service對象來執行特定的任務,需要通過 startService()調用,當任務執行完成之后,你需要用代碼來明確的停止它,并且它啟動之后就不在與啟動它的控件(Activity)交互。
對于Bound類型的Service,你需要通過調用 bindService()來啟動它,并且需要定義一個接口,來說明Service是如何與其他組件進行信息交互的。多個控件可以同時綁定同一個Service,如果一個Service沒有控件綁定,類似指針引用計數為0,則系統會停止這個Service并釋放其使用的資源。
Service是在當前APP的main thread中運行的。
Service是在當前綁定的它的進程的主線程中運行的。相信你的Android手機曾經提醒過你,某個APP長時間沒有響應,請問是否關閉該APP。這個就是因為主線程太長時間沒有響應用戶的UI操作。因此如果Service執行太繁重的任務(一般情況下都會這樣),那么我們必須單獨創建一個線程來執行這個任務,而不要在主線程中執行這個繁重的任務。
有2個類可以用來實現Service控件:Service和IntentService
Service和IntentService是Android里面的兩個Service控件的實現類,他們的不同是IntentService會自動創建一個新的線程,所有的業務邏輯只要在onHandleIntent()這個函數實現就可以了,這個函數自動在主線程之外的線程中執行,避免了上面所說的APP無響應的問題。同時系統會自動實現一個隊列,所有對IntentService的多線程調用都是先把調用請求放入隊列,然后依據隊列順序的調用onHandleIntent(),這就避免了多線程下調用IntentService可能產生的問題。
而Service類需要自己管理線程,不過它的優勢就是你可以根據自己的需要,創建足夠多得線程去處理每一個發送到Service請求,也就是說Service類有更大的靈活性。
Service也可以在前臺運行,比如音樂播放器的Service
有些Service是可以在前臺運行的,這些Service需要與用戶進行頻繁的交互,同時又需要與服務器后臺進行交互, 因此可以把這類Service設置為前臺Service,比較典型的就是播放音樂的Service即使在系統資源緊張的時候,也不會被系統回收資源。
Service的生命周期
Service的生命周期要比Activity的簡單很多,只是需要注意的是Service運行時間越久,當系統在資源緊張的情況下,被系統殺死并回收資源的概率越大。
當Service被系統殺死并回收資源之后,系統會在資源充足的情況下試圖恢復該Service,當然要看該Service在啟動時候的配置,這個配置分為3類:
START_NOT_STICKY:不需要重新啟動該Service。
START_STICKY:需要重建該Service,但是忽略之前通過Intent傳入的參數。
START_REDELIVER_INTENT:需要重新啟動該Service,并且重新輸入之前傳入的參數。
這3種類型是Service在創建的時候,調用onStartCommand() 時的返回值。
本部分內容我們講解了Service的幾個重要屬性,需要注意的是除此之外,Service的另一個重要的屬性是,它可以被其它APP調用,我們可以在配置文件中配置一下改Service的屬性,將該Service設置為僅為本APP可以調用。
本節主要從理論和經驗方面,講解了Android主要控件的主要特性,本書限于設計的知識面過于廣泛,無法從所有的細節講解所有的技術,希望大家通過本書的講解,對于技術細節的學習有所有幫助,作為一個提綱挈領的工具書來使用,在下一節中我們將講解本書工程中用到的技術的細節,同時我會把代碼上傳到代碼庫中,希望大家一邊對照代碼一邊對照本書,進行仔細的學習。
歡迎關注公眾號