官术网_书友最值得收藏!

第4章 Android生命周期

經(jīng)過(guò)上一章的學(xué)習(xí),主要了解了Android應(yīng)用程序設(shè)計(jì)的基礎(chǔ)知識(shí),對(duì)Android程序的開發(fā)有了一定的了解。在此基礎(chǔ)上,本章將對(duì)Android系統(tǒng)的進(jìn)程優(yōu)先級(jí)的變化方式、Android系統(tǒng)的4大基本組件、Activity的生命周期中各個(gè)狀態(tài)的變化關(guān)系、Android應(yīng)用程序的調(diào)試方法和工具進(jìn)行學(xué)習(xí)。

4.1 程序生命周期

所謂的應(yīng)用程序生命周期就是應(yīng)用程序進(jìn)程從創(chuàng)建到消亡的整個(gè)過(guò)程。在Android中,多數(shù)情況下每個(gè)程序都是在各自獨(dú)立的Linux進(jìn)程中運(yùn)行的。當(dāng)一個(gè)程序或其某些部分被請(qǐng)求時(shí),它的進(jìn)程就“出生”了;當(dāng)這個(gè)程序沒(méi)有必要再運(yùn)行下去且系統(tǒng)需要回收這個(gè)進(jìn)程的內(nèi)存用于其他程序時(shí),這個(gè)進(jìn)程就“死亡”了??梢钥闯?,Android程序的生命周期是由系統(tǒng)控制而非程序自身直接控制。這和編寫桌面應(yīng)用程序時(shí)的思維有一些不同,一個(gè)桌面應(yīng)用程序的進(jìn)程也是在其他進(jìn)程或用戶請(qǐng)求時(shí)被創(chuàng)建,但是往往是在程序自身收到關(guān)閉請(qǐng)求后執(zhí)行一個(gè)特定的動(dòng)作(如從main方法中return)而導(dǎo)致進(jìn)程結(jié)束的。

簡(jiǎn)而言之,程序的生命周期是在Android系統(tǒng)中進(jìn)程從啟動(dòng)到終止的所有階段,也就是Android程序啟動(dòng)到停止的全過(guò)程,程序的生命周期是由Android系統(tǒng)進(jìn)行調(diào)度和控制的。

但是,一個(gè)不容忽視的問(wèn)題就是,手機(jī)的內(nèi)存是有限的,隨著打開的應(yīng)用程序數(shù)量的增多,隨之而來(lái)的可能會(huì)是應(yīng)用程序響應(yīng)時(shí)間過(guò)長(zhǎng)或者系統(tǒng)假死的糟糕情況。所以,若將Android應(yīng)用程序生命周期交由系統(tǒng)處理的話,那么在系統(tǒng)內(nèi)存不足的情況下,便由Android系統(tǒng)舍車保帥,選擇性地來(lái)終止一些重要性較次的應(yīng)用程序,以便回收內(nèi)存供更重要的應(yīng)用程序使用。

圖4-1 Android系統(tǒng)進(jìn)程優(yōu)先級(jí)

那么,系統(tǒng)是根據(jù)一個(gè)什么樣的重要性標(biāo)準(zhǔn)來(lái)終止Android應(yīng)用程序的呢?

Android根據(jù)應(yīng)用程序的組件及組件當(dāng)前運(yùn)行狀態(tài)將所有的進(jìn)程按重要性程度從高到低劃分了五個(gè)優(yōu)先級(jí):前臺(tái)進(jìn)程、可見進(jìn)程、服務(wù)進(jìn)程、后臺(tái)進(jìn)程、空進(jìn)程。

以下就按優(yōu)先級(jí)由高到低的順序介紹Android系統(tǒng)中的進(jìn)程。

1.前臺(tái)進(jìn)程

前臺(tái)進(jìn)程是顯示在屏幕最前端并與用戶正在交互的進(jìn)程,是Android系統(tǒng)中最重要的進(jìn)程,包含以下4種情況。

? 進(jìn)程中的Activity正在與用戶進(jìn)行交互。

? 進(jìn)程服務(wù)被Activity調(diào)用,而且這個(gè)Activity正在與用戶進(jìn)行交互。

? 進(jìn)程服務(wù)正在執(zhí)行聲明周期中的回調(diào)方法,如onCreate()、onStart()或onDestroy()

? 進(jìn)程的BroadcastReceiver正在執(zhí)行onReceive()方法。

Android系統(tǒng)在多個(gè)前臺(tái)進(jìn)程同時(shí)運(yùn)行時(shí),可能會(huì)出現(xiàn)資源不足的情況,此時(shí)會(huì)清除部分前臺(tái)進(jìn)程,保證主要的用戶界面能夠及時(shí)響應(yīng)。

2.可見進(jìn)程

可見進(jìn)程指部分程序界面能夠被用戶看見,卻不在前臺(tái)與用戶交互,不響應(yīng)界面事件(其onPause()方法已被調(diào)用)的進(jìn)程。如果一個(gè)進(jìn)程包含服務(wù),且這個(gè)服務(wù)正在被用戶可見的Activity調(diào)用,此進(jìn)程同樣被視為可見進(jìn)程。

Android系統(tǒng)一般存在少量的可見進(jìn)程,只有在特殊的情況下,Android系統(tǒng)才會(huì)為保證前臺(tái)進(jìn)程的資源而清除可見進(jìn)程。

3.服務(wù)進(jìn)程

服務(wù)進(jìn)程是指包含由startService()方法啟動(dòng)服務(wù)的進(jìn)程。它有以下特性:沒(méi)有用戶界面;在后臺(tái)長(zhǎng)期運(yùn)行。例如,后臺(tái)MP3播放器或后臺(tái)上傳下載數(shù)據(jù)的網(wǎng)絡(luò)服務(wù)。

Android系統(tǒng)除非不能保證前臺(tái)進(jìn)程或可見進(jìn)程所必要的資源,否則不強(qiáng)行清除服務(wù)進(jìn)程。

4.后臺(tái)進(jìn)程

后臺(tái)進(jìn)程是指不包含任何已經(jīng)啟動(dòng)的服務(wù),而且沒(méi)有任何用戶可見的Activity的進(jìn)程。這些進(jìn)程不直接影響用戶的體驗(yàn)。

