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

2.3 Android應(yīng)用框架

前面介紹了Android的系統(tǒng)框架,主要目的是讓大家對(duì)Android系統(tǒng)有整體的概念,也為日后更深入的學(xué)習(xí)打好基礎(chǔ)。然而,目前我們更需要重點(diǎn)學(xué)習(xí)和掌握的則是Android的應(yīng)用框架,因?yàn)槭欠衲苷莆蘸屠斫釧ndroid應(yīng)用框架,直接關(guān)系到是否能學(xué)好Android應(yīng)用開(kāi)發(fā)。

Android的應(yīng)用框架是一個(gè)龐大的體系,想要理解透徹并不是那么簡(jiǎn)單的事情,但是,好在其中有一些比較清晰的脈絡(luò)可以幫助我們快速地熟悉這個(gè)系統(tǒng),因此抓住這些脈絡(luò)中的核心要點(diǎn)對(duì)于能否學(xué)好Android的應(yīng)用開(kāi)發(fā)來(lái)說(shuō)是至關(guān)重要的。一般來(lái)說(shuō),Android應(yīng)用框架中包含四個(gè)核心要點(diǎn),即活動(dòng)(Activity)、消息(Intent)、視圖(View)和任務(wù)(Task)。

如果你覺(jué)得上述核心要點(diǎn)的概念很陌生,不好理解,那么我們來(lái)看看下面這個(gè)比喻:如果把一個(gè)Android應(yīng)用比喻成海洋,那么每個(gè)Activity就是這個(gè)海洋中的島嶼,假設(shè)我們眼前有一項(xiàng)任務(wù)(也就是Task),需要我們?cè)谄渲腥舾蓚€(gè)島嶼上建立起自己的王國(guó)。于是問(wèn)題來(lái)了,我們要怎么樣從一座島嶼去到另一座島嶼呢?沒(méi)錯(cuò),我們需要交通工具,而Intent就是我們最重要的交通工具。當(dāng)然,Intent不僅可以帶我們?nèi)ィ疫€可以幫我們帶上很多需要的東西。接著,到了島上,我們開(kāi)始建立一個(gè)自己的王國(guó),要知道這可需要很多的資源,這個(gè)時(shí)候,我們就會(huì)想到View這個(gè)建筑公司,因?yàn)樗梢詭椭覀兛焖俚亟ǔ鑫覀冃枰臇|西。這樣,Activity、Intent、View以及Task一起配合完成了一個(gè)完整的Android應(yīng)用的王國(guó)。

從以上的比喻中,我們還可以認(rèn)識(shí)到,在這四個(gè)要點(diǎn)中,Activity是基礎(chǔ),Intent是關(guān)鍵,View是必要工具,而Task則是開(kāi)發(fā)的脈絡(luò)。對(duì)于開(kāi)發(fā)者來(lái)說(shuō),只有掌握了Activity、Intent、View和Task這幾個(gè)核心要素之后,才能夠做出多種多樣的應(yīng)用程序。接下來(lái),讓我們分別學(xué)習(xí)一下這四個(gè)核心要點(diǎn)。

2.3.1 活動(dòng)(Activity)

活動(dòng)(Activity)是Android應(yīng)用框架最基礎(chǔ)、最核心的內(nèi)容和元素,每個(gè)Android應(yīng)用都是由一個(gè)或者若干個(gè)Activity構(gòu)成的。在Android應(yīng)用系統(tǒng)中,Activity的概念類似于界面,而Activity對(duì)象我們通常稱之為“界面控制器”(從MVC的角度來(lái)說(shuō))。從另一個(gè)角度來(lái)理解,Activity的概念比較類似于網(wǎng)站(Web)開(kāi)發(fā)中“網(wǎng)頁(yè)”的概念。此外,當(dāng)Android應(yīng)用運(yùn)行的時(shí)候,每個(gè)Activity都會(huì)有自己獨(dú)立的生命周期,圖2-2所示的就是Activity的生命周期。

圖2-2 Activity生命周期

