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

2.3 Android應用框架

前面介紹了Android的系統框架,主要目的是讓大家對Android系統有整體的概念,也為日后更深入的學習打好基礎。然而,目前我們更需要重點學習和掌握的則是Android的應用框架,因為是否能掌握和理解Android應用框架,直接關系到是否能學好Android應用開發。

Android的應用框架是一個龐大的體系,想要理解透徹并不是那么簡單的事情,但是,好在其中有一些比較清晰的脈絡可以幫助我們快速地熟悉這個系統,因此抓住這些脈絡中的核心要點對于能否學好Android的應用開發來說是至關重要的。一般來說,Android應用框架中包含四個核心要點,即活動(Activity)、消息(Intent)、視圖(View)和任務(Task)。

如果你覺得上述核心要點的概念很陌生,不好理解,那么我們來看看下面這個比喻:如果把一個Android應用比喻成海洋,那么每個Activity就是這個海洋中的島嶼,假設我們眼前有一項任務(也就是Task),需要我們在其中若干個島嶼上建立起自己的王國。于是問題來了,我們要怎么樣從一座島嶼去到另一座島嶼呢?沒錯,我們需要交通工具,而Intent就是我們最重要的交通工具。當然,Intent不僅可以帶我們去,而且還可以幫我們帶上很多需要的東西。接著,到了島上,我們開始建立一個自己的王國,要知道這可需要很多的資源,這個時候,我們就會想到View這個建筑公司,因為他可以幫助我們快速地建出我們需要的東西。這樣,Activity、Intent、View以及Task一起配合完成了一個完整的Android應用的王國。

從以上的比喻中,我們還可以認識到,在這四個要點中,Activity是基礎,Intent是關鍵,View是必要工具,而Task則是開發的脈絡。對于開發者來說,只有掌握了Activity、Intent、View和Task這幾個核心要素之后,才能夠做出多種多樣的應用程序。接下來,讓我們分別學習一下這四個核心要點。

2.3.1 活動(Activity)

活動(Activity)是Android應用框架最基礎、最核心的內容和元素,每個Android應用都是由一個或者若干個Activity構成的。在Android應用系統中,Activity的概念類似于界面,而Activity對象我們通常稱之為“界面控制器”(從MVC的角度來說)。從另一個角度來理解,Activity的概念比較類似于網站(Web)開發中“網頁”的概念。此外,當Android應用運行的時候,每個Activity都會有自己獨立的生命周期,圖2-2所示的就是Activity的生命周期。

圖2-2 Activity生命周期

其實,在Android系統內部有專門的Activity堆棧(Stack)空間,用于存儲多個Activity的運行狀態。一般來說,系統會保證某一時刻只有最頂端的那個Activity是處于前端的活動(foreground)狀態。也正因如此,一個Activity才會有如圖2-2所示的生命周期。當一個Activity啟動并進入活動狀態的時候,調用順序是onCreate、onStart、onResume;退居后臺的時候,調用順序是onPause、onStop;重新回到活動狀態的時候,調用順序是onRestart、onStart、onResume;銷毀的時候,調用順序是onPause、onStop、onDestroy。我們應該深刻理解這些狀態的變化過程,因為在Android應用開發的過程中我們會經常用到。至于如何更好地掌握Activity的特性,大家可以嘗試將以下代碼(代碼清單2-1)放入Android應用中運行,并對照程序打印出的調試信息來理解Activity生命周期各階段的使用。

代碼清單 2-1

// 基礎Activity類,用于測試
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必須在項目基礎配置文件AndroidManifest.xml中聲明,這樣Activity才可以被Android應用框架所識別;如果你只寫了Java代碼而不進行聲明的話,運行時就會拋出ActivityNotFoundException異常。關于Activity聲明的具體操作,我們會在2.10.2節中結合Hello World項目進行詳細介紹。

2.3.2 消息(Intent)

