[2018_android_2]Activity之lifecycle_TBD25html
感謝各位大牛分享精彩內容,幫助了我快速學習。
Thank Ujava
Visible
, Invisible
, Background
, and Foreground
? [TBD]
Fragment
生命週期結合Activity生命週期? Activity
的啓動過程源碼比較複雜,涉及到Instrumentation
, ActivityThread
and ActivityManagerSercice(AMS)
。 [TBD] Window
、Dialog
、Toast
和Activity
的關係 android
正常狀況(又稱典型狀況)下的生命週期,指的是用戶參時,Activity所經歷的生命週期改變。
正常狀況下的生命週期 ,主要關注生命週期函數的調用順序,在每一個生命週期要作什麼?不能作什麼?git
[TBD] Visible
, Invisible
, Background
, and Foreground
?
Visible:表示activity可見狀態,即activity已經顯示出來了。
Invisible:表示activity不可見狀態,即activity沒有顯示出來了。
Background:activity位於後臺,即沒法與用戶交互。
Foreground :activity位於前臺,便可以與用戶交互。github
Activity lifecycle
Figure 1. A simplified illustration of the activity lifecycle. 網絡
https://developer.android.google.cn/guide/components/activities/activity-lifecycle.htmlapp
// A -> B D/A: onPause D/B: onCreate: D/B: onStart D/B: onResume D/A: onSaveInstanceState D/A: onStop // A <- B D/B: onPause: D/A: onRestart: D/A: onStart D/A: onResume D/B: onStop: D/B: onDestroy:
[A] 繼續執行,直到執行完畢,或者當app進程被殺死,未執行完的線程被強制中止。 ide
執行onDestroy()後,未執行完的線程,繼續執行。函數
// Test on Nenux 5X, Android 8.0, API 26 // A -> B,而後A <- B後, // 執行onDestroy()後,未執行完的線程還會繼續執行: D/AActivity: onStart D/AActivity: onResume D/BActivity: run: i=9 D/BActivity: onStop: D/BActivity: onDestroy: I/zygote64: Do partial code cache collection, code=23KB, data=28KB I/zygote64: After code cache collection, code=23KB, data=28KB I/zygote64: Increasing code cache capacity to 128KB D/BActivity: run: i=10 D/BActivity: run: i=11 D/BActivity: run: i=12
當用戶從Recent list close app,未執行完的線程被強制中止,由於整個app進程被殺死,分配的全部資源被OS回收。工具
用戶從Recent list close app D/BActivity: run: i=590 D/BActivity: run: i=591 D/BActivity: run: i=592 // 用戶從Recent list close app,未執行完的線程中止運行 D/AActivity: onPause D/AActivity: onSaveInstanceState D/AActivity: onStop D/BActivity: run: i=593
[TBD]
Fragment
生命週期結合Activity生命週期?
// Open app, A is mainActivity D/A: onCreate: D/A: onStart D/A: onResume // A -> B D/A: onPause D/B: onCreate: D/B: onStart D/B: onResume D/A: onStop // A <- B D/B: onPause: D/A: onRestart: D/A: onStart D/A: onResume D/B: onStop: D/B: onDestroy: // Press back , A -> Homescreen, exit A and APP D/A: onPause D/A: onStop D/A: onDestroy
對於單個Activity,such as A:
與啓動 normal Activity相比,啓動Float Activity或Translucent時,A 僅僅執行onPause,不執行onStop.
Back to A時,A 不執行OnStart,僅僅執行onResume.
Note: 沒有特殊說明,要跳轉的Activity是普通Activity,不是Float/Translucent activity。
// Open app, A is mainActivity D/A: onCreate: D/A: onStart D/A: onResume // A -> TranslucentB D/A: onPause D/TranslucentB: onCreate: D/TranslucentB: onStart D/TranslucentB: onResume // A <- TranslucentB D/TranslucentB: onPause: D/A: onResume D/TranslucentB: onStop: D/TranslucentB: onDestroy: // Press back , A -> Homescreen, exit A and APP D/A: onPause D/A: onStop D/A: onDestroy
// Open app, A is mainActivity D/A: onCreate: D/A: onStart D/A: onResume // A -> FloatB D/A: onPause D/FloatB: onCreate: D/FloatB: onStart D/FloatB: onResume // A <- FloatB D/FloatB: onPause: D/A: onResume D/FloatB: onStop: D/FloatB: onDestroy: // Press back , A -> Homescreen, exit A and APP D/A: onPause D/A: onStop D/A: onDestroy
跟跳轉到本身 app的其餘Activity是同樣的。
// Open app, A is MainActivity D/A: onCreate: D/A: onStart D/A: onResume // Screen OFF ,由於跳轉到其餘activity(Locks Screen app的Activity) D/A: onPause D/A: onSaveInstanceState D/A: onStop // Screen ON D/A: onRestart: D/A: onStart D/A: onResume
這兩個配對的回調分別表明不一樣的意義。
onStart和onStop是從Activity是否可見角度來回調。
onResume和onPause是從Activity是否位於前臺角度來回調。
除了這種區別,在實際使用當中沒有其餘明顯區別。
A:
// A -> B D/A: onPause D/B: onCreate: D/B: onStart D/B: onResume D/A: onStop
[TBD] Activity的啓動過程源碼比較複雜,涉及到Instrumentation, ActivityThread and ActivityManagerSercice(AMS)。
如何簡單理解?
啓動Activity的請求會由Instrumentation來處理, 而後Instrumentation經過Binder 向AMS發送請求,AMS內部維護一個ActivityStack並負責棧內的Activity的狀態同步,AMS經過ActivityThread去同步Activity的狀態而完成生命週期方法的調用。
圖Instrumentation經過Binder 向AMS發送請求:
[TBD1]源碼中的紅色錯誤代碼是怎麼回事
圖ActivityStack.java - resumeTopActivityInnerLocked()
ActivityStackSupervisor.java - realStartActivityLocked()
IApplicationThread thread ,實現是ActivityThread中的ApplicationThread。
這段代碼其實是調用ApplicationThread中的scheduleLaunchActivity(),scheduleLaunchActivity()最終完成新Activity的onCreate,onStart,onResume。
圖ActivityStackSupervisor.java - realStartActivityLocked()
圖 源碼ApplicationThread.java # scheduleLaunchActivity()
A -> B ,A 中onStop模擬耗時操做:開啓一個Thread,打印從1~1000的整數,每次打印一個整數,Thread.sleep 1 second.
A 在onPause中模擬耗時操做也是同樣的。由於是在一個子線程裏面執行,對生命週期函數的執行沒有影響。
@Override protected void onStop() { Log.d(TAG, "onStop"); // 不管super.onStop() 先後調用testIfInOnStopCanDoHeavyWork,log都是同樣的。 testIfInOnStopCanDoHeavyWork(); super.onStop(); // testIfInOnStopCanDoHeavyWork(); } private void testIfInOnStopCanDoHeavyWork() { if (null != counter) { return; } counter = new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 1000; i++) { Log.d(TAG, "run: i=" + i); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }); counter.start(); }
//Open app, A is Main activity. D/A: onCreate D/A: onStart D/A: onResume // A -> B D/A: onPause D/B: onCreate: D/B: onStart D/B: onResume D/A: onStop D/A: run: i=0 D/A: run: i=1 D/A: run: i=2 D/A: run: i=3 D/A: run: i=4 D/A: run: i=5 D/A: run: i=6 D/A: run: i=7 D/A: run: i=8 D/A: run: i=9 D/A: run: i=10 D/A: run: i=11 D/A: run: i=12 D/A: run: i=13 D/A: run: i=14 D/A: run: i=15 D/A: run: i=16 D/A: run: i=17 D/A: run: i=18
<video id="video" controls="" preload="none" poster="http://media.w3.org/2010/05/sintel/poster.png"> <p>Check if onStop can do heavy work. </p> <source id="mp4" src="https://yingvickycao.github.io/img/android/app_component/activity/life_cycle/chec_if_can_do_heavy_work_in_onStop_4_app.mp4" type="video/mp4"> <source id="mp4" src="https://yingvickycao.github.io/img/android/app_component/activity/life_cycle/chec_if_can_do_heavy_work_in_onStop_4_logcat.mp4" type="video/mp4"> </video>
Check if onStop can do heavy work.
Check if onStop can do heavy work.
異常狀況下的生命週期,指的是Activity被系統回收或者因爲當前設備的Configuration改變時,致使Activity被銷燬所經歷的生命週期改變。
TBD[2], 瞭解系統的資源加載機制
把圖片放入drawable目錄後,經過Resource去獲取圖片。爲了兼容不一樣設備,可能須要在其餘目錄防止不一樣的圖片:drawable-mdpi,drawable-hdpi,drawable-land等。 layout也是相似:layout,layout-large,layout-land等。
當app啓動時,系統會根據當前設備的狀況去加載合適的Resource資源,例如橫屏和豎屏分別拿landscape和portrait狀態下的資源:layout、圖片.... 。
當旋轉屏幕,因爲系統配置發生了改變,在默認狀況下,Activity 會被銷燬並從新建立。 也能夠阻止系統從新建立Activity。
打開 auto-ratate模式
<!-- activity沒有設置跟資源配置相關的屬性 --> <activity android:name=".app_component.activity.lifecycle.A"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter>
layout文件夾包含 landscape(橫屏)和portrait(豎屏)狀態的layout。
layout-landscape僅僅包含landscape狀態的layout。
layout-portrait僅僅包含portrait狀態的layout。
//Open app, A is Main activity. D/A: onCreate: D/A: onStart D/A: onResume // 旋轉屏幕 D/A: onPause D/A: onSaveInstanceState D/A: onStop D/A: onDestroy D/A: onCreate: D/A: [onCreate]restore extra_test:test D/A: onStart D/A: [onRestoreInstanceState]restore extra_test:test D/A: onResume
系統只在Activity異常終止的時候纔會調用onSaveInstanceState與onRestoreInstanceState來儲存和恢復數據,其餘狀況不會觸發這個過程。可是按Home鍵或者啓動新Activity仍然會單獨觸發onSaveInstanceState的調用:由於用戶可能再也不回來了,要作現場保護。
@Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); Log.d(TAG, "onSaveInstanceState"); outState.putString(INSTANCE_STATE_KEY, "test"); }
二者的區別:
若是是正常啓動, onCreate的參數savedInstanceState = null,必須判斷是否爲null。
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Log.d(TAG, "onCreate: "); setContentView(R.layout.activity_app_component_activity_lifecycle_a_layout); if (savedInstanceState != null) { String test = savedInstanceState.getString(INSTANCE_STATE_KEY); Log.d(TAG, "[onCreate]restore extra_test:" + test); } } @Override protected void onRestoreInstanceState(Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); String test = savedInstanceState.getString(INSTANCE_STATE_KEY); Log.d(TAG, "[onRestoreInstanceState]restore extra_test:" + test); }
onSaveInstanceState 保存數據,onRestoreInstanceState 恢復數據。
緣由: OS只有在Activity異常終止時纔會調用onSaveInstanceState 和 onRestoreInstanceState。
在onSaveInstanceState和onRestoreInstanceState中,系統自動會作View的相關狀態的保存和恢復工做,好比 文本框的數據、ListView滾動位置等。
具體特定View能恢復什麼數據,要View的onSaveInstanceState和onRestoreInstanceState源碼。
VIew.java實現了onSaveInstanceState和onRestoreInstanceState函數,它的派生類會重寫這兩個函數實現保存和恢復特定數據。
採用委託思想,上層委託下層、父容器委託子元素去處理一個事情。
委託思想在Android 有不少應用,View的繪圖過程、事件分發等採用相似的思想。
Step1: Activity 被意外終止,Activity會調用onSaveInstanceState保存數據,而後Activity會委託Window去保存數據。
Step2: Window 委託它上面的頂級容器去保存數據。頂級容器是一個ViewGroup,通常來講多是DecorView。
Step3: 頂級容器再去一一通知它的子元素來保存數據。
經過委託,完成整個數據保存工程。
[TBD] 檢查API有沒有更新,並對每個屬性進行測試 。國內android官網沒有搜到 configChanges結果
經過爲Activity設置configChanges屬性,當屏幕旋轉時Activity不銷燬並從新建立。
A 添加 android:configChanges="orientation"屬性,當屏幕旋轉時,A不會從新建立, 不會調用onSaveInstanceState 和 onRestoreInstanceState來保存和恢復數據,僅執行onConfigurationChanged
AndroidManifest.xml
<activity android:name=".app_component.activity.lifecycle.A" android:configChanges="orientation"/>
A.java
@Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); // 2 == ORIENTATION_LANDSCAPE // 1 == ORIENTATION_PORTRAIT // 0 == ORIENTATION_UNDEFINED Log.d(TAG, "onConfigurationChanged, newOrientation:" + newConfig.orientation); }
D/A: onConfigurationChanged, newOrientation:1 D/A: onConfigurationChanged, newOrientation:2 D/A: onConfigurationChanged, newOrientation:1
經過設置setRequestedOrientation設置爲橫屏或豎屏。
這種方式,屏幕不會旋轉,什麼函數也不會調用,Activity不銷燬並從新建立。
DisableRotateActivity.java
@Override protected void onCreate(@Nullable Bundle savedInstanceState) { setTitle(R.string.activity_disable_Rotate); super.onCreate(savedInstanceState); // 禁止橫豎屏轉換,設置屏幕方向爲豎屏 //setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); // 禁止橫豎屏轉換,設置屏幕方向爲橫屏 setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); }
DisableRotateActivity.java
@Override protected void onCreate(@Nullable Bundle savedInstanceState) { setTitle(R.string.activity_disable_Rotate); super.onCreate(savedInstanceState); // 手機時,豎屏; 平板時,容許轉屏,容許從新建立Activity boolean isTablet = isTablet(); Log.d(TAG, "onCreate: isTablet=" + isTablet); setRequestedOrientation(isTablet ? ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED : ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); } private boolean isTablet() { String screen = getString(R.string.screen); Log.d(TAG, "isTablet: screen=" + screen); return ("xlarge-land".equalsIgnoreCase(screen) || "xlarge".equalsIgnoreCase(screen)); }
// 啓動,就是橫屏 D/DisableRotateActivity: isTablet: screen=xlarge-land D/DisableRotateActivity: onCreate: isTablet=true D/DisableRotateActivity: onStart D/DisableRotateActivity: onResume // 橫屏 -> 豎屏 D/DisableRotateActivity: onConfigurationChanged: 1 D/DisableRotateActivity: onConfigurationChanged: ORIENTATION_PORTRAIT D/DisableRotateActivity: onPause D/DisableRotateActivity: onSaveInstanceState D/DisableRotateActivity: onStop D/DisableRotateActivity: onDestroy D/DisableRotateActivity: isTablet: screen=xlarge D/DisableRotateActivity: onCreate: isTablet=true D/DisableRotateActivity: onStart D/DisableRotateActivity: [onRestoreInstanceState]restore extra_test:test D/DisableRotateActivity: onResume // 豎屏 -> 橫屏 D/DisableRotateActivity: onConfigurationChanged: 2 D/DisableRotateActivity: onConfigurationChanged: ORIENTATION_LANDSCAPE D/DisableRotateActivity: onPause D/DisableRotateActivity: onSaveInstanceState D/DisableRotateActivity: onStop D/DisableRotateActivity: onDestroy D/DisableRotateActivity: isTablet: screen=xlarge-land D/DisableRotateActivity: onCreate: isTablet=true D/DisableRotateActivity: onStart D/DisableRotateActivity: [onRestoreInstanceState]restore extra_test:test D/DisableRotateActivity: onResume
Activity的優先級按照從高到低的順序,分爲3種:
當系統內存不足時,系統就會按照上述優先級去殺死目標Activity所在的進程,並在後面調用onSaveInstanceState和onRestoreInstanceState來存儲和恢復數據。
當app 沒有四大組件在執行,進程會很快被OS(系統)殺死。
內存資源很低時,當把app放在後臺,MainActivity就被銷燬了,可是app進程還在。過了2分鐘,app進程也被殺死了。
Force Stop,app 進程被殺死時,not invork any lifecycle function。
從Recent list中重啓qpp,app進程?不是原來的進程號,是一個新的進程。
// Open app, A is mainActivity D/A: onCreate: D/A: onStart D/A: onResume // A -> B D/A: onPause D/B: onCreate: D/B: onStart D/B: onResume D/A: onStop // B -> HomeScreen -> Settings -> AppsInfo D/B: onPause: D/B: onSaveInstanceState D/B: onStop: // In AppsInfo app, force Stop My app. app 進程已經殺死了。 // No log . So not invork any lifecycle function. // Reopen app from recent list, 從新重啓qpp, app進程不是原來的進程,是一個新的進程。 D/A: onCreate: D/A: onStart D/A: onResume
<video id="video" controls="" preload="none" poster="http://media.w3.org/2010/05/sintel/poster.png"> <p>force Stop My app </p> <source id="mp4" src="https://yingvickycao.github.io/img/android/app_component/activity/life_cycle/force_stop_app_video_1.mp4" type="video/mp4"> <source id="mp4" src="https://yingvickycao.github.io/img/android/app_component/activity/life_cycle/force_stop_app_video_2.mp4" type="video/mp4"> </video>