Android系統(tǒng)中一般存在數(shù)量較多的后臺(tái)進(jìn)程,因此這些進(jìn)程會(huì)被保存在一個(gè)列表中,以保證在系統(tǒng)資源緊張時(shí),系統(tǒng)將優(yōu)先清除用戶較長(zhǎng)時(shí)間沒(méi)有見到的后臺(tái)進(jìn)程。

5.空進(jìn)程

空進(jìn)程是不包含任何活躍組件的進(jìn)程。一般保留這些進(jìn)程,是為了將其作為一個(gè)緩存,在它所屬的應(yīng)用組件下一次需要時(shí),縮短啟動(dòng)的時(shí)間。

空進(jìn)程在系統(tǒng)資源緊張時(shí)會(huì)被首先清除,但為了提高Android系統(tǒng)應(yīng)用程序的啟動(dòng)速度,Android系統(tǒng)會(huì)將空進(jìn)程保存在系統(tǒng)內(nèi)存中,在用戶重新啟動(dòng)該程序時(shí),空進(jìn)程會(huì)被重新使用。

問(wèn):除了以上的優(yōu)先級(jí)外,還有其他因素決定進(jìn)程的優(yōu)先級(jí)嗎?

? 進(jìn)程的優(yōu)先級(jí)取決于所有組件中的優(yōu)先級(jí)最高的部分。

? 進(jìn)程的優(yōu)先級(jí)會(huì)根據(jù)與其他進(jìn)程的依賴關(guān)系而變化。

4.2 Android組件

組件是可以調(diào)用的基本功能模塊。Android應(yīng)用程序就是由組件組成的,Android系統(tǒng)有4個(gè)重要的組件,分別是Activity、Service、BroadcaseReceiver和ContentProvider。

Activity是Android程序的呈現(xiàn)層,顯示可視化的用戶界面,并接收與用戶交互所產(chǎn)生的界面事件。在界面上的呈現(xiàn)形式就是全屏窗體、非全屏懸浮窗體的對(duì)話框,與在桌面系統(tǒng)上的獨(dú)立事業(yè),如辦公應(yīng)用等類似。Activities是可執(zhí)行的代碼塊,由用戶或者操作系統(tǒng)來(lái)進(jìn)行初始實(shí)例化,并在他們被需求時(shí)致以運(yùn)行。Activities可以與用戶、請(qǐng)求數(shù)據(jù)或者其他Activity、Service的服務(wù)通過(guò)query或Intent進(jìn)行交互。大部分為Android編寫的可執(zhí)行代碼將以Activity的形式執(zhí)行。對(duì)于一個(gè)Android應(yīng)用程序來(lái)說(shuō),可以包含一個(gè)或多個(gè)Activity,一般在程序啟動(dòng)后會(huì)呈現(xiàn)一個(gè)Activity,用于提示用戶程序已經(jīng)正常啟動(dòng)。當(dāng)它不積極運(yùn)行時(shí),Activity可以被操作系統(tǒng)終止以節(jié)省內(nèi)存。

Service用于沒(méi)有用戶界面,但需要長(zhǎng)時(shí)間在后臺(tái)運(yùn)行的應(yīng)用。它類似于桌面應(yīng)用或者服務(wù)器操作系統(tǒng)上的服務(wù)或守護(hù)進(jìn)程。Service是在后臺(tái)運(yùn)行的可執(zhí)行的代碼塊,從它被初始化一直運(yùn)行到該程序關(guān)閉。一個(gè)Service的典型的例子是一個(gè)MP3播放器,盡管用戶已經(jīng)使用其他應(yīng)用程序,但仍然需要持續(xù)播放文件。你的應(yīng)用程序可能需要在沒(méi)有用戶界面的情況下一直執(zhí)行Service來(lái)實(shí)現(xiàn)后臺(tái)任務(wù)。

Broadcast和Intent Receivers對(duì)從其他的應(yīng)用程序的服務(wù)請(qǐng)求做出一個(gè)全系統(tǒng)廣播的響應(yīng),這些廣播響應(yīng)可能來(lái)自于Android系統(tǒng)本身或者是任何在其系統(tǒng)上運(yùn)行的程序。BroadcaseReceiver是用來(lái)接受并響應(yīng)廣播消息的組件。它不包含任何用戶界面,但可以通過(guò)啟動(dòng)Activity或者Notification通知用戶接收到重要信息。

問(wèn):Notification如何提示用戶?

:閃動(dòng)背景燈、振動(dòng)設(shè)備、發(fā)出聲音或在狀態(tài)欄上放置一個(gè)持久的圖標(biāo)。

Activity或Service通過(guò)執(zhí)行一個(gè)IntentReceiver為其他應(yīng)用程序提供了訪問(wèn)其功能的功能。Intent Receiver是一段可執(zhí)行代碼塊,對(duì)其他Activity的數(shù)據(jù)或服務(wù)請(qǐng)求做出響應(yīng)。請(qǐng)求的Activity(客戶端)生成一個(gè)Intent,把其添加至Android Framework中,來(lái)指出哪些應(yīng)用程序(目標(biāo)程序)接收并對(duì)其做出響應(yīng)。Intent是Android的主要構(gòu)成元素之一,它從現(xiàn)有的應(yīng)用程序中創(chuàng)造新的應(yīng)用程序。Intent實(shí)現(xiàn)了應(yīng)用程序和其他的應(yīng)用程序和服務(wù)交換所需信息的功能

ContentProvider是Android系統(tǒng)提供的一種標(biāo)準(zhǔn)的共享數(shù)據(jù)的機(jī)制,應(yīng)用程序可以通過(guò)ContentProvider訪問(wèn)其他應(yīng)用程序的私有數(shù)據(jù)(私有數(shù)據(jù)可以是存儲(chǔ)在文件系統(tǒng)中的文件,也可以是SQLite中的數(shù)據(jù)庫(kù))。Android系統(tǒng)內(nèi)部也提供一些內(nèi)置的ContentProvider,能夠?yàn)閼?yīng)用程序提供重要的數(shù)據(jù)信息。

