Activity異常下的生命週期、啓動模式和標記位,隱式啓動匹配規則IntentFilter

1,Activity異常情況下的生命週期

在Activity遭遇非正常退出時,其生命週期會有所不同。
例如處於後臺的Activity由於內存不足而導致被銷燬,由於配置改變而導致的重建等。

  在這種情況下是非用戶操作而導致的,也就是說是有可能再次顯示的。因此Activity會調用兩個方法來進行數據的保存和恢復。onSaveInstanceState(Bundle outState)和onRestoreInstanceState(Bundle savedInstanceState),在異常情況將要銷燬Activity時,onSaveInstanceState會被調用,此時可以在該方法中進行數據的存儲,可以存儲到Bundle中。該方法運行在onStop方法之前。之後重建Activity時會調用onRestoreInstanceState方法進行數據的恢復。
  另外,在onCreate方法中也可以進行數據的恢復,他們的參數中都有Bundle,裏面儲存着之前保存的數據,不同的是正常情況下onCreate的參數爲null,因此要在onCreate中恢復數據時要進行非空檢測。

避免配置改變帶來的Activity的重建

  當配置改變時,如旋轉屏幕等,會造成Activity的重建。即先進行銷燬後進行創建,同時進行數據的保存與恢復。
  但有時候不想讓Activity重建,那麼可以通過配置manifest中Activity的configChanges屬性來避免重建。

<activity
   android:name=".MainActivity"
   android:configChanges="orientation">
       <intent-filter>
           <action android:name="android.intent.action.MAIN" />
           <category android:name="android.intent.category.LAUNCHER" />
       </intent-filte>
</activity>

configChanges 常用的有一下幾個:
  locale:地區發生改變,通常是更改系統語言
  orientation:屏幕發生旋轉
  keyBoardHidden:鍵盤狀態發生了變化,如調出鍵盤
  screenSize:屏幕大小發生了變化,旋轉屏幕時會改變(api13以上)

若是不想讓該Activity重建,則在Activity的配置中加入這幾個即可,用‘ | ’ 分隔多個。


  另外的,對於一個Activity(A)啓動另一個Activity(B)時,生命週期的順序爲onPause(A)->onCreate(B)->onStart(B)->onResume(B)->onStop(A),因此不能再onPause中進行耗時操作。

2,Activity的啓動方式

Activity啓動方式有4種,standard,singleTop,singleTask,singleInstance
設置啓動模式可以在manifest中的Activity的launchMode中修改

<activity
     android:name=".MainActivity"
     android:launchMode="singleTop"
     android:configChanges="orientation">
        <intent-filter>
           <action android:name="android.intent.action.MAIN" />
           <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
</activity>
standard:標準模式(默認模式)

每次啓動Activity時都會創建一個實例放在任務棧上,無論該Activity是否已經被創建。

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

log日誌:

I/BaseActivity: MainActivity: onCreate
I/BaseActivity: MainActivity: onStart
I/BaseActivity: MainActivity: onResume
I/BaseActivity: MainActivity: onPause
I/BaseActivity: MainActivity: onCreate
I/BaseActivity: MainActivity: onStart
I/BaseActivity: MainActivity: onResume

I/BaseActivity: MainActivity: onStop

singleTop:棧頂複用模式

若是待啓動的Activity已經存在於棧頂,則不會重新創建Activity,而是先調用onPause,然後調用onNewInten再調用onResume

log日誌:

I/BaseActivity: MainActivity: onCreate
I/BaseActivity: MainActivity: onStart
I/BaseActivity: MainActivity: onResume
I/BaseActivity: MainActivity: onPause
I/BaseActivity: onNewIntent:
I/BaseActivity: MainActivity: onResume

singleTask:棧內複用

  若是待啓動的Activity爲singleTask模式,並且已經在任務棧中存在。若是處於棧頂,則與singleTop一樣,先調用onPause,再調用onNewIntent,最後調用onResume。
  若不是處於棧頂,則它上面的所有Activity都出棧。方法調用順序:它上面的非棧頂的Activity調用onDestroy,然後原棧頂調用onPause,然後喚醒Activity onNewIntent->onRestart->onStart->onResume,然後原棧頂Activity調用onStop->onDestroy.

I/BaseActivity: ThirdActivity: onDestroy
I/BaseActivity: MainActivity: onPause
I/BaseActivity: TwoActivity: onNewIntent
I/BaseActivity: TwoActivity: onRestart
I/BaseActivity: TwoActivity: onStart
I/BaseActivity: TwoActivity: onResume

I/BaseActivity: MainActivity: onStop
I/BaseActivity: MainActivity: onDestroy

singleInstance:單實例模式

  該模式下的Activity會單獨存在一個任務棧,其餘與singleTask一樣,只是由於該Activity單獨存在一個任務棧,因此不會出現singleTask那樣的其上Activity全部出棧的情況。
  退出時則按任務棧順序退出,例如任務棧A調用任務棧B(順序爲A->B),任務棧B調用C(順序爲A->B->C),任務棧C再調用任務棧A(順序爲B->C->A),此時按返回鍵退出則是先退出A後退出C,最後退出B。

任務棧

Activity默認都是添加到一個與包名相同的任務棧中,也可以通過設置taskAffinity屬性來指定任務棧。