其實(shí),在Android系統(tǒng)內(nèi)部有專門(mén)的Activity堆棧(Stack)空間,用于存儲(chǔ)多個(gè)Activity的運(yùn)行狀態(tài)。一般來(lái)說(shuō),系統(tǒng)會(huì)保證某一時(shí)刻只有最頂端的那個(gè)Activity是處于前端的活動(dòng)(foreground)狀態(tài)。也正因如此,一個(gè)Activity才會(huì)有如圖2-2所示的生命周期。當(dāng)一個(gè)Activity啟動(dòng)并進(jìn)入活動(dòng)狀態(tài)的時(shí)候,調(diào)用順序是onCreate、onStart、onResume;退居后臺(tái)的時(shí)候,調(diào)用順序是onPause、onStop;重新回到活動(dòng)狀態(tài)的時(shí)候,調(diào)用順序是onRestart、onStart、onResume;銷毀的時(shí)候,調(diào)用順序是onPause、onStop、onDestroy。我們應(yīng)該深刻理解這些狀態(tài)的變化過(guò)程,因?yàn)樵贏ndroid應(yīng)用開(kāi)發(fā)的過(guò)程中我們會(huì)經(jīng)常用到。至于如何更好地掌握Activity的特性,大家可以嘗試將以下代碼(代碼清單2-1)放入Android應(yīng)用中運(yùn)行,并對(duì)照程序打印出的調(diào)試信息來(lái)理解Activity生命周期各階段的使用。

代碼清單 2-1

// 基礎(chǔ)Activity類,用于測(cè)試
public class BasicActivity extends Activity {

  private String TAG = this.getClass().getSimpleName();

  public void onCreate(Bundle savedInstanceState) {
        Log.w(TAG, "TaskId:"+this.getTaskId());
  }

  public void onStart() {
        super.onStart();
        Log.w(TAG, "onStart");
  }

  public void onRestart() {
        super.onStart();
        Log.w(TAG, "onRestart");
  }

  public void onResume() {
        super.onResume();
        Log.w(TAG, "onResume");
  }

  public void onPause() {
        super.onPause();
        Log.w(TAG, "onPause");
  }

  public void onStop() {
        super.onStop();
        Log.w(TAG, "onStop");
  }

  public void onDestroy() {
        super.onDestroy();
        Log.w(TAG, "onDestroy");
  }

  public void onNewIntent() {
        Log.w(TAG, "onNewIntent");
  }
}

此外,所有的Activity必須在項(xiàng)目基礎(chǔ)配置文件AndroidManifest.xml中聲明,這樣Activity才可以被Android應(yīng)用框架所識(shí)別;如果你只寫(xiě)了Java代碼而不進(jìn)行聲明的話,運(yùn)行時(shí)就會(huì)拋出ActivityNotFoundException異常。關(guān)于Activity聲明的具體操作,我們會(huì)在2.10.2節(jié)中結(jié)合Hello World項(xiàng)目進(jìn)行詳細(xì)介紹。

2.3.2 消息(Intent)

參考之前我們對(duì)Android應(yīng)用框架的幾個(gè)核心要點(diǎn)的比喻,我們應(yīng)該知道Intent消息模塊對(duì)于Android應(yīng)用框架來(lái)說(shuō)有多重要;如果沒(méi)有它的話,Android應(yīng)用的各個(gè)模塊就像一座座“孤島”,根本不可能構(gòu)成一個(gè)完整的系統(tǒng)。在Android應(yīng)用系統(tǒng)中,我們常常把Intent稱為消息,實(shí)際上,Intent本身還是一個(gè)對(duì)象,里面包含的是構(gòu)成消息的內(nèi)容和屬性,主要有如下幾個(gè)屬性,我們來(lái)分別認(rèn)識(shí)一下。

1.組件名稱(ComponentName)

對(duì)于Android系統(tǒng)來(lái)說(shuō),組件名稱實(shí)際上就是一個(gè)ComponentName對(duì)象,用于指定Intent對(duì)應(yīng)的目標(biāo)組件,Intent對(duì)象可以通過(guò)setComponent、setClass或者setClassName方法來(lái)進(jìn)行設(shè)置。

2.動(dòng)作(Action)