所有Android組件都具有自己的生命周期,是從組件建立到組件銷毀的整個(gè)過(guò)程。在生命周期中,組件會(huì)在可見、不可見、活動(dòng)、非活動(dòng)等狀態(tài)中不斷變化。

4.3 Activity生命周期

Activity生命周期指Activity從啟動(dòng)到銷毀的過(guò)程。Activity表現(xiàn)為4種狀態(tài),分別是活動(dòng)狀態(tài)、暫停狀態(tài)、停止?fàn)顟B(tài)和非活動(dòng)狀態(tài)。

? 活動(dòng)狀態(tài),Activity在用戶界面中處于最上層,完全能被用戶看到,能夠與用戶進(jìn)行交互。

? 暫停狀態(tài),Activity在界面上被部分遮擋,該Activity不再處于用戶界面的最上層,且不能夠與用戶進(jìn)行交互;或者屏幕被鎖定。

? 停止?fàn)顟B(tài),Activity在界面上完全不能被用戶看到,也就是說(shuō)這個(gè)Activity被其他Activity全部遮擋。

? 非活動(dòng)狀態(tài),不在以上3種狀態(tài)中的Activity則處于非活動(dòng)狀態(tài)。

這四種狀態(tài)是可以相互轉(zhuǎn)換的,轉(zhuǎn)換關(guān)系圖如圖4-2所示。

圖4-2 Activity的4種狀態(tài)的轉(zhuǎn)換關(guān)系圖

圖4-3 解釋了狀態(tài)之間轉(zhuǎn)化的可能路徑。其中著色的橢圓表示活動(dòng)的主要狀態(tài),矩形表示當(dāng)活動(dòng)在狀態(tài)之間轉(zhuǎn)換時(shí)會(huì)被調(diào)用的回調(diào)方法。

圖4-3 Activity活動(dòng)周期

Android調(diào)用以下的事件回調(diào)方法通知Activity從某一狀態(tài)轉(zhuǎn)變到另一狀態(tài)。

代碼清單4-1事件的回調(diào)方法

      public class MyActivity extends Activity {
          protected void onCreate(Bundle savedInstanceState);
          protected void onStart();
          protected void onRestart();
          protected void onResume();
          protected void onPause();
          protected void onStop();
          protected void onDestroy();
      }

表4-1對(duì)各個(gè)事件回調(diào)方法做出說(shuō)明。

表4-1 Activity 生命周期的事件回調(diào)方法

Activity事件回調(diào)方法的調(diào)用順序,如圖4-4所示。

圖4-4 Activity事件回調(diào)方法的調(diào)用順序

Activity的生命周期可分為全生命周期、可視生命周期和活動(dòng)生命周期。每個(gè)生命周期中包含不同的事件回調(diào)方法。

4.3.1 全生命周期

全生命周期是從Activity建立到銷毀的全部過(guò)程,始于onCreate(),結(jié)束于onDestroy()。

使用者通常在onCreate()中初始化用戶界面,分配引用類變量,綁定數(shù)據(jù)控件,并創(chuàng)建服務(wù)和線程等Activity所能使用的全局資源和狀態(tài),并在onDestroy()中釋放這些資源,并確保所有外部連接被關(guān)閉,例如,網(wǎng)絡(luò)或數(shù)據(jù)庫(kù)的聯(lián)系等;在一些極端的情況下,Android系統(tǒng)會(huì)不調(diào)用onDestroy()方法,而直接終止進(jìn)程。

為了避免創(chuàng)造短期對(duì)象和增加垃圾收集的時(shí)間,以致對(duì)用戶體驗(yàn)產(chǎn)生直接影響。如果你的Activity需要?jiǎng)?chuàng)建一些對(duì)象的話,最好在onCreate方法中創(chuàng)建,因?yàn)樗谝粋€(gè)Actvity的完整生命周期中僅調(diào)用一次。

4.3.2 可視生命周期

可視生命周期是Activity在界面上從可見到不可見的過(guò)程,開始于onStart(),結(jié)束于onStop()。

? onStart()一般用來(lái)初始化或啟動(dòng)與更新界面相關(guān)的資源。

? onStop()一般用來(lái)暫停或停止一切與更新用戶界面相關(guān)的線程、計(jì)時(shí)器和服務(wù)。

? onRestart()方法在onSart()前被調(diào)用,用來(lái)在Activity從不可見變?yōu)榭梢姷倪^(guò)程中,進(jìn)行一些特定的處理過(guò)程。

? onStart()和onStop()會(huì)被多次調(diào)用。

? onStart()和onStop()也經(jīng)常被用來(lái)注冊(cè)和注銷BroadcastReceiver或者傳感器。

在onStart()和onStop()這兩個(gè)方法中間,Actvity對(duì)用戶將會(huì)是可見的,盡管它可能部分被遮擋著。在一個(gè)Activity完整的生命周期中可能會(huì)經(jīng)過(guò)幾個(gè)Activity可見的生命周期,因?yàn)锳ctivity可能會(huì)經(jīng)常在前臺(tái)和后臺(tái)之間切換。在極端情況下,系統(tǒng)將銷毀掉一個(gè)Activity即使它在可見狀態(tài)并且不調(diào)用onStop方法。

4.3.3 活動(dòng)生命周期

活動(dòng)生命周期是Activity在屏幕的最上層,并能夠與用戶交互的階段,開始于onResume(),結(jié)束于onPause()。在Activity的狀態(tài)變換過(guò)程中onResume()和onPause()經(jīng)常被調(diào)用,因此這兩個(gè)方法中應(yīng)使用更為簡(jiǎn)單、高效的代碼。

? onPause()是第一個(gè)被標(biāo)識(shí)為“可終止”的方法。

? 在onPause()返回后,onStop()和onDestroy()隨時(shí)能被Android系統(tǒng)調(diào)用。

? onPause()常用來(lái)保存持久數(shù)據(jù),如界面上用戶的輸入信息等。