<activity
   android:name=".TwoActivity"
   android:launchMode="standard"
   android:taskAffinity="com.example.MyApplication" />
<activity

若是指定了singleInstance模式的taskAffinity,則該Activity會出現在最近訪問任務中。taskAffinity只在singleTask和singleInstance中設置有效。

標記位
Intent.FLAG_ACTIVITY_SINGLE_TOP:

  添加該標記位則會在當前棧頂中尋找Activity若是找到,則調用onPause->onNewIntent->onResume。否則創建一個新的Activity。類似於singleTop模式

Intent.FLAG_ACTIVITY_NEW_TASK:

  單獨使用沒什麼作用

Intent.FLAG_ACTIVITY_CLEAR_TOP:

  對於一個standard模式的Activity,加上該標記位則會清除其上的所有Activity(包括自己),然後再重建Activity。
  對於singleTop的Activity,加上該標記相當於singleTask,會清空其上的Activity(不包括自己)並調用onNewIntent->onRestart->onStart->onResume

Activity的Intent啓動匹配規則

  啓動一個Activity有兩種方式,一種是顯式啓動,一種是隱式啓動。
  顯示啓動是直接通過Activity的類名來啓動的。

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

  隱式啓動是指Activity設置一系列的匹配規則,調用方可以不知道Activity的具體類名,而是通過設置對應的規則來匹配想要的Activity。
  匹配規則分爲三個部分,action,category,data。

<activity
    android:name=".MainActivity"
    android:configChanges="orientation"
    android:launchMode="standard">
       <intent-filter>
           <action android:name="android.intent.action.MAIN" />
           <category android:name="android.intent.category.LAUNCHER" />
       </intent-filter>
</activity>

  上面是MainActivity的匹配規則。可以看到匹配規則是在AndroidManifest中的Activity下的intent-filter標籤中設置。

action

  對於action,其值是一串任意的字符串,同時也是區分大小寫的,並且可以添加多個,只要有一個能夠匹配就行。而且要啓動一個Activity,action是必須要設置的(在manifest和intent中都要設置)。

<activity android:name=".TwoActivity">
    <intent-filter>
        <action android:name="android.intent.action.TwoActivity" />
        <action android:name="andooid.intent.action.TwoActivity1"/>
        <category android:name="android.intent.category.DEFAULT" />
   </intent-filter>
</activity>
Intent intent  = new Intent();
intent.setAction("android.intent.action.TwoActivity");
startActivity(intent);
category

  category也是一個任意的區分大小寫的字符串,但是category與action有些不同,因爲在intent中,可以不添加category,而系統會自動添加一個叫做android.intent.category.DEFAULT的category,因此在manifest中,必須添加這個category,不然將會無法啓動Activity。
  另外,category也是可以有多個的。但是在intent中添加的所有的category都必須能夠匹配,否則將會無法啓動Activity。

<activity android:name=".TwoActivity">
       <intent-filter>
              <action android:name="android.intent.action.TwoActivity" />
              <category android:name="android.intent.category.DEFAULT" />
              <category android:name="com.example.MyCategory"/>
       </intent-filter>
</activity>
Intent intent  = new Intent();
intent.setAction("android.intent.action.TwoActivity");
intent.addCategory("com.example.MyCategory");
startActivity(intent);
data

  data由URI和mimeType組成。URI分爲schema,host,port,path。在intent-filter中,可以不設置data過濾標籤,同時intent中也不能設置。可以只設置URI或者mimeType。
  對於只設置URI而不設置mimeType而言,intent中對應也不能設置mimeType。
  對於只設置mimeType而不設置URI而言,intentmimeType必須設置,URI可以不設置,但若是設置,schema只能是content和file類型,並且不匹配其他host等信息。
  若是同時設置,在intent中也必須同時設置,並且不能分開設置,否則會清除另一個信息。

<activity android:name=".TwoActivity">
   <intent-filter>
       <action android:name="android.intent.action.TwoActivity" />
       <category android:name="android.intent.category.DEFAULT" />
       <category android:name="com.example.MyCategory" />
       <data
          android:host="www.example.com"
          android:mimeType="text/*"
          android:path="/a"
          android:port="80"
          android:scheme="content" />
   </intent-filter>
</activity>
Intent intent = new Intent();
intent.setAction("android.intent.action.TwoActivity");
intent.addCategory("com.example.MyCategory");
intent.setDataAndType(Uri.parse("content://www.example.com:80/a"),"text/*");
startActivity(intent);

//同時設置URI和mimeType的時候要使用setDataAndType()不能使用setData+setType的組合方式。
/*
public @NonNull Intent setData(@Nullable Uri data) {
        mData = data;
        mType = null;
        return this;
}
public @NonNull Intent setType(@Nullable String type) {
        mData = null;
        mType = type;
        return this;
}
*/
//由其方法即可看出在設置其中一項的時候回將另一個設置爲null

  對於顯示啓動的Activity,不會對過濾規則進行匹配,而是直接進行啓動。
  另外,對於匹配失敗的intent,將會報android.content.ActivityNotFoundException異常。可以使用intent.resolveActivity(getPackageManager())進行判斷,當匹配失敗的時候,該方法會返回null,可以在返回null的時候取消啓動。

if(intent.resolveActivity(getPackageManager())!=null) {
     startActivity(intent);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章