[2018_android_2]Activity之lifecycle_TBD25
参考
- 《Android开发艺术探索》作者:任玉刚 第1章 Activity的生命周期和启动模拟
- https://developer.android.google.cn/guide/components/activities/activity-lifecycle.html
- https://developer.android.google.cn/guide/components/activities/index.html
- https://developer.android.google.cn/guide/components/activities/state-changes.html
- https://developer.android.google.cn/guide/components/activities/tasks-and-back-stack.html
- https://developer.android.google.cn/guide/components/activities/process-lifecycle.html
- https://developer.android.google.cn/guide/components/activities/parcelables-and-bundles.html
- https://developer.android.google.cn/guide/components/activities/recents.html
- http://android-developers.blogspot.com/2011/06/things-that-cannot-change.html
- https://developer.android.google.cn/guide/topics/manifest/activity-element.html
- https://developer.android.google.cn/guide/components/intents-filters.html
感谢各位大牛分享精彩内容,帮助了我快速学习。
Thank U
TBD
- [TBD] https://developer.android.google.cn/guide/components/activities/activity-lifecycle.html
- [TBD] https://developer.android.google.cn/guide/components/activities/index.html
- [TBD] https://developer.android.google.cn/guide/components/activities/state-changes.html
- [TBD] https://developer.android.google.cn/guide/components/activities/tasks-and-back-stack.html
- [TBD] https://developer.android.google.cn/guide/components/activities/process-lifecycle.html
- [TBD] https://developer.android.google.cn/guide/components/activities/parcelables-and-bundles.html
- [TBD] https://developer.android.google.cn/guide/components/activities/recents.html
- [TBD] http://android-developers.blogspot.com/2011/06/things-that-cannot-change.html
- [TBD] https://developer.android.google.cn/guide/topics/manifest/activity-element.html
- [TBD] https://developer.android.google.cn/guide/components/intents-filters.html
- [TBD] Window、Dialog、Toast和Activity的关系
- [TBD]
Visible
,Invisible
,Background
, andForeground
? [TBD]
Fragment
生命周期结合Activity生命周期?- [TBD]
Activity
的启动过程源码比较复杂,涉及到Instrumentation
,ActivityThread
andActivityManagerSercice(AMS)
。 - [TBD1]源码中的红色错误代码是怎么回事
- [TBD] 选择1: onPause.
- [TBD]保存数据和恢复数据的推荐组合?
- [TBD] onSaveInstanceState 中存储数据的大小限制是多少?
- [TBD] 关于保存和恢复的层次结构,系统的工作流程:
- [TBD] 检查API有没有更新,并对每一个属性进行测试 。国内android官网没有搜到 configChanges结果
- TBDScreenSize和SmallScreenSize 比较特殊, 行为和编译选项有关,和运行环境无关。
- [TBD]方案4:网络请求没有返回之前,不允许方向变化。 返回后,变化方向?
- [TBD] 如何模拟内存不足的情况?目前网上的方法都没有效果。
- [TBD] Activity的优先级排序?有API callback函数可以调用吗?
环境
- [Code Base] https://github.com/YingVickyCao/android-about-demos/tree/activity_lifecycle/app/src/main/java/com/hades/android/example/android_about_project/app_component/activity/lifecycle
- classpath 'com.android.tools.build:gradle:3.0.0'
- compile 'com.android.support:appcompat-v7:26.+'
- android studio 3.0
- gradle 4.1
- Skitch 截图工具
- jietu 截图工具
- OS: mac OS 10.13.1 (17B1003)
Activity生命周期
[TBD] Window
、Dialog
、Toast
和Activity
的关系
正常情况(又称典型情况)下的生命周期
正常情况(又称典型情况)下的生命周期,指的是用户参时,Activity所经历的生命周期改变。
正常情况下的生命周期 ,主要关注生命周期函数的调用顺序,在每个生命周期要做什么?不能做什么?
Acivity lifecycle about functions
[TBD] Visible
, Invisible
, Background
, and Foreground
?
Visible:表示activity可见状态,即activity已经显示出来了。
Invisible:表示activity不可见状态,即activity没有显示出来了。
Background:activity位于后台,即无法与用户交互。
Foreground :activity位于前台,即可以与用户交互。
Activity lifecycle
Figure 1. A simplified illustration of the activity lifecycle.
https://developer.android.google.cn/guide/components/activities/activity-lifecycle.html
结合“A simplified illustration of the activity lifecycle”,具体说明。
情况1: A 跳到B,然后B 按导航Back键,返回到A
// 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:
- [Q] 执行onDestroy()后,未执行完的线程还会继续执行吗?
[A] 继续执行,直到执行完毕,或者当app进程被杀死,未执行完的线程被强制停止。
执行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生命周期?
情况2:A -> B, B -> A, A back to Homescreen
// 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:
- 第一次启动时, onCreate -> onStart -> onResume.
- 跳转到其他activity时,onPause -> onStop.
- 再次回来时,onRestart -> onStart -> onResume.
- 按Back键退出时,onPause -> onStop -> onDestroy
- 从Activity实例上来看,onCreate和onDestroy()是配对的,代表Activity的创建与销毁,只能调用一次。
- 从Activity 是否可见来看,onStart()与onStop()是配对的,这两个方法可能被调用多次。
- 从Activity是否前台来看,onResume()与onPause()是配对的,这两个方法可能被调用多次。
- onSaveInstanceState 不是生命周期函数。
情况3:A -> Float / Translucent B, Float / Translucent B -> A, A back to Homescreen
- Q: 打开Float(悬浮) Activity ,translucent(半透明) Activity 与普通Activity的生命周期区别?
与启动 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
情况4:A -> Screen OFF -> Screen ON
跟跳转到自己 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,onResume和onPause有什么实质不同吗?
这两个配对的回调分别代表不同的意义。
onStart和onStop是从Activity是否可见角度来回调。
onResume和onPause是从Activity是否位于前台角度来回调。
除了这种区别,在实际使用当中没有其他明显区别。
Q:为什么onPause和onStop不能做耗时操作,只能做轻量级操作?
A:
- 因为之后Old activity onPause()执行完后,才能执行New activity onResume()。
- onPause和onStop都不能执行耗时操作,尤其是onPause。应尽量在onStop中做操作,从而使得新的Activity尽快显示出来并切换到前台。
Q: A -> B, B的onResume()和A 的onPause哪个先执行?
- Old activity 先onPause, New activity onCreate -> onStart -> onResume,Old Activity 再onStop。
从生命周期函数调用顺序角度, 验证。
// 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()
验证 onPause和onStop都不能执行耗时操作,尤其是onPause。应尽量在onStop中做操作,从而使得新的Activity尽快显示出来并切换到前台。
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
Check if onStop can do heavy work.
Check if onStop can do heavy work.
异常情况下的生命周期
异常情况下的生命周期,指的是Activity被系统回收或者由于当前设备的Configuration改变时,导致Activity被销毁所经历的生命周期改变。
什么时候回出现异常情况下的生命周期?
- 当资源相关的系统配置发生改变,Activity被杀死。
- 当系统内存不足时,Activity可能被杀死。
情况1: 当资源相关的系统配置发生改变,导致Activity被杀死并重新创建。
TBD[2], 了解系统的资源加载机制
把图片放入drawable目录后,通过Resource去获取图片。为了兼容不同设备,可能需要在其他目录防止不同的图片:drawable-mdpi,drawable-hdpi,drawable-land等。 layout也是类似:layout,layout-large,layout-land等。
当app启动时,系统会根据当前设备的情况去加载合适的Resource资源,例如横屏和竖屏分别拿landscape和portrait状态下的资源:layout、图片.... 。
当旋转屏幕,由于系统配置发生了改变,在默认情况下,Activity 会被销毁并重新创建。 也可以阻止系统重新创建Activity。
默认情况下,Activity 会被销毁并重新创建。
打开 auto-ratate模式
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
onSaveInstanceState and onRestoreInstanceState 执行顺序?
- onSaveInstanceState 在onPause之后,onStop()之前执行。
- onRestoreInstanceState 在onStart之后, onResume之前执行。
系统只在Activity异常终止的时候才会调用onSaveInstanceState与onRestoreInstanceState来储存和恢复数据,其他情况不会触发这个过程。但是按Home键或者启动新Activity仍然会单独触发onSaveInstanceState的调用:因为用户可能不再回来了,要做现场保护。
如何恢复之前保存的使用?
如何保存数据:
- [TBD] 选择1: onPause.
- 选择2:onSaveInstanceState。
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
Log.d(TAG, "onSaveInstanceState");
outState.putString(INSTANCE_STATE_KEY, "test");
}
如何恢复数据?
- 选择1: onCreate
- 选择2: onRestoreInstanceState
两者的区别:
如果是正常启动, 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);
}
[TBD]保存数据和恢复数据的推荐组合?
onSaveInstanceState 保存数据,onRestoreInstanceState 恢复数据。
原因: OS只有在Activity异常终止时才会调用onSaveInstanceState 和 onRestoreInstanceState。
[TBD] onSaveInstanceState 中存储数据的大小限制是多少?
在onSaveInstanceState和onRestoreInstanceState中,系统自动会做View的相关状态的保存和恢复工作,比如 文本框的数据、ListView滚动位置等。
具体特定View能恢复什么数据,要View的onSaveInstanceState和onRestoreInstanceState源码。
VIew.java实现了onSaveInstanceState和onRestoreInstanceState函数,它的派生类会重写这两个函数实现保存和恢复特定数据。
[TBD] 关于保存和恢复的层次结构,系统的工作流程:
采用委托思想,上层委托下层、父容器委托子元素去处理一个事情。
委托思想在Android 有很多应用,View的绘图过程、事件分发等采用类似的思想。
Step1: Activity 被意外终止,Activity会调用onSaveInstanceState保存数据,然后Activity会委托Window去保存数据。
Step2: Window 委托它上面的顶级容器去保存数据。顶级容器是一个ViewGroup,一般来说可能是DecorView。
Step3: 顶级容器再去一一通知它的子元素来保存数据。
通过委托,完成整个数据保存工程。
configChanges的含义
[TBD] 检查API有没有更新,并对每一个属性进行测试 。国内android官网没有搜到 configChanges结果
- 常见的3个选项:local、orientation和keyboardHidden。
- TBDScreenSize和SmallScreenSize 比较特殊, 行为和编译选项有关,和运行环境无关。
Q:如何不转屏,或者转屏时不销毁?
方式1:[允许转屏,但不销毁Activity]
通过为Activity设置configChanges属性,当屏幕旋转时Activity不销毁并重新创建。
A 添加 android:configChanges="orientation"属性,当屏幕旋转时,A不会重新创建, 不会调用onSaveInstanceState 和 onRestoreInstanceState来保存和恢复数据,仅执行onConfigurationChanged
AndroidManifest.xml
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
方案2:[不转屏]
通过设置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);
}
方案3:通过设置setRequestedOrientation设置为手机时竖屏,屏幕不会旋转,什么函数也不会调用; 平板时,允许转屏,允许重新创建Activity。
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
[TBD]方案4:网络请求没有返回之前,不允许方向变化。 返回后,变化方向?
情况2: 当OS内存不足,导致低优先级的Activity被杀死。
[TBD] 如何模拟内存不足的情况?目前网上的方法都没有效果。
[TBD] Activity的优先级排序?有API callback函数可以调用吗?
Activity的优先级按照从高到低的顺序,分为3种:
- Foreground Activity(前台Activity):优先级最高。用户正在和用户交互。执行完了onResume。
- Visible but background Activity(可见但非前台Activity): Activity可见但位于后台无法和用户直接交互。 例如:Activity弹出一个对话框。
- Background Activity(后台Activity):已经被暂停的Activity。比如执行了onStop,优先级最低。
当系统内存不足时,系统就会按照上述优先级去杀死目标Activity所在的进程,并在后面调用onSaveInstanceState和onRestoreInstanceState来存储和恢复数据。
当app 没有四大组件在执行,进程会很快被OS(系统)杀死。
如何降低app 进程被杀死的概率?
- 将后台工作放入Service来保证进程有一定的优先级,在一定程度上能降低被OS杀死的概率。
- [PASS]事实上,在Android 8.0 API26 上,app 进入background,然后开几个耗内存的app,比如Chrome、Photo,过几分钟Activity 被杀死,甚至app进程被杀死。
内存资源很低时,当把app放在后台,MainActivity就被销毁了,但是app进程还在。过了2分钟,app进程也被杀死了。
- [PASS]OS >= Android 6.0 API 24 时,以前Service 保活的方法将不再有效。
- [PASS]OS < Android 6.0 API 24, 以前Service 保活的方法将有效。
- [PASS]在OS内存不足时,可能导致低优先级的Activity被杀死但app进程还活着,也可能整个app进程被OS杀死。
情况3: Use Settings -> AppsInfo app to force 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
force Stop My app
force Stop My app