天天看點

《Android應用開發》——2.2節活動類

本節書摘來自異步社群《android應用開發》一書中的第2章,第2.2節活動類,作者 【美】chris haseman,更多章節内容可以通路雲栖社群“異步社群”公衆号檢視

2.2 活動類

android應用開發

在一個典型的android應用中,活動是操作的骨幹。從本質上說,它們的目的是控制螢幕上的顯示内容。它們消除了想要顯示的資料與實作資料顯示的界面布局檔案、類之間的鴻溝。如果熟悉流行的“模型—視圖—控制器”(mvc)架構,活動就是一個螢幕的控制器。清單檔案中的活動聲明如下所示:

android:name标記告訴系統要到程式包末尾的什麼位置(從清單聲明中)去尋找類定義。例如,在com.haseman.peachpit. myactivity上的樣例項目中,類裝載器會期望找到一個活動類的擴充類。

為了能被找到,檔案必須位于src/com/haseman/peachpit目錄下。對于android所使用的語言,這是标準操作流程。

2.2.1 看着活動類發揮作用

活動如果正确運用的話是一個對象,專門控制一個單獨的螢幕。下面把現實世界裡的rss新聞讀者作為案例研究,來談談這個虛構的活動,這樣可以快速解釋理論上常常忽略的問題。典型做法是使用一個活動列出一個使用者訂閱的所有推送新聞。使用者單擊一個推送時,開發人員就使用第二個活動來顯示那個特定新聞推送的可用文章清單。最後,當使用者單擊一個特定文章時,開發人員用第三個活動來顯示文章的詳細内容。

很容易看出活動是如何擔當一個角色的(訂閱清單,文章清單,文章内容)。同時,活動是一般性的,表現在文章清單應該能夠顯示來自任意rss推送的文章,而文章内容活動應該能顯示通過一個rss讀者所找到的任意文章的内容。

2.2.2 實作自己的活動類

大多數情況下,了解某種事物的最好方法就是使用它。記住這一點,下面把一個新的活動添加到第1章建立的項目中。這可以解釋活動是如何工作的、它的生命周期,以及和它一同工作時需要了解什麼。以下是需要遵循的一般步驟。

(1)在聲明中為新的活動添加一個條目。

(2)建立一個新類,擴充活動類。

(3)建立一個新檔案,包含這個新活動的xml布局說明,并添加一個新的字元串格式文字,讓布局來顯示(别擔心,盡管這聽起來很難,實際操作起來卻簡單得多)。

(4)當所有檔案都準備好的時候,就需要從現有的活動中真正啟動這個新活動。

1.最基本的活動

一個活動最簡單的形式就是一個擴充了活動類的對象。它應該(但不必須)實作oncreate方法。建立一個新的項目時,活動預設的定義如下:

在這段代碼中,裝置在活動啟動的時候調用oncreate方法。oncreate方法告知使用者界面系統setcontentview方法指定這個活動的主布局檔案。每個活動都可以有一個而且最多一個内容視圖,是以一旦設定就不能更改。android sdk就是這樣保證為每個螢幕使用一個新的活動,因為每次想修改根内容視圖的時候,都需要一個不同的活動。

2.通知android系統有使用者友好的新活動

應該裝載和啟動新活動的時候,android系統需要知道到哪去找它。

(1)在eclipse中打開androidmanifest.xml檔案。

(2)在标記内,就在前一個活動聲明的結束标記之後添加下列代碼:

這一短行代碼告訴系統新的活動位于應用程式包内的什麼位置。以本書的範例為例,類裝載器知道在com.haseman.peachpit. newactivity能找到新的活動。

接下去,需要把一個檔案放在那裡讓它找到。

3.建立新活動類

建立一個新活動有幾種方法,在eclipse中建立的最簡單方法如下。

(1)右鍵單擊(或單擊control鍵)選中的程式包名稱(這裡是com.haseman.peachpit)。

(2)選擇菜單new->class。

(3)在對話框中輸入新的類名字。

一個名字就可以建立一個新的檔案。檔案會以指定的名字儲存在主程式包内。在這裡的範例程式中,檔案位于src/com/ haseman/peachpit/newactivity.java目錄下的項目目錄中。

既然已經由一個對象擴充得到一個類,下面需要把它轉換一下,擴充一個活動。

(4)在代碼中完成以下粗體部分所示的修改:

注意,這段代碼與現有活動中的代碼非常類似。接下去要使它有所不同。