消息基類(Intent)中定義了各種動(dòng)作常量(字符串常量),其中比較常見(jiàn)的有:ACTION_MAIN(對(duì)應(yīng)字符串a(chǎn)ndroid.intent.action.MAIN)表示應(yīng)用的入口的初始化動(dòng)作;ACTION_EDIT(對(duì)應(yīng)字符串a(chǎn)ndroid.intent.action.EDIT)表示常見(jiàn)的編輯動(dòng)作;ACTION_CALL(對(duì)應(yīng)字符串a(chǎn)ndroid.intent.action.CALL)則表示用于初始化電話模塊動(dòng)作等。Intent對(duì)象常使用setAction方法來(lái)設(shè)置。

3.數(shù)據(jù)(Data)

不同的動(dòng)作對(duì)應(yīng)不同的數(shù)據(jù)(Data)類型,比如ACTION_EDIT動(dòng)作可能對(duì)應(yīng)的是用于編輯文檔的URI;而ACTION_CALL動(dòng)作則應(yīng)該包含類似于tel:xxx的URI。多數(shù)情況下,數(shù)據(jù)類型可以從URI的格式中獲取,當(dāng)然,Intent也支持使用setData、setType方法來(lái)指定數(shù)據(jù)的URI以及數(shù)據(jù)類型。

4.類別(Category)

既然不同的動(dòng)作應(yīng)該對(duì)應(yīng)不同的數(shù)據(jù)類型,那么不同的動(dòng)作也應(yīng)該由不同的類別的Activity組件來(lái)處理,比如CATEGORY_BROWSABLE表示該Intent應(yīng)該由瀏覽器組件來(lái)打開(kāi),CATEGORY_LAUNCHER表示此Intent由應(yīng)用初始化Activity處理;而CATEGORY_PREFERENCE則表示處理該Intent的應(yīng)該是系統(tǒng)配置界面。此外,消息對(duì)象(Intent)可以使用addCategory添加一種類型,而一個(gè)Intent對(duì)象也可以包含多種類型屬性。

5.附加信息(Extras)

一個(gè)Intent對(duì)象除了可以包含以上的重要信息之外,還可以存儲(chǔ)一些自定義的額外附加信息,一般來(lái)說(shuō),這些信息是使用鍵值對(duì)(key value)的方式存儲(chǔ)的。我們可以使用putExtra方法設(shè)置附加信息,信息類型非常豐富(一般還是以字符串為主);在接收的時(shí)候使用getExtras方法獲取。

6.標(biāo)志(Flags)

除了上面提到的幾個(gè)功能屬性,消息基類中還定義了一系列特殊的消息行為屬性(也就是標(biāo)志),用于指示Android系統(tǒng)如何去啟動(dòng)Activity以及啟動(dòng)之后如何處理。關(guān)于標(biāo)志(Flags)的使用我們還會(huì)在2.3.4節(jié)中介紹。

在Android應(yīng)用中,消息(Intent)的使用方式通常有兩種,一是顯式消息(Explicit Intent),另一個(gè)則是隱式消息(Implicit Intent)。顯式消息的使用比較簡(jiǎn)單,只需要在Intent中指定目標(biāo)組件名稱(也就是前面提到的ComponentName屬性)即可,一般用于目標(biāo)Activity比較明確的情形。比如在一個(gè)固定流程中,我們需要從一個(gè)Activity跳轉(zhuǎn)到另一個(gè),那么我們就會(huì)使用顯式的消息。而隱式消息則比較復(fù)雜一點(diǎn),它需要通過(guò)消息過(guò)濾器(IntentFilter)來(lái)處理,一般用于目的性不是那么明確的情形,比如應(yīng)用中的某個(gè)功能需要往目的地發(fā)送消息,但是我們卻不確定要使用短信發(fā)送還是微博發(fā)送,那么這個(gè)時(shí)候就應(yīng)該使用隱性消息來(lái)處理了。下面是一個(gè)典型的消息過(guò)濾器的配置范例,如代碼清單2-2所示。

代碼清單 2-2

<activity...>
  <intent-filter>
        <action android:name="android.intent.action.SEND" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:scheme="content" android:mimeType="image/*" />
  </intent-filter>
</activity>

我們看到,配置消息過(guò)濾器使用的是標(biāo)簽,一般需要包含三個(gè)要素:action、category以及data。其中,action是必需的,category一般也是需要的,而data則允許沒(méi)有設(shè)置。接下來(lái),我們學(xué)習(xí)一下這幾個(gè)要素的使用方法。

