Android知識點回顧之Activity基礎

星泉毅發表於2019-03-01

Activity生命週期

Activity的生命週期包括onCreate(),onRestart(),onStart(),onResume(),onPause(),onStop(),onDestroy()。其相互轉化的過程如下圖所示。左邊的圖是單個Activity的生命週期回撥情況。右邊的是當前Activity跳轉到另外一個Activity時,兩者相關生命週期回撥先後順序的情形。

圖1:Activity生命週期
圖1:Activity生命週期

onCreate()

表示Activity正在被建立

此方法在整個生命週期中只會被呼叫一次。可以在這裡做一些初始化的操作。

引數savedInstanceState儲存Activity因為異常情況而被銷燬前的狀態,可以利用此引數做一些資料恢復的操作。

onStart()

表示Activity正在被啟動。此時Activity已經可見,即將進入前臺介面,但是還不能喝使用者進行互動。

onResume()

表示Activity已經啟動完成,進入到了前臺,可以同使用者進行互動了。需要注意的是此方法在Activity的整個生命週期中可能會被多次呼叫到。

onPause()

表示Activity正在被停止。

可以在這裡釋放系統資源,動畫的停止

不宜在此做耗時操作,因為此方法結束後會呼叫新Activity的onCreate(),onStart(),onResume(),耗時操作會影響到新Activity的顯示

onStop()

當Activity不可見的時候回撥此方法。

需要在這裡釋放全部使用者使用不到的資源。

可以做較重量級的工作,如對註冊廣播的解註冊,對一些狀態資料的儲存

此時Activity還不會被銷燬掉,而是保持在記憶體中,但隨時都會被回收。

onDestroy()

Activity即將被銷燬。

此時會釋放掉所有佔用的資源。

通常的:

onCreate()和onDestroy()成對存在

onStart()和onStop()成對存在

onResume()和onPause()成對存在

Activity狀態的儲存和恢復

狀態的儲存

當Activity 非正常退到後臺時,就會呼叫onSaveInstanceState()方法。非正常的情況包括:有新的Activity啟動,滅屏,未設定儲存狀態的橫豎屏切換,按home鍵到桌面,切換到最近任務列表。正常情況是使用者點選返回鍵,程式呼叫finish()方法,此時不會回撥onSaveInstanceState()方法。

呼叫的次序為:onPause()->onSaveInstanceState()->onStop()

可對一些狀態進行儲存,

static final String STATE_LISTVIEW_CURRENT_POSITION = "list_view_current_position";
...


@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
    // 儲存當前ListView的位置
    savedInstanceState.putInt(STATE_LISTVIEW_CURRENT_POSITION , mCurrentPosition);

    // Always call the superclass so it can save the view hierarchy state
    super.onSaveInstanceState(savedInstanceState);
}複製程式碼

系統會為View自動儲存相關的狀態,前提是View要在佈局檔案裡設定唯一的id值,即android:id屬性。

由於onSaveInstanceState()方法不能保證百分百呼叫到,所以只能做一些臨時狀態資訊的儲存,如果要做持久化的操作,需要在onStop()裡進行。

狀態的恢復

當Activity在被銷燬後重新建立時,可以從onCreate(Bundle savedInstanceState)方法中的savedInstanceState引數恢復之前儲存的狀態,也可以從onRestoreInstanceState(Bundle savedInstanceState)方法中恢復。兩種方法的效果都是一樣的,只是前者需要判空操作,後者不需要

onCreate(Bundle savedInstanceState)的恢復:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState); 

   //如果不為空的話,則說明有可恢復的狀態
    if (savedInstanceState != null) {        
        mCurrentPosition= savedInstanceState.getInt(STATE_LISTVIEW_CURRENT_POSITION );
    } else {
        //初始化
    }
    ...
}複製程式碼

onRestoreInstanceState(Bundle savedInstanceState)的恢復:

//呼叫此方法,savedInstanceState引數必不為空
public void onRestoreInstanceState(Bundle savedInstanceState) {
    super.onRestoreInstanceState(savedInstanceState);
    mCurrentPosition= savedInstanceState.getInt(STATE_LISTVIEW_CURRENT_POSITION );
}複製程式碼

Activity啟動模式

四種啟動模式

Activity的啟動模式可以允許你的Activity擁有多少個例項。

有兩種方法可以設定:1、在manifest檔案中設定。2、用Intent的flag標識設定

manifest方式

在minifest中對應的Activity設定如下四個中的一個值。

...
<activity 
  ...
   android:launchMode=["standard" | "singleTop" | "singleTask" | "singleInstance"]
  ...
/>
...複製程式碼

standard

預設值,多例項模式。

每啟動一次,都會建立一個新的Activity例項。