(5)在res/values/strings.xml檔案中,在已有的字元串下添加以下标記中粗體顯示的代碼行。

這些代碼告訴android系統,需要一個新的字元串,名字是new_activity_text,可以通過android的資料總管來通路它。

在後面的章節中會對/values檔案夾的内容有更多介紹。接下來需要為新的活動建立一個布局檔案。

4.建立一個新的螢幕布局

建立一個新布局的過程如下。

(1)在res/layout/目錄下建立一個名為new_activity.xml的新檔案。它應該位于已有的main.xml檔案(現有的活動正在使用它)旁邊。這個new_activity.xml檔案看起來應該與main.xml一樣,除了需要添加一個指針,指向剛剛建立的字元串。

(2)插入下面代碼中粗體處理的代碼行,建立一個指針,指向剛剛建立的字元串。

(3)給修改後的textview賦予一個id,這樣使用者的java代碼就可以引用它(後文會介紹關于textview的更多内容,現在需要知道的是,textview是在螢幕上顯示文本的android視圖)。代碼如下:

第3章會專門介紹資源管理和使用者界面設計,目前隻需要記住,字首@是告訴android系統,想把在别處定義的一個id、字元串或drawable類作為一個資源來引用。

既然有了一個新的布局,帶有全新的字元串,就需要告訴newactivity類,讓它使用這個特殊的布局檔案。

(4)把下面粗體處理的代碼行加入到newactivity類的oncreate方法中。

setcontentview方法是告訴android系統新活動會顯示哪個xml檔案。既然已經建立了一個新的類、字元串和布局檔案,下面該啟動這個活動,在螢幕上顯示新的視圖。

5.捕捉鍵盤操作

啟動新活動的一個簡單方法是使用者按他(她)手機上的center鍵。如果手機上沒有center鍵,也可以輕松修改下列代碼,接收想按的任意鍵。為了檢測按鍵事件,需要擴充初始活動類的onkeydown方法。記住這是一個簡單的範例。在使用者按一個鍵的時候啟動一個新的活動,這可能在實踐中并不是很常見的例子,但它構成了一個簡單的好例子。大多數新的活動是在使用者選擇一個清單項、單擊一個按鈕或者在螢幕上進行另一種操作時啟動的。

新版本的onkeydown應該如下所示:

聲明onkeydown是要重載預設的鍵盤事件處理方法, 采取新活動專有的動作。如果傳入的按鍵事件不是需要活動自己來處理的事件,就把它傳給父類版本的方法,這一向是好的做法。

注意,當keycode與center鍵比對時,傳回true。這是告訴android系統的活動和視圖,這個按鍵事件已經被正确處理,不必再傳給别人,否則就讓活動的父類來處理這個按鍵事件。現在看起來也許關系不大,但當android的活動變得越來越複雜的時候,這一點就會重要得多。

6.啟動活動

終于到了啟動新活動的時刻。這裡會簡單涉及到意圖。每個新活動被啟動,都是作為一個新的意圖被分派到系統(擁有這個意圖并進行适當的操作)内的結果。為了啟動第一個活動,需要指向應用環境的一個指針和代表新活動的類對象。下面先建立新的意圖。

(1)把下列代碼加入到onkeydown鍵盤處理方法中。

這是給新的意圖傳送一個應用環境和想要啟動的活動的類對象。這告訴android系統到底到應用程式包的什麼地方去找。有許多方法可以建立意圖并和它互動,但這裡介紹的是啟動一個新活動的最簡單方法,一旦正确建構意圖後,接下來就隻需要告訴android系統想要啟動這個新活動。

(2)把下列代碼加入到鍵盤處理方法中。

注意:

在整個過程中, 初始活動一次也未曾通路過新活動的執行個體。在兩個活動之間可能傳遞的任何資訊都必須經過中間的意圖。後面的2.6節會介紹如何實作這一點。

7.嘗試

如果eclipse在運作中,并且一直跟随本書所述進行編碼,那麼現在要做的事情就很簡單,啟動模拟器并安裝新的活動(應該記得第1章中介紹的步驟)即可。裝載了新的應用之後,按center鍵,檢視之前所有工作的成果(見圖2.1)。

《Android應用開發》——2.2節活動類

既然已經知道如何建立和啟動新活動,下面該讨論這個過程如何作用。要了解每當一個活動顯示到螢幕上或從螢幕上消失都是調用什麼方法完成的,這是考慮到此後的使用者界面布局和資料管理/資料儲存。

