Android之Activity全面解析

darryrzhong發表於2018-09-29

前言

基於最近要準備去面試,特意系統的複習了下Android基礎,看到Activity這塊時,發現很多都忘了,而且之前也沒有系統的學習和寫筆記記錄,所以,特此寫下這篇關於Activity的一些理解,旨在幫助大家更好的理解Activity.

Activity是什麼?

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

Android應用程式通常由多個彼此鬆散繫結的Activity組成。通常,應用程式中的一個Activity被指定為“主要”Activity,該Activity在首次啟動應用程式時呈現給使用者。然後,每個Activity可以啟動另一個Activity以執行不同的操作。每次新Activity開始時,前一個Activity都會停止,但系統會將Activity保留在後臺堆疊中(“後堆疊”)。當一個新的Activity開始時,它會被推到後面的堆疊上,並引起使用者的注意。後棧遵循基本的“ 後進先出”堆疊機制,因此,當使用者完成當前活動並按下"後退按鈕"時,它從堆疊彈出(並銷燬),之前的活動恢復。(後臺堆疊將在後面為大家詳細介紹。)

如何建立Activity

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

public class ExampleActivity extends AppCompatActivity {

 @Override
    public void onCreate(@Nullable Bundle savedInstanceState, @Nullable PersistableBundle persistentState) {
        super.onCreate(savedInstanceState, persistentState);
//您必須實現此方法。系統在建立Activity時呼叫此方法。在您的實施中,您應該初始化Activity的基本組成部分。最重要的是,您必須在此處呼叫以定義Activity使用者介面的佈局。
        setContentView();
    }


//系統將此方法稱為使用者離開您的Activity的第一個指示(儘管並不總是意味著Activity正在被銷燬)。這通常是您應該提交應該在當前使用者會話之外保留的任何更改的地方(因為使用者可能不會回來)。

@Override
    protected void onPause() {
        super.onPause();
        //在此處應該提交應該在當前使用者會話之外保留的任何更改的地方
    }

}

複製程式碼

在訪問Activity時,必須在manifest中宣告此Activity,

<manifest ... >
 <application ... > 
<activity android:name = “.ExampleActivity” />       ..
. </ application ... >   .
.. </ manifest >
複製程式碼

你也可以指定Activity的<intent-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>  
複製程式碼

如何啟動Activity

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

  • 啟動指定自建的Activity
Intent intent = new Intent(this, SignInActivity.class);
startActivity(intent);
複製程式碼

此種啟動又叫做顯示Intent .

  • 啟動其他型別的Activity
Intent intent = new Intent(Intent.ACTION_SEND);
intent.putExtra(Intent.EXTRA_EMAIL, recipientArray);
startActivity(intent);
複製程式碼

此種啟動又叫做隱式Intent .

有時候,我們可能需要從上一個Activity接收返回資料結果,這時,我們就需要另外一種啟動方式了.

在這種情況下,通過呼叫startActivityForResult()(而不是startActivity())來啟動Activity。然後,要從後續Activity接收結果,就需要實現onActivityResult()回撥方法。完成後續Activity後,它會在您的onActivityResult() 方法中返回結果。

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...
        }
    }
}
複製程式碼

關閉Activity

您可以通過呼叫其finish()方法來關閉活動。您還可以關閉之前通過呼叫啟動的單獨活動finishActivity()。

接下來便是整個Activity最核心的地方了,只要搞清楚一下內容,Activity也就理解的差不多了

Activity生命週期詳解

Activity之所以能夠成為Android四大元件之一,原因便是其具有非常靈活的生命週期回撥方法,通過實現回撥方法來管理Activity的生命週期對於開發強大而靈活的應用程式至關重要。Activity的生命週期直接受其與其他Activity,其任務和後臺堆疊的關聯的影響。

  • Activity基本上存在於三種狀態:
  1. 恢復 onResume() Activity位於螢幕的前景並具有使用者焦點。

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