·<action/>:在Android應(yīng)用中,一般會(huì)通過(guò)<action/>元素來(lái)匹配消息(Intent),如果找到Action就表明匹配成功,否則就是還沒(méi)找到目標(biāo)。需要注意的是,如果消息過(guò)濾器沒(méi)有指定<action/>元素,那么此消息只能被顯式消息匹配上,不能匹配任何的隱式消息;相反,當(dāng)消息沒(méi)有指定目標(biāo)組件名稱時(shí),可以匹配含有任何包含<action/>的消息過(guò)濾器,但不能匹配沒(méi)有指定<action/>信息的消息過(guò)濾器。

·<category/>:<category/>元素用于標(biāo)注消息的類別。值得注意的是,假如我們使用<category/>元素來(lái)標(biāo)識(shí)消息類別,系統(tǒng)在調(diào)用Context.startActivity方法或者Context.startActivityForResult方法時(shí)都會(huì)自動(dòng)加上DEFAULT類別。因此,除了Intent已經(jīng)指定為Intent.ACTION_MAIN以外,我們還必須指定<category/>為android.intent.category.DEFAULT,否則該消息將不會(huì)被匹配到。另外,對(duì)于Service和BroadcastReceiver,如果Intent中沒(méi)有指定<category/>,那么在其消息過(guò)濾器中也不必指定。

·<data/>:通過(guò)data字段來(lái)匹配消息相對(duì)來(lái)講比較復(fù)雜,通常的data字段包含uri、scheme(content,file,http)和type(mimeType)幾種字段。對(duì)于Intent來(lái)說(shuō),我們可以使用setData和setType方法來(lái)設(shè)置,對(duì)于IntentFilter來(lái)講,則可以通過(guò)android:scheme和android:mimeType屬性分別來(lái)指定,使用范例如代碼清單2-3所示。

代碼清單 2-3

<activity ...>
  <intent-filter>
        <action android:name="android.intent.action.SEND" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:scheme="file" android:mimeType="image/*" />
  </intent-filter>
</activity>

以上的配置表明該Activity可以發(fā)送圖片,而且內(nèi)容必須是單獨(dú)的一個(gè)文件,也就是說(shuō),該文件的URI路徑必須是以“file://”開(kāi)頭的。當(dāng)然,如果我們把這里的“android:scheme”改成“content”的話,則表明該圖片內(nèi)容必須是由ContentProvider提供的,即URI必須是以“content://”開(kāi)頭的。

至此,我們已經(jīng)介紹了消息(Intent)和消息過(guò)濾器(IntentFilter)的基本概念和用法。我們必須清楚的是,消息分為顯式消息和隱式消息兩種,而消息過(guò)濾器一般是提供給隱式消息使用的。Android消息過(guò)濾器的過(guò)濾規(guī)則比較嚴(yán)格,只要我們申明了除了默認(rèn)值(DEFAULT)之外的action、category和data,那么,只有當(dāng)對(duì)應(yīng)消息對(duì)象的動(dòng)作(action)、類別(category)和數(shù)據(jù)類型(data)同時(shí)符合消息過(guò)濾器的配置時(shí)才會(huì)被考慮。關(guān)于<intent-filter/>標(biāo)簽的具體使用方法,我們將會(huì)在本書(shū)7.2.4節(jié)中結(jié)合實(shí)例進(jìn)行講解。

2.3.3 視圖(View)

視圖(View)系統(tǒng)主管Android應(yīng)用的界面外觀顯示,因此也稱作Android UI系統(tǒng),是Android應(yīng)用框架中最重要的組成部分之一。我們?cè)贏ctivity中展示或者操作的幾乎所有控件都屬于View。Android應(yīng)用框架的View System包含View和ViewGroup兩類基礎(chǔ)組件。下面我們來(lái)理解一下Android視圖系統(tǒng)的層次結(jié)構(gòu),如圖2-3所示。

圖2-3 Android視圖系統(tǒng)的層次結(jié)構(gòu)

