1. 程式人生 > >Android官方文件—APP元件(Activities)(概述)

Android官方文件—APP元件(Activities)(概述)

Activities

activity是一個應用程式元件,它提供了一個螢幕,使用者可以通過該螢幕進行互動以執行某些操作,例如撥打電話,拍照,傳送電子郵件或檢視地圖。每個activity都有一個視窗,用於繪製其使用者介面。視窗通常填滿螢幕,但也可能比螢幕小,或者漂浮在其他視窗的頂部。

應用程式通常由多個彼此獨立的activity組成。通常,應用程式中的一個activity被指定為“main”activity,該activity在首次啟動應用程式時呈現給使用者。然後,每個activity可以啟動另一個activity以執行不同的操作。每次新活動start時,前一個活動都會stopped,但系統會將前一個activity保留在堆疊中(“回退棧”)。當一個新的活動start時,它會被push到回退棧頂,並取得使用者的焦點。回退棧遵循基本的“後進先出”堆疊機制,因此,當用戶完成當前活動並按下後退按鈕時,它將從堆疊中彈出(並銷燬)並恢復先前的活動。 (後臺堆疊將在“任務和後臺堆疊”文件中進行更多討論。)

當activity因新activity啟動而停止時,會通過activity的生命週期回撥方法通知此狀態更改。由於狀態的變化,activity可能會收到多種回撥方法 - 系統是否正在建立,停止,恢復或銷燬它 - 每次回撥都為您提供執行適合的特定工作的機會。例如,當activity停止時,您的activity應釋放任何大型物件,例如網路或資料庫連線。當activity恢復後,您可以重新獲取必要的資源並恢復已中斷的操作。這些狀態轉換都是activity生命週期的一部分。

本文件的其餘部分討論瞭如何構建和使用activity的基礎知識,包括完整討論activity生命週期的工作方式,以便您可以正確管理各種activity狀態之間的轉換。

建立一個activity

要建立活動,您必須建立Activity的子類(或它的現有子類)。在子類中,您需要實現當活動在其生命週期的各個狀態之間轉換時系統呼叫的回撥方法,例如在建立,停止,恢復或銷燬活動時。兩個最重要的回撥方法是:

您必須實現此方法。系統在建立activity時呼叫此方法。在您建立時,您應該初始化activity的基本組成部分。最重要的是,您必須呼叫setContentView()來定義activity使用者介面的佈局。

當用戶焦點從您的activity離開時(並不意味著你的activity被銷燬),系統第一時間會呼叫此方法。通常你需要在此方法中儲存使用者會話的狀態(因為使用者可能不在返回)。

您還應該使用其他幾種生命週期回撥方法,以便在activity之間提供流暢的使用者體驗,並處理導致您的activity被停止甚至銷燬的意外中斷。所有生命週期回撥方法將在後面的“管理活動生命週期”一節中討論。

實現使用者介面

activity的使用者介面由從View類派生的檢視物件層次結構提供。每個檢視控制activity視窗中的特定矩形空間,並可響應使用者互動。例如,檢視可以是在使用者觸控動作時啟動動作的按鈕。

Android提供了許多現成的檢視,您可以使用它們來設計和組織您的佈局。“Widgets”是為螢幕提供可視(和互動)元素的檢視,例如按鈕,文字欄位,複選框或影象。“佈局”是從ViewGroup派生的檢視,為其子檢視提供獨特的佈局模型,例如線性佈局,網格佈局或相對佈局。您還可以將View和ViewGroup類(或現有子類)子類化,以建立自己的Widgets和佈局,並將它們應用於您的activity佈局。

使用檢視定義佈局的最常用方法是使用儲存在應用程式資源中的XML佈局檔案。這樣,您可以將使用者介面的設計與定義activity行為的原始碼分開維護。您可以使用setContentView()將佈局設定為activity的UI,通過傳遞佈局的資源ID。但是,您還可以在activity程式碼中建立新檢視,並通過將新檢視插入ViewGroup來構建檢視層次結構,然後通過將根ViewGroup傳遞給setContentView()來使用該佈局。

有關建立使用者介面的資訊,請參閱使用者介面文件。

在清單中宣告activity

您必須在清單檔案中宣告您的activity,以便系統可以訪問它。要宣告您的activity,請開啟您的清單檔案並新增<activity>元素作為<application>的子元素。例如:

<manifest ... >
  <application ... >
      <activity android:name=".ExampleActivity" />
      ...
  </application ... >
  ...
</manifest >