當(dāng)系統(tǒng)而不是用戶關(guān)閉一個(gè)活動(dòng)來(lái)節(jié)省內(nèi)存時(shí),用戶可能希望返回到活動(dòng)且是它之前的狀態(tài)。為了獲得活動(dòng)被關(guān)閉之前的狀態(tài),可以執(zhí)行活動(dòng)的onSaveInstanceState()方法。Android在活動(dòng)容易被銷毀前調(diào)用這個(gè)方法,也就是調(diào)用onPause()之前。該方法的參數(shù)是一個(gè)Bundle對(duì)象,這個(gè)對(duì)象可以名值對(duì)記錄活動(dòng)的動(dòng)態(tài)狀態(tài)。當(dāng)活動(dòng)再次啟動(dòng)時(shí),Bundle同時(shí)被傳遞到onCreate()和調(diào)用onCreate()之后的方法onRestoreInstanceState()。

因?yàn)閛nSaveInstanceState()方法不總被調(diào)用,你應(yīng)該僅使用onSaveInstanceState()來(lái)記錄活動(dòng)的臨時(shí)狀態(tài),而不是持久的數(shù)據(jù)。應(yīng)該使用onPause()來(lái)存儲(chǔ)持久數(shù)據(jù)。

問(wèn):onPause()和onSaveInstanceState()這兩個(gè)函數(shù)都可以用來(lái)保存界面的用戶輸入數(shù)據(jù),它們有什么區(qū)別呢?

? onPause()一般用于保存持久性數(shù)據(jù),并將數(shù)據(jù)保存在存儲(chǔ)設(shè)備上的文件系統(tǒng)或數(shù)據(jù)庫(kù)系統(tǒng)中的。

? onSaveInstanceState()主要用來(lái)保存動(dòng)態(tài)的狀態(tài)信息,信息一般保存在Bundle中。

? Bundle是能夠保存多種格式數(shù)據(jù)的對(duì)象。

? onSaveInstanceState()保存在Bundle中的數(shù)據(jù),系統(tǒng)在調(diào)用onRestoreInstanceState()和onCreate()時(shí),會(huì)同樣利用Bundle將數(shù)據(jù)傳遞給函數(shù)。

當(dāng)一個(gè)活動(dòng)啟動(dòng)另一個(gè)活動(dòng)時(shí),這兩個(gè)活動(dòng)都經(jīng)歷生命周期轉(zhuǎn)換。一個(gè)暫?;蚴峭V?,然而被啟動(dòng)的活動(dòng)則啟動(dòng)。有時(shí),這些活動(dòng)可能需要協(xié)調(diào)。當(dāng)這兩個(gè)活動(dòng)在同一個(gè)進(jìn)程中,生命周期的回調(diào)順序是明確界定的:調(diào)用當(dāng)前活動(dòng)的onPause()方法;然后,按序調(diào)用啟動(dòng)活動(dòng)的onCreate()、onStart()、onResume()方法;之后,如果該活動(dòng)不需再在屏幕上可見,則調(diào)用它的onStop()方法。下面我們就來(lái)詳細(xì)學(xué)習(xí)一下關(guān)于Android如何管理多個(gè)Activity。

(1)Android用Activity Stack來(lái)管理多個(gè)Activity,因此,同一時(shí)刻只會(huì)有最頂上的Activity是處于active或者running狀態(tài)。其他的Activity都被壓在下面。

(2)如果非活動(dòng)的Activity仍是可見的(如果上面壓著的是一個(gè)非全屏的Activity或透明的Activity),它是處于paused狀態(tài)的。在系統(tǒng)內(nèi)存不足的情況下,paused狀態(tài)的Activity是有可能被系統(tǒng)銷毀掉的。

注意

因?yàn)锳ndroid應(yīng)用程序的生存期并不是由應(yīng)用本身直接控制的,而是由Android系統(tǒng)平臺(tái)進(jìn)行管理的,所以,對(duì)于開發(fā)者而言,需要了解不同的組件Activity、Service和IntentReceiver的生命,切記:如果組件的選擇不當(dāng),系統(tǒng)很有可能會(huì)關(guān)閉一個(gè)正在進(jìn)行重要工作的進(jìn)程。

4.4 Activity啟動(dòng)模式

Activity作為Android中重要一環(huán),它有4種不同的啟動(dòng)模式,類似于C語(yǔ)言中的局部變量、全局變量及靜態(tài)變量等。這4種啟動(dòng)模式如下。

? standard:標(biāo)準(zhǔn)模式,調(diào)用startActivity()方法就會(huì)產(chǎn)生一個(gè)新的實(shí)例。

? singleTop:檢查是否已經(jīng)存在了一個(gè)實(shí)例位于Activity Stack的頂部,如果存在就不產(chǎn)生新的實(shí)例,反之則調(diào)用Activity的newInstance()方法產(chǎn)生一個(gè)新實(shí)例。

? singleTask:在一個(gè)新的Task中產(chǎn)生這個(gè)實(shí)例,以后每次調(diào)用都會(huì)使用此實(shí)例,而避免產(chǎn)生新的實(shí)例。

? singleInstance:這個(gè)基本上跟singleTask一樣,只是有一點(diǎn)不同,那就是在這個(gè)模式下的Activity實(shí)例所處的Task中,只能有這一個(gè)Activity實(shí)例,而不能有其他的實(shí)例。

這些啟動(dòng)模式在Android清單文件AndroidManifest.xml中,通過(guò)<activity>中的launchMode屬性進(jìn)行設(shè)置,如代碼清單4-2所示。

代碼清單4-2 AndroidManifest.xml

      <activity android:name=".Activity2"
            android:launchMode="singleTask"></activity>

也可以在Eclipse ADT圖形界面中編輯,如圖4-5所示。

圖4-5 設(shè)置Activity啟動(dòng)模式

下面通過(guò)一個(gè)簡(jiǎn)單的例子——LaunchMode_Test來(lái)對(duì)四種啟動(dòng)模式進(jìn)行簡(jiǎn)要分析。在該例中涉及Fx_Main、Activity2及Activity3三個(gè)Activity。

下面介紹一下例子中涉及的三個(gè)Activity及其界面。

首先是Fx_Main,其界面如圖4-6所示。

圖4-6 Fx_Main的界面

在圖4-6所示的界面中,單擊“跳轉(zhuǎn)到AC2”按鈕之后,跳轉(zhuǎn)至Activity2,具體代碼如代碼清單4-3所示。

