Android 元件 — Activity (建立與啟動)
Activity 簡介
Activity 是應用程式的四大元件之一,為使用者提供一個互動介面,比如打電話、發簡訊、傳送郵件等。每一個Activity都會提供一個視窗用於繪製使用者介面。通常視窗都是全屏,但是也可以浮在其他的視窗之上。
app一般由一個或者多個Activity鬆散耦合在一起。最常見的情況是,一個Activity被指定為“mian”Activity,作為使用者啟動app時最先進入的介面。Activity可以啟動其他的Activity介面來滿足不同德操作。每次開啟一個新的Activity時會先關閉當前的Activity,然後將其儲存在“Back Stack”(類似先進後出的棧結構),預設情況下,被關閉的Activity會被放在Back Stack中,當用戶按下返回鍵時,以前的Activity會從棧頂彈出並獲得使用者的焦點。(關於Back
Stack的部分會在
當一個Activity因為新開啟一個Activity是被關閉,他會將Activity狀態的變化通知Activity的“生命週期回撥”方法。Activity的生命週期被分為幾個狀態:creation、started、stopped、pause、resume、destroy,每個狀態會對應一個回撥方法。系統會在Activity狀態發生改變的時候回撥這些方法,這樣你就有機會指定一些適當的程式碼。舉個例子,當你關閉一個Activity的時候你必須釋放一些太好記憶體的物件,比如網路、資料庫連線等。當Activity恢復時,你可以再次申請那些必要的資源來恢復之前被中斷的操作。
這篇文章剩餘的部分會討論如何建立和使用一個Activity,包括Activity生命週期的狀態切換,以及如何處理狀態的變換。
Activity的建立
首先你需要建立一個子類來繼承Activity。在子類中需要實現回撥方法以提供給系統在不同的狀態下呼叫,其中最重要的兩個回撥函式如下:
- 這個方法你必須實現。系統在Activity被建立的時候會呼叫此方法。在此方法中你應用初始化Activity必須用到的資源。最終要的時候你需要呼叫setContentView()設定Layout檔案來填充你的使用者介面。
- 當用戶離開當前的介面時會呼叫此方法,雖然不一定會終止Activity,但是有可能此介面不會在恢復,所以你需要儲存一些使用者的設定,以保證使用者的設定生效。
還有一些其他的生命週期回撥方法,你應該合理的運用這些回撥方法,保證在正不同Activity切換和Activity被異常中斷的情況下仍然有一個流暢的使用者體驗,稍後會在Managing the Activity Lifecycle 一節中詳細的討論所有的回撥方法。
實現使用者介面
一個使用者介面由不同級別的檢視(view)組成—view是繼承於View類的物件。每一個檢視都控制者Activity視窗中的一個矩形區域,而且可以與使用者互動。舉個例子,一個檢視可能是一個按鈕,當用戶點選時可以觸發某個操作。
Android 提供了許多現成的檢視(view),你可以用來設計和組織你layout(佈局)。“Widgets”是view的別稱,它提供了一個在螢幕視覺化的元素,比如一個按鈕(button)、文字框(Text filed)、單選框(checkbox)、或者僅僅是一副影象(image)。“Layout”繼承於ViewGroup,為view提供了一個特有的佈局模型,比如線性佈局(LinearLayout)、網格佈局(grid
Layout)、相對佈局(relative Layout)。當然,你也可以直接繼承 View
和
類來建立自己的widget和layout,然後應用到Activity的佈局中。
一般會在app的資原始檔中定義一個XML Layout檔案。通過這個方法,你可以將Activity的行為與使用者的介面分開維護。每一個佈局檔案都有一個ID,你可以呼叫setContentView(layout ID)方法為你的Activity設定佈局檔案,你也可以在程式碼中(分級式地)建立view並插入到ViewGroup中,然後將ViewGroup作為layout引數傳遞給setContentView()。
在AndroidManifest.xml中宣告Activity
為了保證系統能夠訪問你的Activity,你需要在AndroidManifest.xml檔案中進行宣告。宣告方式如下:開啟工程目中的AndroidManifest.xml檔案,在<application>元素中新增一個子元素<activity>:
<manifest ... >
<application ... >
<activity android:name=".ExampleActivity" />
...
</application ... >
...
</manifest >
<activity>元素下還可以增加其他屬性,比如增加一個標籤、定義Activity的風格或主題。android:name
屬性是必須設定的,它指明瞭Activity的類名。一旦你釋出app,你的類名就不能隨意的更改,否則會破壞一些功能,比如已經建立好的app快捷方式,如果你更改了android:name
,快捷方式就會失效。(read
the blog post,
Things That Cannot Change).
“intent filter”的使用
AndroidManifest.xml檔案中,為了方便其他的app能夠啟用你的activity,可以在<activity>
增加<intent-filter>
元素。
當你用Android SDK tools建立一個app時,它會自動為你建立一個放在“launcher” 類中的activity的存根,以便響應“main”action,inten filter的使用方式如下:
<activity android:name=".ExampleActivity" android:icon="@drawable/app_icon">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<action>
標識了app的“main”入口,<category>
表示activity會被放在系統的ApplicationLauncher中,以便使用者啟動(launch)這個activity。
如果你打算只在當前app中使用該activity,而且不允許其他app啟用該activity,那你就不需要任何intent filter。如同上面的例子,只能有一個activity的intent filter 能使用者“main”action和“launcher”category。你不需要其他app訪問的activity則不需要宣告intent filter,該activity所在的app只需要指定activity類名就可以啟動了。
啟動Activity
你可以呼叫startActivity((Intent intent, Bundle options))來啟動其他的Activity,intent用於描述想要啟動的Activity。intent可以是明確指定啟動的Activity類名,或者描述你要啟動的動作(系統會根據你的描述為你選擇一個合適的Activity,甚至從不同app查詢也是有可能的)。intent也可以攜帶少量的資料傳遞給需要啟動的Activity。
在Activity內部啟動當前app中的其他Activity可以直接在intent中指定類名,方式如下:
Intent intent = new Intent(this, SignInActivity.class);
startActivity(intent);
然而,你的app執行的某些操作,比如打電話、發簡訊、檢視地圖,但是你的app可能沒有完成這些功能的Activity,這個時候你可以利用裝置上其他具備這些功能的Activity來完成這些操作。intent真正的價值在於,你只需要描述你想要做的操作,系統會自動為你匹配合適的Activity,如果存在相同功能的Activity的時候,會再提示使用者選擇。舉個例子,如果你允許使用者發郵件,你可以像下面這樣建立一個intent:
Intent intent = new Intent(Intent.ACTION_SEND);
intent.putExtra(Intent.EXTRA_EMAIL, recipientArray);
startActivity(intent);
putExtra中recipientArray是一個email 地址的字串,當帶有email功能的app響應這個intent的時候,它會解析recipientArray中的內容然後放到收件人的位址列裡。這種情況下,email 傳送後會再恢復你之前的Activity。
Activity 的反饋機制
有時候,你可能希望啟動的Activity完成操作後能回傳資料。這種情況下,你可以呼叫startActivityForResult()來啟動Activity(而不是呼叫startActivity())。為了能接收Activity處理的結果,你需要實現回撥方法:onActivityResult()。當被啟動的Activity推出後會返回一個intent給onActivityResult()。
舉個例子,可能你想要使用者選擇一個聯絡人,然後你的Activity就可以使用聯絡人資訊做一些事情。上程式碼:
private void pickContact() {
// Create an intent to "pick" a contact, as defined by the content provider URI
Intent intent = new Intent(Intent.ACTION_PICK, Contacts.CONTENT_URI);
startActivityForResult(intent, PICK_CONTACT_REQUEST);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// If the request went well (OK) and the request was PICK_CONTACT_REQUEST
if (resultCode == Activity.RESULT_OK && requestCode == PICK_CONTACT_REQUEST) {
// Perform a query to the contact's content provider for the contact's name
Cursor cursor = getContentResolver().query(data.getData(),
new String[] {Contacts.DISPLAY_NAME}, null, null, null);
if (cursor.moveToFirst()) { // True if the cursor is not empty
int columnIndex = cursor.getColumnIndex(Contacts.DISPLAY_NAME);
String name = cursor.getString(columnIndex);
// Do something with the selected contact's name...
}
}
}