視圖類(View)是所有視圖(UI)控件(包括ViewGroup)的基類。視圖組(ViewGroup)則類似于集合,一個(gè)視圖組可以包含多個(gè)ViewGroup和View,類似于Html標(biāo)簽中的層(div)。接下來(lái),我們?cè)賮?lái)看看View中會(huì)經(jīng)常使用的一些UI控件(見(jiàn)表2-3),你也可以在Android SDK參考文檔(Reference)中的android.widget包下找到它們。

從表2-3中可以看出,Android應(yīng)用框架為我們提供了非常豐富的視圖控件,從某種程度上來(lái)說(shuō),Android應(yīng)用的界面是通過(guò)各種各樣的視圖控件組合起來(lái)的。至于這些視圖控件的具體用法,我們將在第7章中結(jié)合項(xiàng)目實(shí)例進(jìn)行介紹。

表2-3 Android主要UI控件

本節(jié)只是從應(yīng)用程序框架組成部分的角度簡(jiǎn)單地介紹了Android UI系統(tǒng)的概念,關(guān)于UI系統(tǒng)的更多知識(shí)以及UI控件的具體用法,我們將在本章2.7節(jié)中更系統(tǒng)地介紹。

2.3.4 任務(wù)(Task)

本節(jié)介紹Android任務(wù)(Task)的概念。區(qū)別于以上介紹的活動(dòng)、消息和視圖這幾個(gè)要點(diǎn),任務(wù)的概念顯得比較抽象,且我們?cè)谌粘>幋a過(guò)程中也不會(huì)直接接觸到,但是,理解任務(wù)卻是理解整個(gè)Android應(yīng)用框架的關(guān)鍵。

首先,我們來(lái)認(rèn)識(shí)一下Android系統(tǒng)中的任務(wù)是如何運(yùn)行的。簡(jiǎn)單來(lái)說(shuō),當(dāng)我們?cè)谑謾C(jī)的應(yīng)用列表(Application Launcher)中點(diǎn)擊某個(gè)應(yīng)用圖標(biāo)的時(shí)候,一個(gè)新的Task就啟動(dòng)了,后面的操作可能會(huì)涉及多個(gè)應(yīng)用中不同Activity的界面,而這些Activity的運(yùn)行狀態(tài)都會(huì)被存儲(chǔ)到Task的Activity堆棧(Activity Stack)中去。和其他的堆棧一樣,Activity堆棧采用的是“后進(jìn)先出”的規(guī)則。圖2-4展示就是一個(gè)常見(jiàn)任務(wù)中Activity堆棧的變化情況。

每次啟動(dòng)一個(gè)新的Activity,其都會(huì)被壓入(push)到Activity堆棧的頂部,而每次按“BACK”鍵,當(dāng)前的Activity就會(huì)被彈出(pop)Activity堆棧;另外,如果按了“HOME”鍵的話,該Task會(huì)失去焦點(diǎn)并被保存在內(nèi)存中;而一旦重新啟動(dòng),Task會(huì)自動(dòng)讀出并顯示上次所在的Activity的界面。那么,從一個(gè)應(yīng)用進(jìn)入另一個(gè)應(yīng)用的情況是怎樣呢?比如,應(yīng)用中需要配置一些系統(tǒng)設(shè)置,那么我們就需要考慮一下多任務(wù)切換的情況了,如圖2-5所示。

圖2-4 單任務(wù)模式中Activity堆棧的變化

圖2-5 多任務(wù)模式中Activity堆棧的變化

我們假設(shè)Task A是應(yīng)用A的任務(wù),也是我們所在的任務(wù),當(dāng)運(yùn)行到Activity 3的時(shí)候我們按了“Home”鍵,于是Task A中的所有Activity就都被停止了,同時(shí)Task A暫時(shí)退居到后臺(tái)(Background);這時(shí),我們點(diǎn)擊應(yīng)用B的圖標(biāo)激活了Task B,于是Task B就被推到了前臺(tái)(Foreground),并展示出最上層的Activity Z;當(dāng)然,我們還可以用類似的操作把Task A激活并放置到前臺(tái)進(jìn)行操作。以上也是我們使用Android系統(tǒng)最經(jīng)常使用的行為操作,大家可以結(jié)合實(shí)際情況好好理解一下。

