1.Activity生命周期
1.正常情况:
(1) onCreate:
表示
Activty
正在被创建,这是Activity
生命周期的第一个方法,可以做一些初始化的工作,比如:加载布局,绑定控件,初始化数据等等。
(2) onRestart:
表示
Activity
正在重新启动,通常由Activty
由不可见变为可见时被调用。触发场景:按 Home 键进入桌面,或者启动一个新的
Activity
,这时当前Activity
被暂停,然后又回到了该Activity
,此时会触发onRestart
。
(3) onStart:
表示当前
Activity
正在被启动,此时Activity
已经可见了,但是还未出现在前台,无法与用户交互,用户是看不到的。
(4) onResume:
当前
Activity
已经可见,出现在前台并且开始活动,onStart
和onResume
都表示 Activity 可见,onStart
时候 Activity 还在后台,onResume
的时候 Activity 才显示到前台。
(5) onPause:
表示当前 Activity 正在停止,此时还是可见,正常情况下,接着就会调用
onStop
方法。可以做一些数据存储,动画停止等轻量级回收工作,不能太耗时。因为这会影响到新 Activity 的显示,只有onPause
执行完,新 Activity 的onResume
方法才会执行。注意:例如弹出一个非全屏
Dialog
或者启动一个透明的 Activty,那么当前onStop
方法不会调用。
(6) onStop:
表示 Activity 即将停止,不可见,可以做一些稍微重量级的会后工作,同样不能太耗时。
(7) onDeatroy:
表示 Activity 即将被销毁,这是 Activity 生命周期的最后一个回调,可以做一些回收工作和最终的资源释放。
生命周期流程图
拓展问题:
1.onStart
和onResume
,onPause
和onStop
有什么实质上的不同?
onStart
和onStop
是从 Activity 是否可见角度来回调的,而onResume
和onPause
是从Activity是否位于前台来回调的。
2.A 启动 B,那么 B 的 onResume
和 A 的 onPause
哪个先执行?
结论:A 先执行
onPause()
方法,紧接着 B 执行onCreate() -> onSatar() -> onResume()
方法,接着执行 A的onStop()
原因:
启动 Activity 的请求有Instrumentation
通过Binder
接口向AMS
发送请求,AMS 内部维护着一个ActivityStack
并负责栈内Activity
的状态同步,接着AMS
通过IApplicationThread
( 位于当前应用进程内) 的Binder
接口去同步Activity
的状态从而完成生命周期方法的调用。调用
ApplicationThread.scheduleLaunchActivity
通过handle.sendMessage
通知ActivityThread
完成新Activity 的onCreate,onStart,onResume
的调用过程。
ActivtyStack.resumeTopActivityInnerLocked();//
---TaskDisplayArea.pauseBackStacks();//暂停所有堆栈或仅后堆栈中的所有活动
----ActivtyStack.startPausingLocked();//开始暂停当前处于 resumed 的活动
------ActivityStackSupervisor.realStartActivityLocked();
.... app 进程
ActivtyThread.handleLaunchActivity(){
----ActivtyThread.performLaunchActivity();
----1.ContextImpl appContext = createBaseContextForActivity(r);//创建该 context实现类
----2.mInstrumentation.newActivity();//通过appContext类加载器反射创建Activity
----3.LoadedApk.makeApplication();//创建application
----4.appContext与activity相关联
----5.执行activity.attach()方法。
----6.mInstrumentation.callActivityOnCreate();//执行oncreate方法
2.异常情况下的生命周期:
1.系统配置发生改变导致Activity被杀死并重新创建
异常终止:点击了返回键 ,锁屏 ,点击home键 ,有其他APP进入前台(比如接听电话) ,启动了新的Activity ,屏幕方向发生旋转 ,APP被杀死。
总之:系统只在 Acitivity 被异常终止的时候才会触发onSaveInstanceState
和onRestoreInstanceState
。
当系统配置变更,Activity被销毁重建后的生命周期流程:
可以在
onSaveInstanceState
的Bundle
中保存数据,在onRestoreInstanceState
中恢复数据。系统自动为我们做了一定的恢复工作。当 Activity 重建时,系统会为我们保存当前 Activity 的视图结构,并在 Activity 重启后恢复数据。
View视图数据恢复:
每个
View
都有onSaveInstanceState
和onRestoreInstanceState
方法。
Activity
被恶意终止时,Activity 会调用onSaveInstanceState
去保存数据,然后Activity
会委托Window
去保存数据,接着Window
再委托它上面的顶级容器去保存数据,通常顶级容器是一个ViewGroup
,一般来说很可能是DecorVew
。最后底层容器再去一一通知它的子View来保存数据,即调用ViewGroup
的dispatchSaveInstanceState()
最终调用具体的View 的onSaveInstanceState
去保存数据。注意:View的数据恢复必须要在当前 xml 中为其设置 ID,否则是无法恢复的。具体保存的数据可以看View子类的具体实现。
2.资源内存不足导致Activty被杀死
Acitivty的优先级:
1.前台 Activity,正在与用户交互,优先级最高。
2.可见但非前台,比如弹窗,透明Activity启动。
3.后台Activity,已经被暂停的 Activity,优先级最低。
后台工作不适合脱离四大组件而独立运行,这样的进程很容易被杀死,可以提供进程的优先级来降低被杀死的风险。
3.如何避免 Activity 重建
在
AndroidManifest
中为 Activity 配置configChanges
配置来避免因配置变更导致 Acitivty 被销毁重建。这样只会走 Activity 的
onConfigurationChanged
方法。android:configChanges="screenSize|orientation|keyboardHidden|screenLayout"
2.Activity的启动模式
1.LaunchMode
standard:标准模式
系统默认的模式,每次启动一个
Activity
都会重新创建一个新的实例,不管这个实例是否存在。一个任务栈可以有多个该Activity
的实例,每个实例也可以在不同的任务栈。在该模式下,谁启动了该
Acitivty
,当前Activity
就运行在启动它的那个Activity
的任务栈中。
注意:在
standard
模式下,如果通过ApplicationContext
去启动Activity
,会报异常,因为ApplicationContext
启动的Activity
没有所谓的任务栈。解决方法是为待启动的Activity
指定FLAG_ACTIVITY_NEW_TASK
标记位,这样启动的时候就会为它创建一个新的任务栈,这个时候待启动的Activity
实际是以singleTask
模式启动的。
应用场景: 大多数 Activity 适用的场景。
singleTop:栈顶复用模式
如果要新创建的
Activity
已经位于任务栈的栈顶,那么次Activity
不会被重新创建,同时它的onNewIntent
方法会被调用,对应的onCreate
和onStart
方法不会被调用。如果新创建的
Activity
实例已存在,但不位于栈顶,那么新Activity
仍然会被重新创建。
应用场景:通知栏点击收到的通知,需要启动一个Activity,就可以用
singleTop
,否则每次点击都会新建一个Activity
singleTask:栈内复用模式
一种单例模式,只要 Activity 在一个栈中存在,那么多次启动此 Activity 都不会重新创建实例,系统会调用其
onNewIntent
方法。当启动一个模式为singleTask
的Acitivity
A 的时候,系统首先寻找是否有 A 想要的任务栈,如果不存在,则重启创建一个任务栈,将 A 放进去。如果存在该任务栈,检查该任务栈中是否有 A 的实例,如果没有,创建放入。如果存在A的实例,则系统会把 A 上方的其他Activity
移除,让 A 位于栈顶,并调用onNewIntent
方法。
应用场景:大多数App的主页。对于大部分应用,当我们在主界面点击回退按钮的时候都是退出应用那么当我们第一次进入主界面之后,主界面位于栈底,以后不管我们打开了多少个Activity,只要我们再次回到主界面,都应该使用将主界面Activity上所有的Activity移除的方式来让主界面Activity处于栈顶,而不是往栈顶新加一个主界面Activity的实例,通过这种方式能够保证退出应用时所有的Activity都能报销毁。
场景一: A->B->C,B为 singleTask 模式,当前任务栈如下:
在【A,B,C】 中启动 B,任务栈如下:
场景二: 当前任务栈为:A->B->C,接着以 singTask
+ taskAffinity
= “com.ubtech.taskS2” 启动 ActivityD
直接按Back键回退情况:
D -> C -> B -> A -> 桌面
查看手机的后台任务窗口如下:
此时如果打开任务栈S2,则回退栈变为: D -> 桌面
此时如果打开任务栈S1,则回退栈变为:C->B->A->桌面
singleInstance:单实例模式
加强的
singleTask
,具有此模式的Activity
只能单独的位于一个任务栈中,整个系统只有一个实例。应用场景:系统界面,如来电界面等。
TaskAffinity
该参数标识了一个 Activity 所需要的任务栈的名称,默认情况下,所有的 Activity 的任务栈的名字都为应用的包名。
任务栈分为前台任务栈,后台任务栈,可以将后台任务栈切换到前台。
2.Activity 中 Flags 含义
FLAG_ACTIVITY_NEW_TASK
- 这个标记位的作用就是为 Activity 指定 “singleTask” 模式,效果和在 xml 中指定该模式相同
FLAT_ACTIVITY_SINGLE_TOP
- 这个标记位的作用就是为 Activity 指定 “singleTop” 模式,效果和在 xml 中指定该模式相同
FLAG_ACTIVITY_CLEAR_TOP
- 具有此标记位的 Activity,当它启动时,在同一个任务栈中所有位于它上面的 Activity 都要出栈,需要和
FLAG_ACTIVITY_NEW_TASK
配合使用,如果实例存在,会调用onNewIntent
方法。FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
- 具有此标记位的 Activity 不会出现在历史 Acitivity 的列表中,如果不希望用户通过历史列表回到该 Activity的时候使用该标记位置,等同于在 xml 中指定 android:excludeFromRecents=“true”。不等同于回退栈中没有它。
3. IntentFilter匹配规则
1.显示/隐式调用
1.显示调用
//1.显示调用方式一
Intent intent1 = new Intent(this,ActivityB.class);
startActivity(intent1);
//2.显示调用方式二
ComponentName component = new ComponentName(this,ActivityB.class);
Intent intent2 = new Intent();
intent2.setComponent(component);
startActivity(intent2);
//3.显示调用方式三
Intent intent3 = new Intent();
intent3.setClass(this,ActivityB.class);
startActivity(intent3);
2.隐式调用
注意:必须在添加默认的
2.匹配规则
- IntentFilter 中的过滤信息有
action
,category
,data
。- 一个 IntentFilter 可以有多个 action,category,data。同一类别的信息共同约束当前类别的匹配过程。只有一个 Intent 同时匹配 action类别,category类别,data类别才算完全匹配。
- 一个 Activity 可以有多个 IntentFilter,一个 Intent 只要能匹配任何一组 intent-filter 即可。
1. action匹配规则
Intent 中的 action 必须能后和 intent-filter 中的 action 匹配,一个 intent-filter 中有多个 action,Intent 中的 action 只要和其中的一个匹配就可。如果 Intent 中没有指定 action,则匹配失败。action 区分大小写。
2.category匹配规则
Intent
中如果有category
,所有的category
都必须和intent-filter
中的其中一个匹配,如果Intent
中 出现的category
必须是intent-filter
中已经定义的。Intent
中可以没有 category,仍可以匹配成功。系统在调用startActivity
时候默认会为Intent
添加"android.intent.category.DEFAULT"
这个category
。- 为了
activity
能够接受隐式调用,必须在intent-filter
中指定"android.intent.category.DEFAULT"
这个category
。
3. data匹配规则
- 如果 intent-filter 中定义了 data,则 Intent中必须也要定义可匹配的 data。
- data 分为两部分组成: mimeType 和 URI
- mimeType 指媒体类型,比如 imgage/jpeg,audo/* 和 video/* 等,可以表示图片,文本,视频等不同的媒体格式。
- URI 的每个部分都是一个单独的属性:
scheme
、host
、port
和path
- 😕/:/
,eg:content://com.example.project:200/folder/subfolder/etc - Scheme: URI 模式,比如 http,file,content 等,如果 URI 未指定,则无效
- Host: URI的主机名,未指定则整个 URI 则无效。
- Port:URI中的端口号,仅当 URI 中指定 scheme 和 host参数时候该 port 才有效。
- path:路径信息
3.隐式Activity是否存在判断
- 通过 PackageManager.resolveActivity 判断
- 通过 PackageManager.queryIntentActivities() 判断,该方法返回一个集合列表。
4.拓展问题
1.Activity和Fragment的区别
Activity是系统的四大组件之一,由
ActivityManager
管理,生命周期由系统控制。
Fragment
是在3.0后引入的组件,由FragmentManager
管理,可以由Activity
自由控制,引入或者删除,更方便。由于
Fragment
是Activity管理,所以在使用的时候,要格外注意,创建之前需要getFragmentByTag
或者ById
查看一下是否已经有存在的,FragmentManager
也跟ActivityManager
一样有一个缓存机制。同一个TAG的fragment
如果多次被添加到activity中,那么通过getFragmentBytag
获取Fragment的时候返回的是最后一次被添加的Fragment
fragment
在显示到销毁的过程中会执行自己的生命周期。onAttach
->onCreate
->onCreateView
->onActivityCreate
->onStart
->onResume
->onPause
->onStop
->onDestroyView
->onDestroy
->onDetach
同时也受到activity生命周期的影响,activity onPause Fragment也会执行相应的onPause
2.为什么用setArgument传参,而不是使用带有参数的构造器?
- 可以通过构造函数和
setArgument
方式传递参数,一但写有参构造函数,就必要构建无参构造函数- 通过构造函数传递参数隐患:当
Fragment
被重新创建,系统会调用Fragment
中的默认构造函数,从而导致之前传递的参数不见了,官方推荐setArgument
,fragment
被销毁重建后,最终会通过反射无参构造实例化一个新的Fragment
,并且给mArgments
初始化为原先的值,而原来的Fragment
实例的数据都丢失了,并重新进行了初始化- Activity重新创建时,会重新构建它所管理的Fragment,原先的Fragment的字段值将会全部丢失,但是通过Fragment.setArguments(Bundle bundle)方法设置的bundle会保留下来,并在重建时恢复。
3.window、view 和 Activity 的关系?
4.finish 方法后如何走到 onDestroy 的?
在Activity中调用finish()方法,会走哪些生命周期方法?
如果
FirstActivt
中启动SecondActivity
,则会先走FisrtActivity
的onPause
方法,然后走SecondActivity
的onCreate
->onStart
->onResume
->再到FirstActivity
的onStop
->onDestory
面试官:为什么 Activity.finish() 之后 10s 才 onDestroy ?
Activity 的
onStop/onDestroy
是依赖IdleHandler
来回调的。正常情况下当主线程空闲时会调用。但是由于某些特殊场景下的问题,导致主线程迟迟无法空闲,onStop/onDestroy 也会迟迟得不到调用。但这并不意味着 Activity 永远得不到回收,系统提供了一个兜底机制,当 onResume 回调 10s 之后,如果仍然没有得到调用,会主动触发。
5.dialog会不会影响Activity生命周期,为什么?
不会影响,Activity 生命周期回调都是
AMS
通过Binder
通知应用进程调用的;而弹出Dialog
、Toast
、PopupWindow
本质上都直接是通过WindowManager.addView
显示的(没有经过AMS
),所以不会对生命周期有任何影响。除非启动的是Theme为Dialog的Activity