擷取意圖

意圖可以有無數種形式。

每次需要啟動一個活動或服務的時候都用到它們。而且會經常用意圖實作系統範圍内的通信。例如,注冊一個廣泛釋出的意圖,就可以接收關于動力系統變化的通知。如果一個活動注冊了一個聲明中的意圖(例如com.haseman.peachpit.ohohpickme),那麼手機上任何位置的任何應用都可以通過如下調用來直接啟動活動,如果這個活動是公共的話。

2.2.3 活動的生命和重要時刻

我們知道,每個活動都有一個非常短暫但輝煌的生命。活動注冊時定義為要接收一個意圖,當這個意圖被廣播給系統的時候,就開啟了活動的生命周期。系統調用活動的構造函數(必要時也會啟動應用),然後以下列順序依次調用活動的各個方法:

實作一個活動之後,任務就是擴充構成這個生命周期的方法。唯一一個需要擴充的方法是oncreate。其他方法如果已經聲明,就會在生命周期内按順序調用。

活動是頂層可見的應用,它可以拖到螢幕上,接收關鍵事件,它通常是一切的中心。當使用者在活動上按back鍵時,相關的方法以下列順序被調用:

活動短暫、殘酷的一生

活動的生命極其短暫。它們被不斷建立和消滅,是以非常重要的一點是,在活動中不要儲存與該活動所控制的螢幕之外相關的資料。

會導緻活動毀滅的所有有效操作如下。

(1)使用者将手機螢幕從豎向轉到橫向或者反之。

(2)該活動在螢幕上不再可見,或系統資源短缺。

(3)當天是周二。

(4)使用者按了back或home鍵,離開了應用。

再次確定活動的資料成員隻與螢幕上的因素相關。而且,不要期望活動儲存任何資料。後面的章節會介紹處理這種情況的政策。

執行了這些方法之後,活動被關閉,并準備垃圾收集。

為了了解活動在生命周期内的流轉過程,下面快速浏覽一下生命周期内各方法的細節。記住,各方法中都必須調用父類的方法(通常是在什麼都還沒做的時候),否則android系統會抛出異常。

oncreate是應用的生命周期内唯一一個必須使用者來實作的方法。筆者在大部分android開發工作中發現,最後隻能實作這些方法中的一兩個,這取決于活動具體完成什麼工作。

1.public void oncreate(bundle icicle)

android系統會在活動啟動的時候調用這個方法的聲明。但是要記住,在應用運作期間,可能經過每個活動的好幾個執行個體。例如,如果使用者将螢幕的方向從橫向變為豎向,活動就會被銷毀,然後建立同一個活動的一個新執行個體并對其初始化。

例如,如果活動的标題是動态的,但在活動啟動後就不會改變,那麼這個方法會在視圖層次中想到達的地方設定标題。這個方法不是用來配置資料的,當應用位于背景或另一個活動裝載到它之上的時候,資料可能會改變。

而且,如果應用運作在背景而系統在資源短缺的情況下運作,應用就可能被殺死。如果這樣,當應用從背景傳回的時候,就會在同一活動的一個新執行個體上調用oncreate方法。

oncreate方法也是唯一為活動調用setcontentview的機會。正如前文所見,這是告訴系統希望這個螢幕用怎樣的布局。一旦可以設定使用者界面上的資料,就調用setcontentview。它可以包含任何設定,從設定清單的内容到設定textview或imageview。

2.public void onstart()

活動啟動時,在oncreate方法之後立即調用onstart方法。如果應用位于背景(另一個應用裝載在該應用之上,或者使用者按了home鍵),當應用繼續後但在活動與螢幕互動之前會調用onstart方法。一般應避免重載onstart方法,除非當應用開始使用螢幕之前需要特别檢視什麼内容。

3.public void onresume()

當活動有通路螢幕的許可時,onresume是活動生命周期中最後調用的方法。如果當活動在背景時使用者界面的元素改變了,這個方法就用來確定使用者界面和手機狀态同步。活動啟動時,在oncreate和onstart之後調用這個方法。當活動重新傳回前台時,不管它之前處于什麼狀态,都會調用onresume方法。

4.太好了,活動現在在運作了

完成這些設定、配置工作之後,活動現在對使用者可見了。按鈕可以單擊, 資料可以解析和顯示,清單可以滾動,一切都在進行之中。但是,在某個時間點上(也許是因為使用者按了back鍵),這一切必須結束,需要讓事情平息下來。