代碼清單4-3 Fx_Main.Activity

      import android.app.Activity;
      import android.content.Intent;
      import android.os.Bundle;
      import android.util.Log;
      import android.view.View;
      import android.view.View.OnClickListener;
      import android.widget.Button;
      import android.widget.TextView;
      public class Fx_Main extends Activity {
          /** Called when the activity is first created. */
        private Button b;
        @Override
      protected void onCreate(Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        TextView tv=(TextView)findViewById(R.id.TextView01);
        tv.setText("Main---->"+getTaskId());
        Log.i("System.out", "Main---->"+this.toString()+"Task ID---->"+getTaskId());
        b=(Button)findViewById(R.id.Button01);
        b.setOnClickListener(new OnClickListener(){
            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                Intent i=new Intent(Fx_Main.this,Activity2.class);
                startActivity(i);
            }});
      }
        @Override
        protected void onDestroy() {
            // TODO Auto-generated method stub
            super.onDestroy();
            Log.i("System.out", "Fx_Main--->Destory");
        }
      }

其次是Activity2,其界面如圖4-7所示。

圖4-7 Activity2的界面

在該界面中,單擊“跳回到Main”按鈕,則跳轉(zhuǎn)至Fx_Main,而單擊“跳到本頁(yè)面”則仍顯示Activity2的界面,單擊“跳到AC3”則跳轉(zhuǎn)到Activity3的界面,具體代碼如代碼清單4-4所示。

代碼清單4-4 Activity2.Activity

      import android.app.Activity;
      import android.content.Intent;
      import android.os.Bundle;
      import android.util.Log;
      import android.view.View;
      import android.view.View.OnClickListener;
      import android.widget.Button;
      import android.widget.TextView;
      public class Activity2 extends Activity {
      private Button b;
      private Button b2;
      private Button b3;
        @Override
      protected void onCreate(Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity2);
        b=(Button)findViewById(R.id.Button02);
        b2=(Button)findViewById(R.id.Button03);
        b3=(Button)findViewById(R.id.Button04);
        TextView tv=(TextView)findViewById(R.id.TextView02);
        tv.setText("Ac2---->"+getTaskId());
        Log.i("System.out", "Ac2---->"+this.toString()+"Task ID---->"+getTaskId());
        b.setOnClickListener(new OnClickListener(){
            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                Intent i=new Intent(Activity2.this,Fx_Main.class);
                startActivity(i);
            }});
        b2.setOnClickListener(new OnClickListener(){
            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                Intent i=new Intent(Activity2.this,Activity2.class);
                startActivity(i);
            }});
        b3.setOnClickListener(new OnClickListener(){
            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                Intent i=new Intent(Activity2.this,Activity3.class);
                startActivity(i);
            }});
        }
        @Override
            protected void onDestroy() {
                // TODO Auto-generated method stub
                super.onDestroy();
                Log.i("System.out", "Ac2--->destory");
        }
      }

最后是Activity3,其界面如圖4-8所示。

圖4-8 Activity3的界面

如圖4-8所示,單擊“返回Main”則跳轉(zhuǎn)至Fx_Main,單擊“返回AC2”,則跳轉(zhuǎn)到Activity2。具體代碼如代碼清單4-5所示。

代碼清單4-5 Activity3.Activity

      import android.app.Activity;
      import android.content.Intent;
      import android.os.Bundle;
      import android.util.Log;
      import android.view.View;
      import android.view.View.OnClickListener;
      import android.widget.Button;
      import android.widget.TextView;
      public class Activity3 extends Activity {
        private Button b;
        private Button b2;
        @Override
      protected void onCreate(Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity3);
        b=(Button)findViewById(R.id.Button03);
        b2=(Button)findViewById(R.id.Button04);
        TextView tv=(TextView)findViewById(R.id.TextView03);
        tv.setText("Ac3---->"+getTaskId());
        Log.i("System.out", "Ac3---->"+this.toString()+"Task ID---->"+getTaskId());
        b.setOnClickListener(new OnClickListener(){
            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                Intent i=new Intent(Activity3.this,Fx_Main.class);
                startActivity(i);
            }});
        b2.setOnClickListener(new OnClickListener(){
            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                Intent i=new Intent(Activity3.this,Activity2.class);
                startActivity(i);
            }});
      }
        @Override
        protected void onDestroy() {
            // TODO Auto-generated method stub
            super.onDestroy();
        Log.i("System.out", "Ac3--->Destory");
        }
      }

4.4.1 standard標(biāo)準(zhǔn)模式

在standard模式也就是默認(rèn)模式下,不需要配置launchMode。此時(shí)的AndroidManifest.xml如代碼清單4-6所示。

代碼清單4-6 AndroidManifest.xml

      <?xml version="1.0" encoding="utf-8"?>
      <manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="feixun.com.jiang"
          android:versionCode="1"
          android:versionName="1.0">
        <application android:icon="@drawable/icon" android:label="@string/app_name">
              <activity android:name=".Fx_Main"
                      android:label="@string/app_name" >
                  <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
                    <category android:name="android.intent.category.LAUNCHER" />
                  </intent-filter>
              </activity>
            <activity android:name=".Activity2" android:label="@string/Ac2"/ >
            <activity android:name=".Activity3" android:label="@string/Ac3/>
          </application>
          <uses-sdk android:minSdkVersion="4" />
      </manifest>

運(yùn)行例子,從Fx_Main開始,一直點(diǎn)回到Activity2按鈕時(shí),Log信息如圖4-9所示。

圖4-9 Standard啟動(dòng)模式下Log信息

發(fā)現(xiàn)每次都創(chuàng)建了Activity2的新實(shí)例。standard的加載模式就是這樣的,Intent將發(fā)送給它新的Activity實(shí)例。

現(xiàn)在點(diǎn)擊Android設(shè)備的回退鍵,可以看到Log信息按照剛才創(chuàng)建Activity實(shí)例的倒序依次出現(xiàn),類似退棧的操作,而剛才操作跳轉(zhuǎn)按鈕的過(guò)程是壓棧的操作。

4.4.2 singleTop

