生命周期
- onCreate();
- onStart(); 可見,
- onResume(); 可操作,在前臺
- onPause();
- onStop();
- onDestroy();
- onRestart(); 返回前activity, 讓他重新啟動
- 當Activity只執行onPause方法時(透明Activity),這時候如果App設置的targetVersion大于11則不會執行onSaveInstanceState方法
處理廣播, 在onStart和onStop, onResume和onPause易觸發并且可能只是暫時不能執行(被透明activity擋住)
跳轉B前操作數據庫, 并且B需要數據庫, 在onPause,
onPause
onCreate--B
onStart--B
onResume--B
onStop
所以得確保B沒出來的時候數據加載完畢, 但是如果B是半透明的, 則不會執行onStop
B返回A
onPause -B
onRestart
onStart
onResume
onStop --B
onDestroy --B
黑屏/按home/按任務列表生命周期:
onPause
onStop
onRestart
onStart
onResume
橫豎屏切換生命周期,此時開 alertDialog.Builder 格式的dialog, 生命周期不影響
onPause
onStop
onDestroy
onCreate
onStart
onResume
Activity 是否運行
(activity == null || activity.isDestroyed() || activity.isFinishing())
原因: isFinishing 根據 mFinished, mFinished只在finish
時改變, 無法確定finish一定會執行, 且 finish 執行后, 無法保證立馬走到 onDestroy
public boolean isFinishing() {
return mFinished;
}
根據Google源碼中的Dialer應用的源碼Link, 添加 isDestroyed 判斷
@Override
protected void onPostExecute(Void result) {
final Activity activity = progressDialog.getOwnerActivity();
if (activity == null || activity.isDestroyed() || activity.isFinishing()) {
return;
}
if (progressDialog != null && progressDialog.isShowing()) {
progressDialog.dismiss();
}
}
onUserLeaveHint 和 onUserInteraction
- onUserLeaveHint
當用戶的操作導致activity即將進入后臺的時候, 此方法會被調用, 但在來電時不被調用 - onUserInteraction
activity 在分發各種事件之前會調用, 但啟動另一個activity會被調用兩次, 一次是 activity 補貨到事件時, 一次是調用activity.onUserLeaveHint之前會調用onUserInteraction
臨時狀態保存 -- onSaveInstanceState 和 onRestoreInstanceState
是用來臨時存儲數據, 長期的還是需要數據庫
前提是view必須有ID
保存狀態
-
onSaveInstanceState(Bundle savedInstanceState)
執行在 onStop 之前- 只有back不會觸發
=================== - home觸發
- 任務列表鍵會觸發
- 跳轉 Activity 會觸發
- 旋轉屏幕會觸發
- 黑屏會觸發
Activity 創建時有一個 bundle 對象, 該對象是上次被系統銷毀時在 onSaveInstanceState 保存的參數,
```java
static final String STATE_SCORE = "playerScore";
static final String STATE_LEVEL = "playerLevel";@Override public void onSaveInstanceState(Bundle savedInstanceState) { // Save the user's current game state savedInstanceState.putInt(STATE_SCORE, mCurrentScore); savedInstanceState.putInt(STATE_LEVEL, mCurrentLevel); // Always call the superclass so it can save the view hierarchy state super.onSaveInstanceState(savedInstanceState); } ``` **注意,自定義的部分在super之前**
使用onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState), 則onSaveInstanceState不會被調用
- 只有back不會觸發
-
onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState)
API21 后添加的屬性, 能在關機重啟后擁有數據恢復的功能, 需要在 manifest 中給 acticity 添加屬性android:persistableMode="persistAcrossReboots"
須
- 重寫 onCreate(Bundle savedInstanceState, PersistableBundle persistentState),
- 并不用普通的 onCreate 和 onSaveInstanceState
重寫此onSaveInstanceState會導致 onSaveInstanceState 不執行
恢復狀態
-
onRestoreInstanceState
執行在 onStart 之后, 只有有savedInstanceState時才會調用, 無需判空public void onRestoreInstanceState(Bundle savedInstanceState) { // Always call the superclass so it can restore the view hierarchy super.onRestoreInstanceState(savedInstanceState); // Restore state members from saved instance mCurrentScore = savedInstanceState.getInt(STATE_SCORE); mCurrentLevel = savedInstanceState.getInt(STATE_LEVEL); }
-
onCreate中恢復, 需判空
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Always call the superclass first // Check whether we're recreating a previously destroyed instance if (savedInstanceState != null) { // Restore value of members from saved state mCurrentScore = savedInstanceState.getInt(STATE_SCORE); mCurrentLevel = savedInstanceState.getInt(STATE_LEVEL); } else { // Probably initialize members with default values for a new instance } ... }
透明activity(繼承AppCompatActivity)
- 透明color
<?xml version="1.0" encoding="UTF-8"?>
<resources>
<color name="transparent">#0000</color>
</resources>
- style設置background, style的parent不加會報錯:
You need to use a Theme.AppCompat theme (or descendant) with this activity.
<style name="myTransParent" parent="Theme.AppCompat.Light.NoActionBar">
<item name ="android:windowBackground">@color/transParent</item>
<item name ="android:windowNoTitle">true</item>
<item name ="android:windowIsTranslucent">true</item>
<item name ="android:colorBackgroundCacheHint">@null</item>
<item name ="android:windowAnimationStyle">@android:style/Animation.Translucent</item>
<item name ="android:statusBarColor">@color/transParent</item>
</style>
- 清單設置 activity 的 theme
android:theme="@style/myTransparent"
ActivityA 打開透明ActivityB
onPause
onCreate --B
onStart --B
onResume --B
透明ActivityB返回ActivityA
onPause --B
onResume
onStop --B
onDestroy --B
啟動模式
A組件運行在A應用, A組件調用系統的照相組件, 雖然不在一個應用, 但是安卓運行跨應用組件, 雖然兩者不在一個進程, 但是可以通過Task來實現, Task可以理解為實現一個功能而負責管理所有用到的 Activity實例的棧
standard
默認, 先進后出
singleTop
棧頂不重復, 唯一
如果要開啟的 activity 已經在棧頂存在, 就不會創建新的實例, 而去調用 onNewIntent().
如果棧頂沒有, 則創建新的實例
比如 通知欄的通知點擊打開activity, 用singleTop可以避免重復創建
快速點擊不能只依賴這個, 不靠譜, 第一個棧實例可能還沒入棧,就創建了第二個然后和第一個一起入棧了, 建議對快速點擊另作判斷
singleTask
棧內不重復, 干掉頭上的其他activity
如果要開啟的activity已經在棧內存在, 就不會創建新的棧和新的實例, 而去調用 onNewIntent(), 并且清空它上面的所有activity
比如 主界面, 保證主界面上面沒有activity
singleInstance
單一實例, 單一棧, 棧里只有自己
謹慎使用
如果要開啟的activity已經在進程中存在, 就不會創建新的實例, 而去調用 onNewIntent().
比如 呼叫來電界面
指定啟動模式
- manifest指定
<activity android:name=".SecondActivity"
android:launchMode="singleTask"/>
- 代碼指定
Intent intent = new Intent(MainActivity.this, SecondActivity.class);
//通過 intent.addFlags 設置
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
-
對比
- 代碼指定優先于 manifest
- manifest無法設定singleInstance模式
-
Intent.FLAG_:
- FLAG_ACTIVITY_NEW_TASK = 開新棧, 多用在service和Application, 因為他倆沒有棧
- FLAG_ACTIVITY_SINGLE_TOP = singleTop
- FLAG_ACTIVITY_CLEAR_TOP = singleTask
- FLAG_ACTIVITY_NO_HISTORY 啟動完其他的后, 自己消失
-
StartActivityForResult 5.0 分水嶺
- 5.0 之前可能不回調
- 5.0 后全部有回調
-
adb 查看任務棧
-
運行
adb shell dumpsys activity
-
搜 Recent tasks 查看已經存在的任務棧
Recent tasks: * Recent #0: TaskRecord{5bd8fdd #749 A=com.taskaffinity U=0 sz=1} * Recent #1: TaskRecord{a972e43 #748 A=com.activityfull U=0 sz=1} * Recent #2: TaskRecord{7a2180c #664 I=com.android.launcher3/.Launcher U=0 sz=1}
-
搜 Running activities (most recent first)查看activity
Running activities (most recent first): TaskRecord{25d22cd #684 A=com.activityfull U=0 sz=2} Run #1: ActivityRecord{8e55d0b u0 com.activityfull/.SecondActivity t684} Run #0: ActivityRecord{d26b857 u0 com.activityfull/.MainActivity t684}
棧的唯一標號是#682, sz 表示棧內有幾個組件, 棧頂的是 SecondActivity,
- Recent tasks查看手機程序的任務棧, != 應用數(一個應用可能包含多個棧), 更不是進程數
任務棧數 = 手機任務列表里顯示的數量 + com.android.launcher + com.android.systemuiACTIVITY MANAGER RECENT TASKS (dumpsys activity recents) Recent tasks: * Recent #0: TaskRecord{42a273f #682 A=com.activityfull U=0 sz=1} * Recent #1: TaskRecord{7a2180c #664 I=com.android.launcher3/.Launcher U=0 sz=1} * Recent #2: TaskRecord{8a01555 #667 A=com.android.systemui U=0 sz=1}
-
android:exported="true"
允許其他app打開這個activity
taskAffinity + allowTaskReparenting
- taskAffinity
設置activity進入指定完整棧名的棧,一般和singleTask一起使用- Application中設置(推薦activity設置), 不設定則默認包名, 設定了內部activity和Application一致
- Activity中設置, 如果指定的任務棧不存在, 那么會自己新開一個指定包名的任務棧
- allowTaskReparenting
是否依附與開啟這個activity的任務棧 - 舉例:
APP1內有activity1, activity2; APP2內有activity3和activity4, 當APP1 的 activity1 在 APP1 內打開這個 APP2 的 activity4 時,
-
<activity android:name=".FourActivity" android:exported="true" />
a. 只有 APP1 的任務棧打開, 存放的是 activity1 和 activity4;
b. 按home, 點開 APP1 看到的是 activity4, 按back, 看到activity1, 按back, 退出
c. 按home, 點開 APP2 看到的是 activity3, 按back, 退出 -
<activity android:name=".FourActivity" android:exported="true" android:allowTaskReparenting="true" />
a. 只有 APP1 的任務棧打開, 存放的是 activity1 和 activity4;
b. 按home, 點開 APP1 看到的是 activity1,
c. 按home, 點開 APP2 看到的是 activity4, 按back, 看到activity3, 按back, 退出 -
<activity android:name=".FourActivity" android:exported="true" android:allowTaskReparenting="true" android:launchMode="singleTask" />
a. APP1 任務棧存放 activity1, APP2 的任務棧存放activity4
b. 按home, 點開APP1 看到的是 activity1
c. 按home, 點開APP2 看到的是 activity4, 按back退出
參考