3.停止 onStop() 該Activity完全被另一個Activity遮擋(活動現在位於“背景”中)。停止的Activity也仍然存在(Activity 物件保留在記憶體中,它維護所有狀態和成員資訊,但不 附加到視窗管理器)。但是,它不再對使用者可見,並且當其他地方需要記憶體時,它可能被系統殺死。 如果Activity暫停或停止,系統可以通過要求它完成(呼叫其finish()方法)或簡單地終止其程式來從記憶體中刪除它。當活動再次開啟時(在完成或殺死之後),必須全部建立它。

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.
    }
}
複製程式碼

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

  1. Activity中存在的三個迴圈生命週期
  • Activity的整個生命週期 Activity的整個生命週期發生在呼叫onCreate()和呼叫之間onDestroy()。您的Activity的整個生命週期應該執行“全域性”狀態的設定(例如定義佈局)onCreate(),並釋放所有剩餘資源onDestroy()。

  • Activity的可見生命週期 Activity的可見生命週期發生在呼叫onStart()和呼叫之間onStop()。在此期間,使用者可以在螢幕上看到Activity並與之互動。例如,onStop()在新Activity開始時呼叫,並且此Activity不再可見。在這兩種方法之間,您可以維護向使用者顯示Activity所需的資源。例如,你可以註冊一個 BroadcastReceiver在onStart()監視會影響您的使用者介面的變化,並在登出它onStop()時,使用者將不再能夠看到你所顯示的內容。該系統可能會呼叫onStart()和onStop()活動的整個生命週期內多次為可見和隱藏使用者之間的活動交替。

  • Activity的前景生命週期 Activity的前景生命週期發生在呼叫onResume()和呼叫之間onPause()。在此期間,Activity位於螢幕上的所有其他活動之前,並且具有使用者輸入焦點。Activity可以頻繁地進出前臺 - 例如,onPause()當裝置進入睡眠狀態或出現對話方塊時呼叫。因為這種狀態可以經常轉換,所以這兩種方法中的程式碼應該相當輕量級,以避免使使用者等待的緩慢轉換。

2.Activity生命週期迴圈圖

activity_lifecycle.png

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

3.Activity生命週期的回撥方法摘要

Method Description Killable after? Next
onCreate() 在第一次建立活動時呼叫。這是您應該進行所有常規靜態設定的地方 - 建立檢視,將資料繫結到列表等等。如果捕獲了該狀態,則此方法將傳遞包含活動先前狀態的Bundle物件 No onStart()
onRestart() 在Activity停止後,再次啟動之前呼叫。 No onStart()
onStart() 在活動變得對使用者可見之前呼叫。 No onResume()或onStop()
onResume() 在活動開始與使用者互動之前呼叫。此時,活動位於活動堆疊的頂部,使用者輸入轉到活動堆疊。 No onPause()
onPause() 當系統即將開始恢復另一個活動時呼叫。此方法通常用於將未儲存的更改提交到持久資料,停止動畫以及可能消耗CPU的其他事物,等等。它應該做很快的事情,因為下一個Activity在返回之前不會恢復。 Yes onResume()或onStop()
onStop() 當活動不再對使用者可見時呼叫。這可能是因為它正在被銷燬,或者是因為另一個活動(現有的或新的活動)已經恢復並且正在覆蓋它。 Yes onRestart()或onDestroy()
onDestroy() 在活動被銷燬之前呼叫。這是活動將收到的最後一個電話。可以呼叫它,因為活動正在完成(有人呼叫finish()它),或者因為系統暫時銷燬此活動例項以節省空間。您可以使用該isFinishing()方法區分這兩種情況。 Yes nothing

其中Killable after?表示系統是否可以在方法返回後隨時終止託管Activity的程式,而不執行Activity程式碼的下一個回撥方法.

因為onPause()是三個允許Killable after方法中第一個執行的,一旦活動被建立,onPause()是在回撥過程中能夠保證執行的最後一個方法,如果系統必須在緊急情況下恢復記憶,是會呼叫onPause()方法的,但是onStop()和onDestroy()可能不被呼叫。因此,您應該使用onPause()將關鍵的永續性資料(例如使用者編輯)寫入儲存。而不是onStop()或onDestroy().

所以在此處特別宣告,要儲存使用者輸入資料,需要在onPause()中執行,而不是在onStop()和onDestroy()中.

生命週期回撥的順序是明確定義的,特別是當兩個Activity在同一個程式中而其中一個正在啟動另一個時。以下是ActivityA啟動ActivityB時發生的操作順序:

1.ActivityA的onPause()方法執行。
2.ActivityB的onCreate(),onStart()和onResume() 方法執行順序。(ActivityB現在具有使用者關注點。)
3.然後,如果ActivityA在螢幕上不再可見,則onStop()執行其方法。
複製程式碼

這種可預測的生命週期回撥序列允許您管理從一個Activity到另一個Activity的資訊轉換。例如,如果您必須在第一個Activity停止時寫入資料庫以便以下Activity可以讀取它,那麼您應該在onPause()期間而不是在onStop()期間寫入資料庫。

3.儲存Activity活動狀態 當Activity暫停或停止,該Activity的活動狀態會被保持,因為Activity物件在暫停或停止時仍保留在記憶體中 - 有關其成員和當前狀態的所有資訊仍然存在。因此,Activity會預設保留使用者在活動中所做的任何更改,以便當活動返回到前臺時(當它“恢復”時),那些更改仍然存在。