singleTop和standard模式,都會(huì)將Intent發(fā)送到新的實(shí)例(如果已經(jīng)有了,singleTask模式和singleInstance模式不發(fā)送到新的實(shí)例)。不過(guò),singleTop要求如果創(chuàng)建intent時(shí)棧頂已經(jīng)有要?jiǎng)?chuàng)建Activity的實(shí)例,則將Intent發(fā)送給該實(shí)例,而不發(fā)送給新的實(shí)例。

還是用剛才的示例,只需將Activity2的launchMode改為singleTop,就能看到區(qū)別。修改后AndroidManifest.xml中代碼如代碼清單4-7所示。

代碼清單4-7 AndroidManifest.xml

      <?xml version="1.0" encoding="utf-8"?>
      <manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="feixun.com.jiang"
          android:versionCode="1"
          android:versionName="1.0">
        <application android:icon="@drawable/icon" android:label="@string/app_name">
              <activity android:name=".Fx_Main"
                      android:label="@string/app_name" >
                  <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
                    <category android:name="android.intent.category.LAUNCHER" />
                  </intent-filter>
              </activity>
            <activity android:name=".Activity2" android:label="@string/Ac2"
                          android:launchMode="singleTop"/ >
            <activity android:name=".Activity3" android:label="@string/Ac3/>
          </application>
          <uses-sdk android:minSdkVersion="4" />
      </manifest>

運(yùn)行Fx_Main,跳轉(zhuǎn)到Activity2---->Actvity2時(shí)會(huì)發(fā)現(xiàn),單擊多少遍按鈕,都是相同的Activity2實(shí)例,因?yàn)樵搶?shí)例在棧頂,所以不會(huì)創(chuàng)建新的實(shí)例。如果回退,回到Fx_Main,將退出應(yīng)用,如圖4-10所示。

圖4-10 singleTop模式下“跳轉(zhuǎn)到AC2”的Log信息

singleTop模式,可用來(lái)解決棧頂多個(gè)重復(fù)相同的Activity的問(wèn)題。

如果是Fx_Main跳轉(zhuǎn)到Activity2,再跳轉(zhuǎn)到Fx_Main,行為就和standard一樣了,會(huì)在Activity2跳轉(zhuǎn)到Fx_Main時(shí)創(chuàng)建Fx_Main的新實(shí)例,因?yàn)楫?dāng)時(shí)的棧頂不是Activity2實(shí)例,如圖4-11所示。

圖4-11 singleTop模式下“跳轉(zhuǎn)到AC2”后“跳回到Main”的Log信息

4.4.3 singleTask

singleTask模式和后面的singleInstance模式都是只創(chuàng)建一個(gè)實(shí)例的。

當(dāng)Intent到來(lái),需要?jiǎng)?chuàng)建singleTask模式Activity時(shí),系統(tǒng)會(huì)檢查棧里面是否已經(jīng)有該Activity的實(shí)例。如果有直接將Intent發(fā)送給它(注意此時(shí)原在此Activity棧中上面的Activity將會(huì)被關(guān)閉)。

把Activity2的啟動(dòng)模式改成singleTask,修改后AndroidManifest.xml中代碼如代碼清單4-8所示。

代碼清單4-8 AndroidManifest.xml

      <?xml version="1.0" encoding="utf-8"?>
      <manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="feixun.com.jiang"
          android:versionCode="1"
          android:versionName="1.0">
        <application android:icon="@drawable/icon" android:label="@string/app_name">
              <activity android:name=".Fx_Main"
                      android:label="@string/app_name" >
                  <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
                    <category android:name="android.intent.category.LAUNCHER" />
                  </intent-filter>
              </activity>
            <activity android:name=".Activity2" android:label="@string/Ac2"
                          android:launchMode="singleTask"/ >
            <activity android:name=".Activity3" android:label="@string/Ac3/>
          </application>
          <uses-sdk android:minSdkVersion="4" />
      </manifest>

啟動(dòng)Fx_Main,跳轉(zhuǎn)到Activity2---->Activity3---->Actvity2,此時(shí)看Log信息,如圖4-12所示。

圖4-12 singleTask啟動(dòng)模式下Log信息

可見從AC3再跳轉(zhuǎn)到AC2時(shí),因?yàn)锳C2之前在棧中是存在的所以不生成新的AC2實(shí)例,而是在棧中找到此AC2,并將在AC2上面的AC3關(guān)閉,所以此時(shí)棧中只有Fx_Main和AC2,在AC2點(diǎn)返回會(huì)直接退到Fx_Main然后退出。

4.4.4 singleInstance

在singleInstance模式下,加載該Activity時(shí)如果沒(méi)有實(shí)例化,它會(huì)在創(chuàng)建新的Task后,實(shí)例化入棧,如果已經(jīng)存在,則直接調(diào)用onNewIntent,該Activity的Task中不允許啟動(dòng)其他的Activity,任何從該Activity啟動(dòng)的其他Activity都將被放到其他Task中,先檢查是否有在應(yīng)用的Task,沒(méi)有的話就創(chuàng)建。

在這里介紹一下Task(任務(wù))的概念。按照字面意思,任務(wù)就是自己要實(shí)現(xiàn)的一個(gè)目的,而在Android中的Task的定義是一系列Activity的集合,即要達(dá)到自己最終要到的Actvity,之前所有經(jīng)歷過(guò)的Actvity的集合。它可以是同一個(gè)應(yīng)用內(nèi)部的,也可以是兩個(gè)不同應(yīng)用的。Task可以認(rèn)為是一個(gè)棧,可放入多個(gè)Activity。比如,啟動(dòng)一個(gè)應(yīng)用,那么Android就創(chuàng)建了一個(gè)Task,然后啟動(dòng)這個(gè)應(yīng)用的入口Activity,就是intent-filter中配置為main和launch的那個(gè)。這個(gè)Activity是根(Root)Activity,可能會(huì)在它的界面調(diào)用其他Activity,這些Activity如果按照上面那3個(gè)模式,也會(huì)在這個(gè)棧(Task)中,只是實(shí)例化的策略不同而已。

把Activity2的啟動(dòng)模式改成singleInstance,修改后AndroidManifest.xml中代碼如代碼清單4-9所示。