以上的策略已經(jīng)可以滿足大部分Android應(yīng)用的需求。此外,Android還提供了一些其他的策略來(lái)滿足一些特殊的需求。比較常見(jiàn)的,如我們可以在Android基礎(chǔ)配置文件(Menifest File)中使用<activity/>元素的launchMode屬性來(lái)控制Activity在任務(wù)中的行為特征。launchMode有以下四種模式可供選擇。

·Standard模式:Standard模式為默認(rèn)模式,無(wú)論是打開(kāi)一個(gè)新的Activity,還是接收Intent消息,系統(tǒng)都會(huì)為這個(gè)Activity創(chuàng)建一個(gè)新的實(shí)例(instance);每個(gè)Activity都可以被實(shí)例化多次,并且每個(gè)任務(wù)都可以包含多個(gè)實(shí)例。此模式最常用,但是其缺點(diǎn)就是太耗費(fèi)系統(tǒng)資源。

·singleTop模式:該模式下的行為和Standard模式下的行為基本相同,如果該Activity正好在運(yùn)行狀態(tài)(也就是在Activity堆棧的頂部),那么其接收Intent消息就不需要重新創(chuàng)建實(shí)例,而是通過(guò)該類的onNewIntent()方法來(lái)處理接收到的消息。這種處理方式在一定程度上會(huì)減少一些資源浪費(fèi)。

·singleTask模式:此模式保證該Activity在任務(wù)中只會(huì)有一個(gè)實(shí)例,并且必須存在于該Task的根元素(即棧底)。此模式比較節(jié)省資源,手機(jī)瀏覽器使用的就是這種模式。

·singleInstance模式:此模式與singleTask模式類似,不同之處是該模式保證Activity獨(dú)占一個(gè)Task,其他的Activity都不能存在于該任務(wù)的Activity堆棧中。當(dāng)然,Activity接收Intent消息也是通過(guò)onNewIntent方法實(shí)現(xiàn)。

此外,我們還可以通過(guò)設(shè)置Intent消息的flag標(biāo)志來(lái)主動(dòng)改變Activity的調(diào)用方式,比較常見(jiàn)的flag如下。

·FLAG_ACTIVITY_NEW_TASK:在新的Task中啟動(dòng)目標(biāo)Activity,表現(xiàn)行為和前面提到的singleTask模式下的行為一樣。

·FLAG_ACTIVITY_SINGLE_TOP:如果目標(biāo)Activity正好位于堆棧的頂部,則系統(tǒng)不用新建Activity的實(shí)例并使用onNewIntent()方法來(lái)處理接收到的消息。表現(xiàn)行為和前面提到的singleTop模式下的行為一樣。

·FLAG_ACTIVITY_CLEAR_TOP:如果目標(biāo)Activity的運(yùn)行實(shí)例已經(jīng)存在,使用此方法系統(tǒng)將會(huì)清除目標(biāo)Activity所處的堆棧上面的所有Activity實(shí)例。

需要注意的是,官方文檔中建議多使用默認(rèn)的Task行為模式,因?yàn)樵撃J奖容^簡(jiǎn)單也易于調(diào)試。對(duì)于一些特殊的需求,如果需要使用到其他模式的話,需要模擬不同的情況多進(jìn)行一些測(cè)試,以防止在一些特殊情況下出現(xiàn)不符合預(yù)期的情況。當(dāng)然,說(shuō)句實(shí)話,目前主流移動(dòng)設(shè)備上的Android版本都還比較舊,對(duì)多任務(wù)管理的支持和體現(xiàn)還不夠明顯,不過(guò),我們應(yīng)該可以在Android最新版本(如Android 4.0)里看到對(duì)系統(tǒng)任務(wù)管理功能的加強(qiáng)。

主站蜘蛛池模板: 苍南县| 吴桥县| 江孜县| 贺兰县| 栾川县| 阿合奇县| 桐庐县| 凤凰县| 化隆| 江阴市| 楚雄市| 庆阳市| 龙江县| 德惠市| 通许县| 沙洋县| 苍溪县| 德昌县| 祥云县| 兴义市| 昭通市| 秦安县| 武强县| 玉田县| 洪泽县| 河北省| 莱芜市| 金华市| 柏乡县| 息烽县| 中方县| 台安县| 迭部县| 常州市| 淳安县| 云南省| 汪清县| 临武县| 前郭尔| 上饶县| 会理县|