但是,當系統銷燬Activity以恢復記憶體時,Activity物件被破壞,因此係統不能完好無損地恢復它的活動狀態。在這種情況下,您可以通過實施額外的回撥方法來確保保留有關活動狀態的重要資訊,該方法允許您儲存有關活動狀態的資訊:

onSaveInstanceState()  //用來儲存使用者狀態資訊
複製程式碼

onSaveInstanceState() 在Activity受破壞之前,系統會自動呼叫。系統會傳遞一個Bundle物件,您可以使用諸如putString()和putInt()之類的方法將關於Activity的狀態資訊儲存為名稱 - 值的鍵值對.

然後,如果系統終止您的應用程式程式,系統將重新建立活動並將其Bundle傳遞給Activity的onCreate()和onRestoreInstanceState()。使用這些方法之一,您可以從中提取已儲存的狀態Bundle並恢復活動狀態。如果沒有要恢復的狀態資訊,則Bundle傳遞給您的是null(這是第一次建立活動時的情況)。

如下圖所示:

restore_instance.png

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

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

再者就是,即使不執行任何操作並且未實現onSaveInstanceState(),只要Activity沒有被銷燬,Activity也會預設實現onSaveInstanceState()來恢復某些活動狀態.例如EditText輸入的文字,但前提是控制元件需要有android:id,否則不會預設儲存狀態.

  1. onSaveInstanceState()常用場景

某些裝置配置可能會在執行時更改(例如螢幕方向,鍵盤可用性和語言)。發生此類更改時,Android會重新建立正在執行的活動(系統呼叫onDestroy(),然後立即呼叫onCreate()).處理此類重新啟動的最佳方法是使用onSaveInstanceState()和onRestoreInstanceState()(或onCreate())來儲存和恢復Activity活動狀態.

Activity進出後臺任務和後臺堆疊詳解

每個應用程式通常包含多個Activitys,每個Activity都應圍繞使用者可以執行的特定操作進行設計,並可以啟動其他Activity,甚至可以啟動裝置上其他應用程式中存在的Activity.

Android裝置主螢幕是大多數任務的起始位置。當使用者觸控應用程式啟動器中的圖示(或主螢幕上的快捷方式)時,該應用程式的任務將到達前臺。如果應用程式不存在任務(最近未使用該應用程式),則會建立一個新任務,該應用程式的“主”Activity將作為堆疊中的根Activity開啟。

1.後臺堆疊 後臺堆疊指的是Activitys在後臺任務中的排列規則,按"後進先出"排列.

例如當前Activity啟動另一個Activity時,新Activity將被推到堆疊頂部並獲得焦點。之前的Activity仍在堆疊中,但已停止。當活動停止時,系統將保留其使用者介面的當前狀態。當使用者按下“ 返回” 按鈕時,當前Activity將從堆疊頂部彈出(活動被銷燬),之前的Activity將恢復(其UI的先前狀態將恢復)。堆疊中的活動永遠不會重新排列,只能在當前Activity啟動時從堆疊推送和彈出到堆疊中,並在使用者使用Back按鈕退出時彈出.因此,後臺堆疊作為“後進先出”物件結構操作。

如下圖所示:

diagram_backstack.png

圖中表示任務中的每個新Activity如何將項新增到後臺堆疊。當使用者按下“ 返回”按鈕時,將破壞當前Activity並恢復先前的Activity。

如果使用者繼續按Back,則彈出堆疊中的每個Activity以顯示前一個Activity,直到使用者返回主螢幕(或任務開始時執行的任何活動)。從堆疊中刪除所有活動後,該任務不再存在。

2.後臺任務 任務是一個內聚單元,當使用者開始新任務或通過主頁按鈕進入主螢幕時,可以移動到“後臺背景” 。在後臺,任務中的所有Activitys都會停止,但任務的後臺堆疊保持不變 - 任務在發生另一項任務時完全失去焦點.

如下圖所示:

diagram_multitasking.png

兩個任務:任務B在前臺接收使用者互動,而任務A在後臺,等待恢復,這就是Android上的多工處理的一個示例。

注意:可以在後臺同時儲存多個任務。但是,如果使用者同時執行許多後臺任務,系統可能會開始銷燬後臺活動以恢復記憶體,從而導致活動狀態丟失。

由於後臺堆疊中的活動永遠不會重新排列,如果您的應用程式允許使用者從多個Activitys啟動特定Activity,則會建立該Acticity的新例項並將其推送到堆疊(而不是引入任何先前的活動例項)到頂部。因此,應用程式中的一個Acticity可能會被多次例項化(甚至來自不同的任務).