您可以在此元素中包含其他幾個屬性,以定義屬性例如activity的標籤,activity的圖示或用於設定activityUI樣式的主題。android:name屬性是唯一必需的屬性 - 它指定activity的類名。釋出應用程式後,不應更改此名稱,因為如果這樣做,您可能會破壞某些功能,例如應用程式快捷方式(閱讀部落格文章,無法更改的內容)。

請參閱<activity>元素,以獲取有關在清單中宣告activity的更多資訊。

使用意圖過濾器

<activity>元素也可以指定各種意圖過濾器 - 使用<intent-filter>元素 - 以宣告其他應用程式元件如何啟用它。使用Android SDK工具建立新應用程式時,為您建立的第一個activity會自動包含一個意圖過濾器,該過濾器宣告activity響應“主”入口並應放在“啟動器”類別中。 intent過濾器如下所示:

<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>元素指定這是應用程式的“主要”入口點。<category>元素指定此activity應列在系統的應用程式啟動器中(以允許使用者啟動此activity)。

如果您希望自己的應用程式是封閉的,並且不允許其他應用程式啟用其activity,那麼您不需要任何其他意圖過濾器。只有一個活動應具有“Main”action和“啟動器”類別,如上例所示。您不想其他應用程式訪問的activity不應該設定intent過濾器,您可以使用顯式意圖自行啟動它們(如下一節中所述)。

但是,如果您希望activity響應從其他應用程式(以及您自己的應用程式)提供的隱式意圖,則必須為您的activity定義其他意圖過濾器。對於您要響應的每種型別的意圖,您必須包含<intent-filter>包括<action>元素和(可選)<category>元素和/或<data>元件。這些元素指定您的activity可以響應的意圖型別。

有關活動如何響應意圖的更多資訊,請參閱意圖和意圖過濾器文件。

啟動Activity

您可以通過呼叫startActivity()來啟動另一個activity,並向其傳遞一個描述您要啟動的activity的Intent。intent指定要啟動的確切activity或描述您要執行的操作型別(系統為您選擇適當的activity,甚至可以來自不同的應用程式)。意圖還可以攜帶少量資料以供啟動的activity使用。

在您自己的應用程式中工作時,您通常需要簡單地啟動已知activity。您可以通過使用類名建立一個明確定義要啟動的活動的intent來完成此操作。例如,以下是一個activity如何啟動另一個名為SignInActivity的activity:

Intent intent = new Intent(this, SignInActivity.class);
startActivity(intent);

但是,您的應用程式可能還希望使用activity中的資料執行某些操作,例如傳送電子郵件,簡訊或狀態更新。在這種情況下,您的應用程式可能沒有自己的activity來執行此類操作,因此您可以利用裝置上其他應用程式提供的activity,這些activity可以為您執行操作。這是意圖非常有價值的地方 - 您可以建立描述您要執行的操作的意圖,並且系統從另一個應用程式啟動相應的activity。如果有多個activity可以處理意圖,那麼使用者可以選擇使用哪個activity。例如,如果要允許使用者傳送電子郵件,可以建立以下意圖:

Intent intent = new Intent(Intent.ACTION_SEND);
intent.putExtra(Intent.EXTRA_EMAIL, recipientArray);
startActivity(intent);

新增到intent中的EXTRA_EMAIL extra傳送到的電子郵件地址的字串陣列。當電子郵件應用程式響應此意圖時,它會讀取extra中提供的字串陣列,並將它們放在電子郵件撰寫表單的“to”欄位中。在這種情況下,電子郵件應用程式的activity開始,當用戶操作完成後,您的activity將被恢復。

啟動activity並接收返回值

有時,您可能希望從您啟動的activity中接收結果。在這種情況下,通過呼叫startActivityForResult()(而不是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...
        }
    }
}

此示例顯示了您應該在onActivityResult()方法中使用的基本邏輯,以便處理activity的返回結果。 第一個條件檢查請求是否成功 - 如果是,則resultCode將是RESULT_OK- 在這種情況下,requestCode與startActivityForResult()傳送的第二個引數匹配來確定此結果響應的是否為已知請求 。這樣一來,程式碼可以通過查詢Intent(data引數)中返回的資料來處理activity返回的結果。

接下來,ContentResolver對content provider執行查詢,該查詢返回用於讀取查詢資料的Cursor。有關更多資訊,請參閱content provider文件。

有關使用意圖的更多資訊,請參閱意圖和意圖過濾器文件。

關閉Activity

您可以通過呼叫finish()方法來關閉activity。您還可以通過呼叫finishActivity()來關閉先前啟動的某個activity。

注意:在大多數情況下,您不應使用這些方法顯式結束activity。如以下有關activity生命週期的部分所述,Android系統會為您管理activity的生命週期,因此您無需結束自己的activity。

Activity生命週期管理