5.onpause( )

onpause是應用離開螢幕時系統調用的第一個方法。如果哪個程序或循環(例如動畫)隻有當活動在螢幕上顯示時才能運作,那麼onpause方法是阻止它們的完美場所。如果在目前顯示的活動之上裝載了另一個活動,就會調用onpause方法。

記住,如果系統需要資源,那麼在調用onpause方法之後随時可能殺死你的程序。這種情況并不常見,但需要當心它有可能發生。

onpause方法很重要,因為它可能是對于活動(甚至是整個應用棧)正要離開的唯一警告。正是應該在這個方法中把重要資訊儲存到磁盤、資料庫或者偏好的其他地方。

活動真正離開螢幕之後,會收到在它生命周期内的下一個調用。

6.onstop( )

android系統調用onstop方法時,表明活動已經正式離開螢幕。而且,到使用者離開目前活動、與另一個活動互動時才調用onstop方法。這并不一定意味着目前活動被關閉(盡管有這個可能),隻是假定使用者離開目前活動而轉向另一個。如果目前活動内部有正在執行的程序,隻有在其活躍狀态下才能運作,那就應該做一個好公民,利用這個方法把活動關閉。

7.ondestroy( )

ondestroy是在活動消失之前調用的最後一個方法。這是讓活動清理其事務的最後機會,然後就會傳遞給大型垃圾收集程式。

對于有可能一直被活動置于背景運作的任何背景程序(例如擷取/解析資料),必須在這個方法調用中關閉。

但是,調用了ondestroy并不意味着活動會被銷毀。是以,如果一個線程在運作,那麼甚至在調用了ondestroy方法之後,它也可以繼續運作并占用系統資源。

2.2.4 加分題——資料儲存方法

正如之前所述,在調用onpause方法之後,如果系統需要資源,程序随時可能被殺死。但是,使用者不會知道這種行為。為了實作這一點,android系統提供了兩個選擇,可以儲存狀态資料以備用。

1.onsaveinstancestate(bundle outstate)

本方法傳遞給使用者一個bundle對象,可以把需要的任何資料放入其中,進而在此後的某個時刻把活動恢複到目前狀态。調用類似于outstate.putstring或outstate.putboolean可以實作狀态的恢複。存儲的每個值在存入時需要一個字元串鍵值,取出時需要同樣的鍵值。要重載自己的onsaveinstancestate方法。如果已經聲明,系統就會調用它,否則就錯過了機會。

要恢複此前已殺死的活動時,系統會再次調用oncreate方法,把用onsaveinstancestate建構的bundle對象交還給你。

隻有在系統認為此後可能會需要恢複活動的情況下才會調用onsaveinstancestate。例如,當手機顯然不需要日後繼續這個活動時,如果使用者按了back鍵,就不會調用它。同樣,這個方法不用來儲存使用者資料,隻存放在這個特别的螢幕執行個體中對使用者界面來說重要的臨時資訊。

2.onretainnonconfigurationinstance( )

當使用者在豎向和橫向兩種螢幕模式之間切換時,活動被銷毀,而建立它的一個新執行個體(經曆完全的關閉—啟動周期的方法調用)。銷毀和建立活動時,尤其是因為配置的變化(最常見的原因是手機旋轉)而引發時,onretainnonconfigurationinstance有機會傳回可以在新的活動執行個體中通過調用getlastnonconfigurationinstance來回收的任何對象。

這個政策有助于讓螢幕旋轉變換得更快。記住活動擷取計劃顯示到螢幕上的資料時是否花費了相當多的時間。而調用getlastnon configurationinstance可以獲得此前顯示的資料。

要保持簡單智能

現在知道,活動有可能在無意中被殺死。onsaveinstancestate讓使用者有機會儲存原語為日後之用。這明确說明整個活動必須能夠把所有重要資訊分解成一系列的原語。這進一步強化了下面的概念,即活動必須很簡單,不能包含對外部應用很重要的複雜資料。要避免讓活動中的大規模java垃圾收集器一直裝滿資料,因為它也許會在無意中被終止。

現在應該已經基本了解以下幾個方面:

建立一個新活動的步驟;

如何啟動活動;

活動的生命周期。

現在有了足夠的基礎知識,在後面章節中介紹更複雜的主題時可以跟得上。别擔心,下面很快就會回到活動這個主題。

繼續閱讀