代碼清單4-9 AndroidManifest.xml

      <?xml version="1.0" encoding="utf-8"?>
      <manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="feixun.com.jiang"
          android:versionCode="1"
          android:versionName="1.0">
        <application android:icon="@drawable/icon" android:label="@string/app_name">
              <activity android:name=".Fx_Main"
                      android:label="@string/app_name" >
                  <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
                    <category android:name="android.intent.category.LAUNCHER" />
                  </intent-filter>
              </activity>
            <activity android:name=".Activity2" android:label="@string/Ac2"
                          android:launchMode="singleInstance"/ >
            <activity android:name=".Activity3" android:label="@string/Ac3/>
          </application>
          <uses-sdk android:minSdkVersion="4" />
      </manifest>

然后進(jìn)行測(cè)試,啟動(dòng)Fx_Main---->Actvity2---->Actvity3然后看一下Log信息,如圖4-13所示。

圖4-13 singleInstance啟動(dòng)模式下Log信息

可以看到Fx_Main以及Activity3的Task ID為9,而Actvity2的Task ID為10,此時(shí)在Actvity3單擊“返回”按鈕會(huì)發(fā)現(xiàn)先退到Fx_Main,繼續(xù)返回會(huì)回到Actvity2最后退出。從該過(guò)程可以看出:如果從其他應(yīng)用程序調(diào)用singleInstance模式的Activity(Fx_Main),從該Activity開啟其他Activity(Activity2)時(shí),會(huì)創(chuàng)建一個(gè)新的Task(Task ID為10的那個(gè)),實(shí)際上,如果包含該Activity(Activity2)的Task已經(jīng)運(yùn)行的話,他會(huì)在該運(yùn)行的Task中重新創(chuàng)建。

經(jīng)過(guò)上述的介紹,用下面的表格來(lái)進(jìn)行一個(gè)簡(jiǎn)單的總結(jié),如表4-2所示。

表4-2 Activity4種啟動(dòng)模式對(duì)比

4.5 程序調(diào)試

Android系統(tǒng)提供了兩種調(diào)試工具LogCat和DevTools,用于定位、分析及修復(fù)程序中出現(xiàn)的錯(cuò)誤。

4.5.1 LogCat命令行工具

LogCat是可以顯示在Eclipse集成開發(fā)環(huán)境中的用來(lái)獲取系統(tǒng)日志信息的工具。它的主要功能就是能夠捕獲包括Dalvik虛擬機(jī)產(chǎn)生的信息、進(jìn)程信息、ActivityManager信息、PackagerManager信息、Homeloader信息、WindowsManager信息、Android運(yùn)行時(shí)信息和應(yīng)用程序信息等可被捕獲的信息。

1.LogCat的使用方法

打開方式:選擇“Window”→“Show View ”→“Other”命令,打開Show View的選擇菜單,然后在Andoird → LogCat中選擇LogCat。打開LogCat后,它便顯示在Eclipse的下方區(qū)域,其界面如圖4-14所示。

圖4-14 LogCat界面

從圖中我們可以看到LogCat的右上方有5個(gè)不同的字母,這5個(gè)字母分別表示5種不同類型的日志信息,它們的級(jí)別依次增高,表示含義如下。

? V:詳細(xì)(Verbose)信息。

? D:調(diào)試(Debug)信息。

? I:通告(Info)信息。

? W:警告(Warn)信息。

? E:錯(cuò)誤(Error)信息。

在LogCat中,用戶可以通過(guò)5個(gè)字母圖標(biāo)選擇顯示的信息類型,級(jí)別高于所選類型的信息也會(huì)在LogCat中顯示,但級(jí)別低于所選類型的信息則不會(huì)被顯示。

同時(shí),LogCat提供了“過(guò)濾”功能,在右上角的“+”號(hào)和“-”號(hào),分別是添加和刪除過(guò)濾器。用戶可以根據(jù)日志信息的標(biāo)簽(Tag)、產(chǎn)生日志的進(jìn)程編號(hào)(Pid)或信息等級(jí)(Level),對(duì)顯示的日志內(nèi)容進(jìn)行過(guò)濾。

2.程序調(diào)試原理

? 引入android.util.Log包。

? 使用Log.v()、 Log.d()、 Log.i() 、Log.w() 和Log.e() 5個(gè)方法在程序中設(shè)置“日志點(diǎn)”。

? Log.v()用來(lái)記錄詳細(xì)信息。

? Log.d()用來(lái)記錄調(diào)試信息。

? Log.i()用來(lái)記錄通告信息。

? Log.w()用來(lái)記錄警告信息。

? Log.e()用來(lái)記錄錯(cuò)誤信息。

? 當(dāng)程序運(yùn)行到“日志點(diǎn)”時(shí),應(yīng)用程序的日志信息便被發(fā)送到LogCat中。

? 判斷“日志點(diǎn)”信息與預(yù)期的內(nèi)容是否一致。

? 進(jìn)而判斷程序是否存在錯(cuò)誤。

下面的例子演示了Log類的具體使用方法。

代碼清單4-10 LogCat.java

      package com.example.LogCat;
      import android.app.Activity;
      import android.os.Bundle;
      import android.util.Log;
      public class LogCat extends Activity {
          final static String TAG = "LOGCAT";
          @Override
          public void onCreate(Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              setContentView(R.layout.main);
              Log.v(TAG,"Verbose");
              Log.d(TAG,"Debug");
              Log.i(TAG,"Info");
              Log.w(TAG,"Warn");
              Log.e(TAG,"Error");
          }
      }

在本段代碼中,程序第5行“import android.util.Log;”引入android.util.Log包;第8行定義標(biāo)簽,標(biāo)簽幫助用戶在LogCat中找到目標(biāo)程序生成的日志信息,同時(shí)也能夠利用標(biāo)簽對(duì)日志進(jìn)行過(guò)濾;第14行記錄一個(gè)詳細(xì)信息,Log.v()方法的第一個(gè)參數(shù)是日志的標(biāo)簽,第二個(gè)參數(shù)是實(shí)際的信息內(nèi)容;第15~18行分別產(chǎn)生了調(diào)試信息、通告信息、警告信息和錯(cuò)誤信息。