通過實現回撥方法來管理activity的生命週期對於開發強大而靈活的應用程式至關重要。activity的生命週期直接受其與其他activity,其任務和後臺堆疊的關聯的影響。

活動可以基本上存在於三種狀態:

Resumed

活動位於螢幕的前景並具有使用者焦點。 (這種狀態有時也被稱為“running”。)

Paused

另一個activity是在前臺並具有焦點,但這一個activity仍然可見。也就是說,另一個活動在這個activity的頂部,該activity部分透明或不覆蓋整個螢幕。暫停的activity完全處於活動狀態(Activity物件保留在記憶體中,它保留所有狀態和成員資訊,並保持附加到視窗管理器),但可以在極低記憶體情況下被系統殺死。

Stopped

該activity完全被另一個activity遮擋(activity現在位於“background”中)。已停止的activity仍處於活動狀態(Activity物件保留在記憶體中,它維護所有狀態和成員資訊,但未附加到視窗管理器)。但是,它不再對使用者可見,並且當其他地方需要記憶體時,它可能被系統殺死。

如果activity暫停或停止,系統可以通過請求結束它(呼叫其finish()方法)或簡單地終止其程序來從記憶體中刪除它。當活動再次開啟時(在完成或殺死之後),必須全部建立它。

實現生命週期回撥

當activity進入和退出上述不同狀態時,通過各種回撥方法通知它。所有回撥方法都是關聯的,您可以覆蓋這些方法,以便在activity狀態發生變化時執行適當的工作。以下是activity包括每個基本生命週期方法:

public class ExampleActivity extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // The activity is being created.
    }
    @Override
    protected void onStart() {
        super.onStart();
        // The activity is about to become visible.
    }
    @Override
    protected void onResume() {
        super.onResume();
        // The activity has become visible (it is now "resumed").
    }
    @Override
    protected void onPause() {
        super.onPause();
        // Another activity is taking focus (this activity is about to be "paused").
    }
    @Override
    protected void onStop() {
        super.onStop();
        // The activity is no longer visible (it is now "stopped")
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        // The activity is about to be destroyed.
    }
}

注意:在執行任何工作之前,必須對這些生命週期方法呼叫超類實現,如上面的示例所示。

總之,這些方法定義了activity的整個生命週期。通過實現這些方法,您可以在活動生命週期中監視三個巢狀的迴圈:

  • 一個activity的整個生命週期發生在對onCreate()的呼叫和對onDestroy()的呼叫之間。您的activity應在onCreate()中執行“全域性”狀態(如定義佈局)的設定,並在onDestroy()中的釋放所有剩餘資源。例如,如果您的activity有一個在後臺執行的執行緒從網路下載資料,它可能會在onCreate()中建立該執行緒,然後在onDestroy()中停止該執行緒。
  • activity的可見生命週期發生在對onStart()的呼叫和對onStop()的呼叫之間。在此期間,使用者可以在螢幕上看到activity並與之互動。例如,當一個新activity開始導致這個activity不再可見時,會呼叫onStop()。在這兩種方法之間,您可以維護向使用者顯示activity所需的資源。例如,您可以在onStart()中註冊BroadcastReceiver以監視影響UI的更改,並在使用者無法再看到您正在顯示的內容時在onStop()中取消註冊。系統可能會在activity的整個生命週期內多次呼叫onStart()和onStop(),因為activity在對使用者可見和隱藏之間交替出現。
  • activity的前臺生命週期發生在對onResume()的呼叫和對onPause()的呼叫之間。在此期間,activity位於螢幕上的所有其他activity之上,並且具有使用者輸入焦點。activity可以頻繁地進出前臺 - 例如,當裝置進入睡眠狀態或出現對話方塊時,會呼叫onPause()。因為這種狀態可以經常轉換,所以這兩種方法中的程式碼應該相當輕量級,以避免使使用者等待。

圖1說明了這些迴圈以及activity在狀態之間可能採用的路徑。矩形表示當activity在狀態之間轉換時可以實現的回撥方法。

圖1.activity生命週期。

表1中列出了相同的生命週期回撥方法,它們更詳細地描述了每個回撥方法,並在activity的整個生命週期中定位每個回撥方法,包括系統是否可以在回撥方法完成後結束activity。

表1.activity生命週期的回撥方法的摘要。

標題為“Killable after?”的列表示系統是否可以在方法返回後隨時終止託管activity的程序,而不會執行activity的其他程式碼。三種方法標記為“是”:( onPause(),onStop()和onDestroy())。因為onPause()是三者中的第一個,所以一旦建立了activity,如果系統必須在緊急情況下恢復記憶體,onPause()是在程序被殺死之前保證被呼叫的最後一個方法 ,然後可能不會呼叫onStop()和onDestroy()。因此,您應該使用onPause()將關鍵的持久資料(例如使用者編輯)寫入儲存。但是,您應該選擇在onPause()期間只儲存必要資訊,因為此方法中的任何緩慢過程都會影響過渡到下一個activity從而降低使用者體驗。

