Activity的使用及生命周期探索:
- 1.概述:
- 2.组件的特点:
- 3.如何声明一个Activity.
- 4.如何启动一个Activity.
- 5. Activity的启动过程?(重点)
- 5. Intent的使用:
- 6.任务栈的结构:先进后出
- 7.LaunchMode:
- 8. Activity的生命周期。
- 9.不同的生命周期方法中,常做哪些操作。
- 10. Activity 生命周期探索
- 11. 熄屏下的生命周期
- 12. 横竖屏
- 13. 横竖屏切换的 Activity 生命周期变化?
- 13. 屏蔽用户横竖屏操作
- 14. 什么时候 activity 不走 onDestroy() ?
- 15. onSaveInstanceState() 方法的作用 ? 何时会被调用?
- 16. Activity常用的标记位Flags
- 17. Activity跟window,view之间的关系?
生命周期探索请直接移步 标题 10.
1.概述:
提供能够 ②与用户交互的 ①界面
2.组件的特点:
- 继承父类或实现接口
- 需要注册
- 对象,是系统创建的
- 生命周期方法的自动调用
3.如何声明一个Activity.
- 继承;
- 重写 onCreate() 等;
- 注册;
- 布局;
4.如何启动一个Activity.
-
startActivity(intent)
-
startActivityForResult
Activity1:startActivityForResult(int requestCode,Intent intent) Activity2:setResult(int resultCode,Intent intent); finish(); Activity1: onActivityResult(int requestCode,int resultCode,Intent intent)
5. Activity的启动过程?(重点)
- 点击 App 图标后通过 startActivity 远程调用到 AMS 中,AMS 中将新启动的 activity 以 activityrecord 的结构压入 activity 栈中,并通过远程 binder 回调到原进程,使得原进程进入 pause 状态,原进程 pause 后通知 AMS 它 pause 了;
- 此时 AMS 再根据栈中 Activity 的启动 intent 中的 flag 是否含有 new_task 的标签判断是否需要启动新进程,启动新进程通过 startProcessXXX 的函数;
- 启动新进程后通过反射调用 ActivityThread 的 main 函数,main 函数中调用 looper.prepar 和 lopper.loop 启动消息队列循环机制。最后远程告知AMS 我启动了。AMS 回调 handleLauncherAcitivyt 加载 activity。在 handlerLauncherActivity 中会通过反射调用 Application 的 onCreate 和 activity 的 onCreate 以及通过 handleResumeActivity 中反射调用 Activity 的 onResume;
5. Intent的使用:
Intent不是组件!意图,提供不同组件通信的信使。
-
要去哪?
显式意图Intent(obj,Class clazz),隐式意图Intent(String action) -
能带数据吗?
可以!不过这里的 finish() 不要忘;忘了的话也没关系……那你就按 back 键,不还是 finish() 嘛……putExtra(String key,Xxx xxx)---getXxxExtra(String key) setData(Uri uri) ---getData(); Uri uri = Uri.parse("tel:" + number); -->url (http:) ("content:" + )
-
隐式意图
在保证有权限访问的情况下,通过隐式 Intent 进行目标 Activity 的 IntentFilter 匹配,原则是:-
一个 intent 只有同时匹配某个 Activity 的 intent-filter 中的 action、category、data 才算完全匹配,才能启动该Activity;
-
一个 Activity 可以有多个 intent-filter,一个 intent 只要成功匹配任意一组 intent-filter,就可以启动该 Activity;
-
6.任务栈的结构:先进后出
- 不可以操作系统的栈结构;
- 如果确实需要操作,可以参考我的 P2P金融项目 笔记 02 提供的 AppManager 类;统一应用程序中所有的Activity的栈管理
7.LaunchMode:
-
standard ;标准模式;
每次启动一个Activity都会重新创建一个新的实例,不管这个实例是否已经存在,此模式的Activity默认会进入启动它的Activity所属的任务栈中;
-
singleTop 适用于 目标 activity 启动费时的情况,给目标 activity 设置 singleTop ,防止用户多次点击,创建多个活动 (那不更卡了……) ;
如果新 Activity 已经位于任务栈的栈顶,那么此Activity不会被重新创建,同时会回调 onNewIntent 方法,如果新Activity实例已经存在但不在栈顶,那么Activity依然会被重新创建;
-
singleTask 设置为此时,任务栈里只新建一个该活动;当栈顶的活动调用该活动时,自动清空栈中,目标活动上的所有活动;
只要 Activity 在一个任务栈中存在,那么多次启动此 Activity 都不会重新创建实例,并回调 onNewIntent 方法;
此模式启动Activity A,系统首先会寻找是否存在A想要的任务栈,如果不存在,就会重新创建一个任务栈,然后把创建好A的实例放到栈中;
-
singleInstance 新建一个栈单独存储;少见……
8. Activity的生命周期。
-
4 种状态:运行状态,暂停状态,停止状态,死亡状态。
不同状态之间,切换时,哪些生命周期方法调用; -
7 个生命周期方法。
onCreate():表示Activity正在被创建,常用来初始化工作, 比如调用 setContentView 加载界面布局资源,初始化Activity所需数据等; onRestart():表示Activity正在重新启动,一般情况下, 当前Acitivty从不可见重新变为可见时,OnRestart就会被调用; onStart():表示Activity正在被启动,此时Activity可见但不在前台, 还处于后台,无法与用户交互; onResume():表示Activity获得焦点, 此时Activity可见且在前台并开始活动,这是与onStart的区别所在; onPause():表示Activity正在停止,此时可做一些存储数据、停止动画等工作,但是不能太耗时, 因为这会影响到新Activity的显示,onPause必须先执行完,新Activity的onResume才会执行; onStop():表示 Activity 即将停止,可以做一些稍微重量级的回收工作, 比如注销广播接收器、关闭网络连接等,同样 不能太耗时; onDestroy():表示 Activity 即将被销毁,这是 Activity 生命周期中的最后一个回调, 常做回收工作、资源释放;
onCreate()— onDestroy():对象是否存在。(在对象整个生命周期过程中,只调用一次)
onStart()— onStop():当前activity是否可见
onResume() — onPause():当前的activity是否可操作 -
面试考查:
①Activity1–>Activity2.
②demo总结;
9.不同的生命周期方法中,常做哪些操作。
-
onCreate();setContentView(),视图的初始化,绑定监听
-
onDestroy();正常情况下,销毁一个activity对象之前调用。
-
onPause();当activity结束运行状态时,临时数据的保存。(不适合执行数据量较大的保存操作:数据库)
-
onSaveInstanceState() / onStop():用于保存数据(执行时间较长);onSaveInstanceState() 操作在onPause()和 onStop()之间;
10. Activity 生命周期探索
A. 普通启动第二个页面;
-
P1首先不可操作,
onPause()
-
而后P2创建;可视;可操作;
-
最后P1才不可见;
onStop()
MainActivity onPause() SecondActivity onCreate() SecondActivity onStart() SecondActivity onResume() MainActivity onStop()
-
在P2通过返回键;返回到P1;此时P1调用 onRestart() 或 onCreate()取决于是否 finish(),不过通过 返回键的最后,P2会 onDestroy;
SecondActivity onPause() MainActivity onRestart() MainActivity onStart() MainActivity onResume() SecondActivity onStop() SecondActivity onDestroy()
B. 带回调启动第二个页面;
-
从P1携带信息去P2时;这里的 onSaveInstanceState() 在 onStop() 之前执行;这也就决定了,在什么时候写数据保存操作;
MainActivity onPause() SecondActivity onCreate() SecondActivity onStart() SecondActivity onResume() MainActivity onSaveInstanceState() MainActivity onStop()
-
从P2携带信息回到 P1时;这里的重中之重是;P1的onActivityResult() 先 onResume() 执行; 以确保重新显示的时候,之前的数据已经恢复;
SecondActivity onPause() MainActivity onActivityResult() MainActivity onRestart() MainActivity onStart() MainActivity onResume() SecondActivity onStop() SecondActivity onDestroy()
C. 启动 DialogActivity
-
dialog 出现在最上层,P1 可见,不可操作;依次执行的是;
MainActivity onPause() //不执行 onStop() 因为其依然可视; MainActivity onSaveInstanceState()
-
返回到 P1
MainActivity onResume()
D. 启动 AlertDialog
-
效果如下:
-
但是 P1毫无波澜;
-
这是因为,AlertDialog 是 P1 的一部分;所以P1的生命周期没有改变;
代码如下,当启动 DialogActivity 时,启动的是另外一个 Activity;当启动 AlertDialog 时;传入的 参数是 “this”;并且此处也只能是 this;所以 P1 生命周期不改变;
public void startDialogActivity(View v) { startActivity(new Intent(this, DialogActivity.class)); } public void startDialog(View v) { new AlertDialog.Builder(this) .setTitle("测试") .setPositiveButton("确定", null) .setNegativeButton("取消", null) .show(); }
11. 熄屏下的生命周期
-
熄灭屏幕;先是不可操作,随后是不可见;二者之间时保存数据;
MainActivity onPause() MainActivity onSaveInstanceState() MainActivity onStop()
-
点亮屏幕;此时没有
onActivityResult()
;这个方法需要三个参数:onActivityResult(int requestCode, int resultCode, Intent data)
;MainActivity onRestart() MainActivity onStart() MainActivity onResume()
12. 横竖屏
-
解决横竖屏造成的 activity 生命周期方法重新调用:
<activity android:name=".MainActivity" android:configChanges="keyboardHidden|screenSize|orientation" />
13. 横竖屏切换的 Activity 生命周期变化?
-
不设置 Activity 的 android:configChanges 时,切屏会销毁当前 Activity,然后重新加载调用各个生命周期,切横屏时会执行一次,切竖屏时会执行两次;
onPause() →onStop()→onDestory()→onCreate()→onStart()→onResume()
-
设置 Activity 的
android:configChanges="orientation"
,经过机型测试-
在 Android5.1 即 API 23 级别下,切屏还是会重新调用各个生命周期,切横、竖屏时只会执行一次
-
在 Android9 即 API 28 级别下,切屏不会重新调用各个生命周期,只会执行 onConfigurationChanged 方法
-
官方纠正后,原话如下
如果您的应用面向 Android 3.2 即API 级别 13 或更高级别(按照 minSdkVersion 和 targetSdkVersion
属性所声明的级别),则还应声明 “screenSize” 配置,因为当设备在横向与纵向之间切换时,该配置也会发生变化。即便是在 Android 3.2 或更高版本的设备上运行,此配置变更也不会重新启动 Activity
o
-
-
设置 Activity 的
android:configChanges="orientation|keyboardHidden|screenSize"
时,机型测试通过,切屏不会重新调用各个生命周期,只会执行 onConfigurationChanged 方法;
13. 屏蔽用户横竖屏操作
屏蔽掉横竖屏的切换操作,这样就不会出现切换的过程中Activity生命周期重新加载的情况了,具体做法是,在Activity中加入如下语句:
android:screenOrientation="portrait"
始终以竖屏显示android:screenOrientation="landscape"
始终以横屏显示
如果不想设置整个软件屏蔽横竖屏切换,只想设置屏蔽某个Activity的横竖屏切换功能的话,只需要下面操作:
Activity.this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
以竖屏显示Activity.this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
以横屏显示
当你横竖屏切换的时候,如果走了销毁Activity的流程,那么需要保存当前和恢复当前Activity的状态的话,我们可以灵活运用 onSaveInstanceState() 方法和 onRestoreInstanceState() 方法。
14. 什么时候 activity 不走 onDestroy() ?
当用户后台强杀应用程序时,当前返回栈仅有一个activity实例时,这时候,强杀,是会执行onDestroy方法的;
当返回栈里面存在多个Activity实例时,栈底的 activity 执行会ondestroy方法,其他的不会执行;
比如说:从mainactivity跳转到activity-A(或者继续从activity-A再跳转到activity-B),这时候,从后台强杀,只会执行mainactivity的onDestroy方法,activity-A(以及activity-B)的onDestroy方法都不会执行;
15. onSaveInstanceState() 方法的作用 ? 何时会被调用?
-
发生条件:异常情况下
(系统配置发生改变时导致 Activity 被杀死并重新创建、资源内存不足导致低优先级的 Activity 被杀死)
系统会调用 onSaveInstanceState 来保存当前 Activity 的状态,此方法调用在 onStop 之前,与 onPause 没有既定的时序关系; -
当Activity被重建后,系统会调用 onRestoreInstanceState,并且把 onSaveInstanceState 方法所保存的 Bundle 对象同时传参给 onRestoreInstanceState 和 onCreate() ,因此可以通过这两个方法判断 Activity 是否被重建,调用在 onStart 之后;
16. Activity常用的标记位Flags
-
FLAG_ACTIVITY_NEW_TASK : 对应singleTask启动模式,其效果和在XML中指定该启动模式相同;
-
FLAG_ACTIVITY_SINGLE_TOP : 对应singleTop启动模式,其效果和在XML中指定该启动模式相同;
-
FLAG_ACTIVITY_CLEAR_TOP : 具有此标记位的 Activity,当它启动时,在同一个任务栈中所有位于它上面的 Activity 都要出栈。这个标记位一般会和 singleTask 模式一起出现,在这种情况下,被启动 Activity 的实例如果已经存在,那么系统就会回调 onNewIntent。
如果被启动的 Activity 采用 standard 模式启动,那么它以及连同它之上的 Activity 都要出栈,系统会创建新的Activity实例并放入栈中;
-
FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS : 具有这个标记的 Activity 不会出现在历史 Activity 列表中;
17. Activity跟window,view之间的关系?
- Activity 创建时通过 attach() 初始化了一个 Window 也就是 PhoneWindow,
- 一个 PhoneWindow 持有一个 DecorView 的实例,DecorView 本身是一个 FrameLayout,继承于View,
- Activty 通过 setContentView 将 xml 布局控件不断 addView() 添加到 View 中,最终显示到 Window 于我们交互;