第2章第2節Android主要控件

內容摘要

本節開始講解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

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主要控件的主要特性,本書限于設計的知識面過于廣泛,無法從所有的細節講解所有的技術,希望大家通過本書的講解,對于技術細節的學習有所有幫助,作為一個提綱挈領的工具書來使用,在下一節中我們將講解本書工程中用到的技術的細節,同時我會把代碼上傳到代碼庫中,希望大家一邊對照代碼一邊對照本書,進行仔細的學習。

歡迎關注公眾號

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,963評論 6 542
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,348評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,083評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,706評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,442評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,802評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,795評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,983評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,542評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,287評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,486評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,030評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,710評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,116評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,412評論 1 294
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,224評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,462評論 2 378

推薦閱讀更多精彩內容