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);
}