參考之前我們對Android應用框架的幾個核心要點的比喻,我們應該知道Intent消息模塊對于Android應用框架來說有多重要;如果沒有它的話,Android應用的各個模塊就像一座座“孤島”,根本不可能構成一個完整的系統。在Android應用系統中,我們常常把Intent稱為消息,實際上,Intent本身還是一個對象,里面包含的是構成消息的內容和屬性,主要有如下幾個屬性,我們來分別認識一下。

1.組件名稱(ComponentName)

對于Android系統來說,組件名稱實際上就是一個ComponentName對象,用于指定Intent對應的目標組件,Intent對象可以通過setComponent、setClass或者setClassName方法來進行設置。

2.動作(Action)

消息基類(Intent)中定義了各種動作常量(字符串常量),其中比較常見的有:ACTION_MAIN(對應字符串android.intent.action.MAIN)表示應用的入口的初始化動作;ACTION_EDIT(對應字符串android.intent.action.EDIT)表示常見的編輯動作;ACTION_CALL(對應字符串android.intent.action.CALL)則表示用于初始化電話模塊動作等。Intent對象常使用setAction方法來設置。

3.數據(Data)

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

4.類別(Category)

既然不同的動作應該對應不同的數據類型,那么不同的動作也應該由不同的類別的Activity組件來處理,比如CATEGORY_BROWSABLE表示該Intent應該由瀏覽器組件來打開,CATEGORY_LAUNCHER表示此Intent由應用初始化Activity處理;而CATEGORY_PREFERENCE則表示處理該Intent的應該是系統配置界面。此外,消息對象(Intent)可以使用addCategory添加一種類型,而一個Intent對象也可以包含多種類型屬性。

5.附加信息(Extras)

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

6.標志(Flags)

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

在Android應用中,消息(Intent)的使用方式通常有兩種,一是顯式消息(Explicit Intent),另一個則是隱式消息(Implicit Intent)。顯式消息的使用比較簡單,只需要在Intent中指定目標組件名稱(也就是前面提到的ComponentName屬性)即可,一般用于目標Activity比較明確的情形。比如在一個固定流程中,我們需要從一個Activity跳轉到另一個,那么我們就會使用顯式的消息。而隱式消息則比較復雜一點,它需要通過消息過濾器(IntentFilter)來處理,一般用于目的性不是那么明確的情形,比如應用中的某個功能需要往目的地發送消息,但是我們卻不確定要使用短信發送還是微博發送,那么這個時候就應該使用隱性消息來處理了。下面是一個典型的消息過濾器的配置范例,如代碼清單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>

我們看到,配置消息過濾器使用的是標簽,一般需要包含三個要素:action、category以及data。其中,action是必需的,category一般也是需要的,而data則允許沒有設置。接下來,我們學習一下這幾個要素的使用方法。

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

·<category/>:<category/>元素用于標注消息的類別。值得注意的是,假如我們使用<category/>元素來標識消息類別,系統在調用Context.startActivity方法或者Context.startActivityForResult方法時都會自動加上DEFAULT類別。因此,除了Intent已經指定為Intent.ACTION_MAIN以外,我們還必須指定<category/>為android.intent.category.DEFAULT,否則該消息將不會被匹配到。另外,對于Service和BroadcastReceiver,如果Intent中沒有指定<category/>,那么在其消息過濾器中也不必指定。

·<data/>:通過data字段來匹配消息相對來講比較復雜,通常的data字段包含uri、scheme(content,file,http)和type(mimeType)幾種字段。對于Intent來說,我們可以使用setData和setType方法來設置,對于IntentFilter來講,則可以通過android:scheme和android:mimeType屬性分別來指定,使用范例如代碼清單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可以發送圖片,而且內容必須是單獨的一個文件,也就是說,該文件的URI路徑必須是以“file://”開頭的。當然,如果我們把這里的“android:scheme”改成“content”的話,則表明該圖片內容必須是由ContentProvider提供的,即URI必須是以“content://”開頭的。

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

2.3.3 視圖(View)

視圖(View)系統主管Android應用的界面外觀顯示,因此也稱作Android UI系統,是Android應用框架中最重要的組成部分之一。我們在Activity中展示或者操作的幾乎所有控件都屬于View。Android應用框架的View System包含View和ViewGroup兩類基礎組件。下面我們來理解一下Android視圖系統的層次結構,如圖2-3所示。