在Killable列中標記為“No”的方法可以保護託管activity的程序免於被殺死。因此,從onPause()返回到呼叫onResume()的時間,activity是可被殺死的。在再一次呼叫onPause()並返回之前,它不會再次被殺。

注意:表1中這個定義在技術上不“可殺”的activity可能仍然被系統殺死 - 但這隻會在資源極端情況下發生。當一個活動是否可能被殺死時,將在“程序和執行緒”文件中進一步討論。

儲存activity狀態

管理activity生命週期的簡介簡要提到,當activity暫停或停止時,activity的狀態將保留。因為Activity物件在暫停或停止時仍保留在記憶體中 - 有關其成員和當前狀態的所有資訊仍然存在。因此,保留使用者在activity中所做的任何更改,以便當activity返回到前臺時(當它“恢復”時),那些更改仍然存在。

但是,當系統銷燬activity以恢復記憶體時,Activity物件將被銷燬,因此係統不能簡單地恢復它的狀態。相反,如果使用者導航回到它,系統必須重新建立Activity物件。然而,使用者並不知道系統破壞了活動並重新建立了activity, thus,probably expects the activity to be exactly as it was.。在這種情況下,您可以通過實現一個額外的回撥方法來確保保留有關activity狀態的重要資訊,該方法允許您儲存有關activity狀態的資訊:onSaveInstanceState()。

在activity可能被銷燬之前,系統會呼叫onSaveInstanceState(),系統將Bundle方法傳遞給該方法,在該方法中,您可以使用putString()和putInt()等方法將有關activity的狀態資訊以鍵值對形式儲存。然後,如果系統終止您的應用程式程序後用戶導航回您的activity,系統將重新建立activity並將Bundle傳遞給onCreate()和onRestoreInstanceState()。使用這些方法之一,您可以從Bundle中提取已儲存的狀態並恢復activity狀態。如果沒有要恢復的狀態資訊,則傳遞給您的Bundle為null(這是第一次建立活動時的情況)。

圖2.activity返回到使用者焦點且狀態完好無損的兩種方式:activity被銷燬,然後重新建立,activity必須恢復先前儲存的狀態,或activity停止,然後恢復,activity狀態仍然完整存在。

注意:無法保證在銷燬activity之前一定呼叫onSaveInstanceState(),因為在某些情況下不需要儲存狀態(例如當用戶使用“返回”按鈕離開activity時,因為使用者是明確關閉活動)。如果系統呼叫onSaveInstanceState(),它會在onStop()之前呼叫,也可能在onPause()之前執行。

但是,即使您不執行任何操作並且未實現onSaveInstanceState(),Activity類的預設實現onSaveInstanceState()也會恢復某些activity狀態。具體來說,預設實現為佈局中的每個View呼叫相應的onSaveInstanceState()方法,這允許每個檢視提供有關儲存的自身的資訊。幾乎Android框架中的每個控制元件都會適當地實現此方法,以便在重新建立活動時自動儲存和恢復對UI的任何可見更改。例如,EditText控制元件儲存使用者輸入的任何文字,CheckBox控制元件儲存是否已選中。您需要的唯一工作是為要儲存其狀態的每個小部件提供唯一的ID(帶有android:id屬性)。如果控制元件沒有ID,則系統無法儲存其狀態。

因為onSaveInstanceState()的預設實現有助於儲存UI的狀態,所以如果重寫方法以儲存其他狀態資訊,則應該在執行任何工作之前必須呼叫onSaveInstanceState()的超類實現。同樣,如果覆蓋它,也應該呼叫onRestoreInstanceState()的超類實現,因此預設實現可以恢復檢視狀態。

注意:因為無法保證呼叫onSaveInstanceState(),所以您應該僅使用它來記錄activity的瞬態(UI的狀態) - 您永遠不應該使用它來儲存持久資料。相反,您應該使用onPause()在使用者離開activity時儲存持久資料(例如應儲存到資料庫的資料)。

測試應用程式恢復其狀態的能力的一種好方法是簡單地旋轉裝置以使螢幕方向發生變化。當螢幕方向改變時,系統會銷燬並重新建立activity,以便應用可能適用於新螢幕配置的備用資源。僅僅因為這個原因,您的activity在重新建立時完全恢復其狀態非常重要,因為使用者在使用應用程式時會定期旋轉螢幕。

處理配置更改