如下圖所示:

diagram_multiple_instances.png

如果不希望多次例項化Acticity,則可以修改此行為。通常來說關於這部分內容是不太需要我們去更改的,所以這裡我就不介紹了,有興趣的同學可以自行翻閱[官方文件].

3.定義Activity啟動模式

啟動模式允許您定義Activity的新例項與當前任務的關聯方式。您可以通過兩種方式定義不同的啟動模式:

  • 使用 manifest file(manifest 清單檔案)

在清單檔案中宣告Activity時,可以使用 元素的launchMode屬性指定Activity在啟動時應如何與任務關聯。

launchMode 屬性分配四有種不同的啟動模式 :

modes description
"standard" (預設模式) 預設。系統在啟動它的任務中建立Activity的新例項,並將意圖路由到該例項。Activity可以多次例項化,每個例項可以屬於不同的任務,一個任務可以有多個例項。
"singleTop" 如果Activity的例項已存在於當前任務的頂部,則系統通過呼叫其onNewIntent()方法將意圖路由到該例項,而不是建立Activity的新例項。Activity可以多次例項化,每個例項可以屬於不同的任務,一個任務可以有多個例項(但只有當後端堆疊頂部的Activity不是Activity的現有例項時)。
"singleTask" 系統建立新任務並在新任務的根目錄下例項化Activity。但是,如果Activity的例項已存在於單獨的任務中,則系統會通過呼叫其onNewIntent()方法將意圖路由到現有例項,而不是建立新例項。一次只能存在一個Activity例項。
"singleInstance" 相同"singleTask",區別在於:系統不啟動任何其他Activity納入控制例項的任務。Activity始終是其任務的唯一成員; 任何由此開始的Activity都在一個單獨的任務中開啟。

無論Activity是在新任務中啟動還是在與啟動它的Activity相同的任務中啟動,“ 返回”按鈕始終會將使用者帶到上一個Activity。但是,如果啟動指定singleTask啟動模式的活動,則如果後臺任務中存在該Activity的例項,則將整個任務帶到前臺。此時,後端堆疊現在包括堆疊頂部提出的任務中的所有Activitys。

如下圖所示:

diagram_backstack_singletask_multiactivity.png

  • 使用 Intent flags(意圖傳遞)

當您呼叫時startActivity(),您可以在其中包含一個標誌Intent,宣告新Activity應如何(或是否)與當前任務相關聯。

Intent flags description
FLAG_ACTIVITY_NEW_TASK 與上述"singleTask" 效果相同
FLAG_ACTIVITY_SINGLE_TOP 與上述"singleTop"效果相同
FLAG_ACTIVITY_CLEAR_TOP 如果正在啟動的Activity已在當前任務中執行,則不會啟動該Activity的新例項,而是銷燬其上的所有其他Activitys,並將此意圖傳遞給Activity的恢復例項

注意:如果指定Activity的啟動模式是 "standard",它也將從堆疊中刪除,並在其位置啟動新例項以處理傳入的意圖。這是因為在啟動模式下,總是為新意圖建立一個新例項"standard"。

4.清理後堆疊

如果使用者長時間離開任務,系統將清除除根Activity之外的所有Activitys的任務。當使用者再次返回任務時,僅還原根Activity。系統以這種方式執行,因為在很長一段時間之後,使用者可能已經放棄了之前正在做的事情並返回任務以開始新的事情。

可以使用一些Activity屬性來修改此行為:

attribute description
alwaysRetainTaskState 如果任務的根Activity將此屬性設定為"true",則不會發生剛才描述的預設行為。即使經過很長一段時間,任務仍會保留堆疊中的所有Activity
clearTaskOnLaunch 與"alwaysRetainTaskState"正好相反,即使在離開任務片刻之後,使用者也始終以初始狀態返回任務
finishOnTaskLaunch 此屬性類似clearTaskOnLaunch,但它在單個活動上執行,而不是在整個任務上執行。它還可以導致任何活動消失,包括根活動。當它設定為時"true",活動仍然是當前會話的任務的一部分。如果使用者離開然後返回任務,它將不再存在

深呼一口氣,不知不覺寫了這麼多,關於Activity的基本解析到這裡就告一段落了,寫完這些我對Activity又有了更深刻的理解和認識了,本文不止對Activity表面解析,更是深入到了一些底層驅動,希望看完這些能對你有所幫助.

歡迎關注作者darryrzhong,更多幹貨等你來拿喲.

請賞個小紅心!因為你的鼓勵是我寫作的最大動力!

更多精彩文章請關注

相關文章