最終運(yùn)行結(jié)果如圖4-15所示,從圖中還可以看出LogCat對(duì)不同類型的信息使用了不同的顏色加以區(qū)別。

圖4-15 LogCat工程的運(yùn)行結(jié)果

3.添加過(guò)濾器

上文中提到LogCat提供了“過(guò)濾”功能,下面就來(lái)介紹一下LogCat是如何添加過(guò)濾器的。

首先,單擊右上角的“+”,在彈出的對(duì)話框中填入過(guò)濾器的名稱:LogcatFilter,設(shè)置過(guò)濾條件為“標(biāo)簽=LOGCAT”即可,操作方法如圖4-16所示。

圖4-16 添加過(guò)濾器

經(jīng)過(guò)上述過(guò)濾器過(guò)濾后,無(wú)論什么類型的日志信息,屬于哪一個(gè)進(jìn)程,只要標(biāo)簽為L(zhǎng)ogCat,都將顯示在LogcatFilter區(qū)域內(nèi)。LogCat過(guò)濾后的輸入結(jié)果如圖4-17所示。

圖4-17 LogCat過(guò)濾后的輸入結(jié)果

4.5.2 DevTools開發(fā)調(diào)試工具

DevTools是用于調(diào)試和測(cè)試的工具,它包括了如下所示一系列各種用途的用戶小工具:Development Settings、Exception Browser、Google Login Service、Instrumentation、Media Scanner、Package Browser、Pointer Location、Raw Image Viewer、Running processes和Terminal Emulator。

如圖4-18 所示,為DevTools使用時(shí)的界面。由使用時(shí)的界面也可以看出其中的各個(gè)小工具。

圖4-18 DevTools的使用界面

以下著重講解Dev Tools的一些小工具。

1.Development Settings

Development Settings中包含了程序調(diào)試的相關(guān)選項(xiàng),單擊功能前面的選擇框,出現(xiàn)綠色的“對(duì)號(hào)”表示功能啟用,模擬器會(huì)自動(dòng)保存設(shè)置。

圖4-19顯示了Development Settings的運(yùn)行界面。

圖4-19 Development Settings運(yùn)行界面

下面就詳細(xì)介紹Development Settings中各個(gè)選項(xiàng)的含義,如表4-3所示。

表4-3 Development Settings中各選項(xiàng)的含義

2.Pointer Location

Pointer Location是屏幕點(diǎn)位置查看工具,能夠顯示觸摸點(diǎn)的X軸坐標(biāo)和Y軸坐標(biāo),如圖4-20所示。

圖4-20 Pointer Location的使用畫面

3.Running processes

Running processes能夠查看在Android系統(tǒng)中正在運(yùn)行的進(jìn)程,并能查看進(jìn)程的詳細(xì)信息,包括進(jìn)程名稱和進(jìn)程所調(diào)用的程序包。

圖4-21 Andoird模擬器默認(rèn)情況下運(yùn)行的進(jìn)程和com.android.phone進(jìn)程的詳細(xì)信息

4.Terminal Emulator

Terminal Emulator可以打開一個(gè)連接底層Linux系統(tǒng)的虛擬終端,但具有的權(quán)限較低,且不支持提升權(quán)限的su命令。如果需要使用root權(quán)限的命令,可以使用ADB工具。

圖4-22是Terminal Emulator運(yùn)行時(shí)的畫面,輸入ls命令,顯示出根目錄下的所有文件夾。

圖4-22 Terminal Emulator運(yùn)行時(shí)的畫面

4.6 本章小結(jié)

本章主要介紹了Android系統(tǒng)的進(jìn)程優(yōu)先級(jí)排序、不同優(yōu)先級(jí)進(jìn)程之間的變化方式,Android系統(tǒng)的4大基本組件及其用途,Activity的生命周期中各個(gè)狀態(tài)及狀態(tài)間的變化關(guān)系、Android應(yīng)用程序的調(diào)試方法和工具。

關(guān)鍵知識(shí)點(diǎn)測(cè)評(píng)

1.以下有關(guān)Android系統(tǒng)進(jìn)程優(yōu)先級(jí)的說(shuō)法,不正確的一個(gè)是( )。

A.前臺(tái)進(jìn)程是Android系統(tǒng)中最重要的進(jìn)程

B.空進(jìn)程在系統(tǒng)資源緊張時(shí)會(huì)被首先清除

C.服務(wù)進(jìn)程沒(méi)有用戶界面并且在后臺(tái)長(zhǎng)期運(yùn)行

D.Android系統(tǒng)中一般存在數(shù)量較多的可見進(jìn)程

2.以下有關(guān)Android組件的敘述,正確的一個(gè)是( )。

A.Service是Android程序的呈現(xiàn)層

B.BroadcaseReceiver本身包含界面,用于通知用戶接收到重要信息

C.應(yīng)用程序可以通過(guò)ContentProvider訪問(wèn)其他應(yīng)用程序的私有數(shù)據(jù)

D.不是所有的Android組件都具有自己的生命周期

3.以下有關(guān)Activity生命周期的描述,不正確的是(?。?。

A.Activity的狀態(tài)之間是可以相互轉(zhuǎn)換的

B.Activity的全生命周期是從Activity建立到銷毀的全部過(guò)程,始于onCreate(),結(jié)束于onDestroy()

C.活動(dòng)生命周期是Activity在屏幕的最上層,并能夠與用戶交互的階段

D.onPause()函數(shù)在Android系統(tǒng)因資源不足終止Activity前調(diào)用

主站蜘蛛池模板: 新晃| 乌鲁木齐市| 永登县| 禹州市| 铁力市| 砀山县| 汝州市| 日照市| 上栗县| 昌黎县| 石屏县| 慈溪市| 府谷县| 安庆市| 平山县| 阳新县| 沂源县| 大荔县| 敦化市| 南澳县| 大宁县| 广东省| 长治市| 绵阳市| 深泽县| 潜山县| 福泉市| 巢湖市| 华池县| 法库县| 翁牛特旗| 威海市| 德令哈市| 枞阳县| 东安县| 平南县| 揭东县| 咸阳市| 法库县| 临江市| 哈尔滨市|