圖2-3 Android視圖系統的層次結構

視圖類(View)是所有視圖(UI)控件(包括ViewGroup)的基類。視圖組(ViewGroup)則類似于集合,一個視圖組可以包含多個ViewGroup和View,類似于Html標簽中的層(div)。接下來,我們再來看看View中會經常使用的一些UI控件(見表2-3),你也可以在Android SDK參考文檔(Reference)中的android.widget包下找到它們。

從表2-3中可以看出,Android應用框架為我們提供了非常豐富的視圖控件,從某種程度上來說,Android應用的界面是通過各種各樣的視圖控件組合起來的。至于這些視圖控件的具體用法,我們將在第7章中結合項目實例進行介紹。

表2-3 Android主要UI控件

本節只是從應用程序框架組成部分的角度簡單地介紹了Android UI系統的概念,關于UI系統的更多知識以及UI控件的具體用法,我們將在本章2.7節中更系統地介紹。

2.3.4 任務(Task)

本節介紹Android任務(Task)的概念。區別于以上介紹的活動、消息和視圖這幾個要點,任務的概念顯得比較抽象,且我們在日常編碼過程中也不會直接接觸到,但是,理解任務卻是理解整個Android應用框架的關鍵。

首先,我們來認識一下Android系統中的任務是如何運行的。簡單來說,當我們在手機的應用列表(Application Launcher)中點擊某個應用圖標的時候,一個新的Task就啟動了,后面的操作可能會涉及多個應用中不同Activity的界面,而這些Activity的運行狀態都會被存儲到Task的Activity堆棧(Activity Stack)中去。和其他的堆棧一樣,Activity堆棧采用的是“后進先出”的規則。圖2-4展示就是一個常見任務中Activity堆棧的變化情況。

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

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

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

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

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

·Standard模式:Standard模式為默認模式,無論是打開一個新的Activity,還是接收Intent消息,系統都會為這個Activity創建一個新的實例(instance);每個Activity都可以被實例化多次,并且每個任務都可以包含多個實例。此模式最常用,但是其缺點就是太耗費系統資源。

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

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

·singleInstance模式:此模式與singleTask模式類似,不同之處是該模式保證Activity獨占一個Task,其他的Activity都不能存在于該任務的Activity堆棧中。當然,Activity接收Intent消息也是通過onNewIntent方法實現。

此外,我們還可以通過設置Intent消息的flag標志來主動改變Activity的調用方式,比較常見的flag如下。

·FLAG_ACTIVITY_NEW_TASK:在新的Task中啟動目標Activity,表現行為和前面提到的singleTask模式下的行為一樣。

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

·FLAG_ACTIVITY_CLEAR_TOP:如果目標Activity的運行實例已經存在,使用此方法系統將會清除目標Activity所處的堆棧上面的所有Activity實例。

需要注意的是,官方文檔中建議多使用默認的Task行為模式,因為該模式比較簡單也易于調試。對于一些特殊的需求,如果需要使用到其他模式的話,需要模擬不同的情況多進行一些測試,以防止在一些特殊情況下出現不符合預期的情況。當然,說句實話,目前主流移動設備上的Android版本都還比較舊,對多任務管理的支持和體現還不夠明顯,不過,我們應該可以在Android最新版本(如Android 4.0)里看到對系統任務管理功能的加強。

主站蜘蛛池模板: 抚顺市| 阿图什市| 资阳市| 安阳县| 吉安市| 常德市| 太湖县| 洛浦县| 金塔县| 泗水县| 道孚县| 秀山| 康乐县| 错那县| 中卫市| 哈密市| 腾冲县| 澳门| 寻甸| 商都县| 兴和县| 黄冈市| 建湖县| 克拉玛依市| 当雄县| 阳西县| 永嘉县| 天柱县| 霍山县| 会东县| 商都县| 亳州市| 盐津县| 长治市| 江安县| 新巴尔虎左旗| 都匀市| 乌海市| 成都市| 锡林浩特市| 通海县|