並且新例項可以在不同的任務棧上建立,相同的任務棧可以建立多個例項。

啟動的生命週期為:onCreate()->onStart()->onResume()

需要注意的是,此模式下啟動的例項所在的任務棧,為啟動它的那個Activity所屬的任務棧。比如A屬於task1,A啟動了B,B的模式為standard,則B所在的任務棧為task1

singleTop

棧頂複用模式

如果任務棧頂已經存在需要啟動的目標Activity,則直接啟動,並會回撥onNewIntent()方法,生命週期順序為:
onPause() ->onNewIntent()->onResume()

如果任務棧上頂沒有需要啟動的目標Activity,則建立新的例項,此時生命週期順序為:
onCreate()->onStart()->onResume()

兩種情況如下圖,從圖中可以看出,此模式下還是會出現多例項,只要啟動的目標Activity不在棧頂的話。

singleTop啟動模式,A為standard模式,B為singleTop模式
singleTop啟動模式,A為standard模式,B為singleTop模式

singleTask

棧內複用模式,一個任務棧只能有一個例項。

有幾種情況:

  • 當啟動的Activity目標任務棧不存在時,則以此啟動Activity為根Activity建立目標任務棧,並切換到前面

    D為singleTask模式
    D為singleTask模式

  • 當啟動的Activity目標任務棧存在,啟動的Activity不存在時,則直接在目標任務棧上建立Activity

    D的啟動模式為singleTask,並且taskAffinity為task2
    D的啟動模式為singleTask,並且taskAffinity為task2

  • 當啟動的Activity存在時,則會直接切換到Activity所在的任務棧,並且任務棧中在Activity上面的所有其他Activity都出棧(呼叫destroy()),此時啟動的Activity位於任務棧頂,並且會回撥onNewIntent()方法。

    左邊的DtaskAffinity為task2的情況,右邊D的taskAffinity為task1的情況
    左邊的DtaskAffinity為task2的情況,右邊D的taskAffinity為task1的情況

singleInstance

和singleTask模式類似,只不過獨佔其所在的任務棧,比如A為singleInstance模式,其所需的任務棧為task1,則task1只有A一個Activity,由A啟動的Activity會另起一個task。而後續啟動此Activity,都不會再建立新的Activity。

Intent的方式

除了可以在manifest中設定Activity的啟動模式,也可以通過設定Intent的flag標識來設定Activity的啟動模式。

常用的有:FLAG_ACTIVITY_NEW_TASK,FLAG_ACTIVITY_SINGLE_TOP,FLAG_ACTIVITY_CLEAR_TOP

FLAG_ACTIVITY_NEW_TASK

啟動Activity時,如果不存在Activity的例項,則會以此Activity為根Activity建立新的任務棧,如果存在的話則直接切換到對應的Activity例項,並回撥onNewIntent()方法。相當於“singleTask”啟動模式。

FLAG_ACTIVITY_SINGLE_TOP

相當於“singleTop”模式

FLAG_ACTIVITY_CLEAR_TOP

設定此標識的Activity在啟動時,如果當前的任務棧記憶體在此Activity例項,則跳轉到此例項,並清除掉在此例項上面的所有Activity例項,此時此Activity例項位於任務棧的棧頂

Activity間的資料傳遞

Activity直接的資料傳遞,一般使用Intent就夠了。

可以使用Intent.putExtra()方法

把值傳給待啟動的Activity

//MianActivity
 Intent intent = new Intent(this, SecondActivity.class);
 intent.putExtra("data_key", "abcd");

 Bundle data = new Bundle();
 data.putString("key1","value1");
 intent.putExtra("data",data);
...
 startActivity(intent);

...
//SecondActivity
...
protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.my_activity_layout);
        Intent intent = getIntent();
        Toast.makeText(this,intent.getStringExtra("data_key"),Toast.LENGTH_LONG).show();

        Bundle data = intent.getBundleExtra("data");
        String key1 = data.getString("key1");
        Toast.makeText(this,key1,Toast.LENGTH_LONG).show();
 }
...複製程式碼

把值返回給啟動它的Activity

Intent intent = new Intent(MainActivity.this,SecondActivity.class);

 startActivityForResult(intent, REQUEST_CODE);

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
 super.onActivityResult(requestCode, resultCode, data);
 String str = data.getStringExtra("data");
 if(REQUEST_CODE == requestCode){
    if(SecondActivity.RESULT_CODE == resultCode){
      ...
    }
 }

 ...
}
...
//SecondActivity
...
 Intent intent = new Intent();
 intent.putExtra("data", "result_value");
 setResult(RESULT_CODE, intent);
 finish();
...複製程式碼

相關文章