Activity生命周期及启动模式详解

1.Activity生命周期

1.正常情况:

(1) onCreate:

表示 Activty 正在被创建,这是 Activity 生命周期的第一个方法,可以做一些初始化的工作,比如:加载布局,绑定控件,初始化数据等等。

(2) onRestart:

表示 Activity 正在重新启动,通常由 Activty 由不可见变为可见时被调用。

触发场景:按 Home 键进入桌面,或者启动一个新的 Activity,这时当前 Activity 被暂停,然后又回到了该 Activity ,此时会触发 onRestart

(3) onStart:

表示当前 Activity 正在被启动,此时 Activity 已经可见了,但是还未出现在前台,无法与用户交互,用户是看不到的。

(4) onResume:

当前 Activity 已经可见,出现在前台并且开始活动,onStartonResume 都表示 Activity 可见,onStart 时候 Activity 还在后台,onResume 的时候 Activity 才显示到前台。

(5) onPause:

表示当前 Activity 正在停止,此时还是可见,正常情况下,接着就会调用 onStop 方法。可以做一些数据存储,动画停止等轻量级回收工作,不能太耗时。因为这会影响到新 Activity 的显示,只有 onPause 执行完,新 Activity 的 onResume 方法才会执行

注意:例如弹出一个非全屏 Dialog 或者启动一个透明的 Activty,那么当前 onStop 方法不会调用。

(6) onStop:

表示 Activity 即将停止,不可见,可以做一些稍微重量级的会后工作,同样不能太耗时。

(7) onDeatroy:

表示 Activity 即将被销毁,这是 Activity 生命周期的最后一个回调,可以做一些回收工作和最终的资源释放。

生命周期流程图

在这里插入图片描述

拓展问题:
1.onStartonResumeonPauseonStop有什么实质上的不同?

onStartonStop 是从 Activity 是否可见角度来回调的,而 onResumeonPause 是从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 被异常终止的时候才会触发 onSaveInstanceStateonRestoreInstanceState
当系统配置变更,Activity被销毁重建后的生命周期流程:

在这里插入图片描述

可以在 onSaveInstanceStateBundle 中保存数据,在 onRestoreInstanceState 中恢复数据。系统自动为我们做了一定的恢复工作。当 Activity 重建时,系统会为我们保存当前 Activity 的视图结构,并在 Activity 重启后恢复数据。

View视图数据恢复:

每个 View 都有 onSaveInstanceStateonRestoreInstanceState 方法。

Activity 被恶意终止时,Activity 会调用 onSaveInstanceState 去保存数据,然后 Activity 会委托 Window 去保存数据,接着 Window 再委托它上面的顶级容器去保存数据,通常顶级容器是一个 ViewGroup,一般来说很可能是 DecorVew。最后底层容器再去一一通知它的子View来保存数据,即调用 ViewGroupdispatchSaveInstanceState() 最终调用具体的View 的 onSaveInstanceState 去保存数据。

注意:View的数据恢复必须要在当前 xml 中为其设置 ID,否则是无法恢复的。具体保存的数据可以看View子类的具体实现。

Activity中的状态保存与恢复,这些是你应该知道的

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 方法会被调用,对应的 onCreateonStart 方法不会被调用。

如果新创建的 Activity 实例已存在,但不位于栈顶,那么新 Activity 仍然会被重新创建。


应用场景:通知栏点击收到的通知,需要启动一个Activity,就可以用 singleTop,否则每次点击都会新建一个Activity

在这里插入图片描述
在这里插入图片描述

singleTask:栈内复用模式

一种单例模式,只要 Activity 在一个栈中存在,那么多次启动此 Activity 都不会重新创建实例,系统会调用其 onNewIntent 方法。当启动一个模式为 singleTaskAcitivity 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 中的过滤信息有 actioncategorydata
  • 一个 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匹配规则

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bmaLMAc9-1649915991862)(../../pic/image-20220414113324639.png)]

  • 如果 intent-filter 中定义了 data,则 Intent中必须也要定义可匹配的 data。
  • data 分为两部分组成: mimeType 和 URI
  • mimeType 指媒体类型,比如 imgage/jpeg,audo/* 和 video/* 等,可以表示图片,文本,视频等不同的媒体格式。
  • URI 的每个部分都是一个单独的属性:schemehostportpath
  • 😕/:/ ,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中的默认构造函数,从而导致之前传递的参数不见了,官方推荐setArgumentfragment被销毁重建后,最终会通过反射无参构造实例化一个新的Fragment,并且给mArgments初始化为原先的值,而原来的Fragment实例的数据都丢失了,并重新进行了初始化
  • Activity重新创建时,会重新构建它所管理的Fragment,原先的Fragment的字段值将会全部丢失,但是通过Fragment.setArguments(Bundle bundle)方法设置的bundle会保留下来,并在重建时恢复。

3.window、view 和 Activity 的关系?

4.finish 方法后如何走到 onDestroy 的?

在Activity中调用finish()方法,会走哪些生命周期方法?

如果FirstActivt中启动SecondActivity,则会先走FisrtActivityonPause方法,然后走SecondActivityonCreate->onStart->onResume->再到FirstActivityonStop->onDestory

面试官:为什么 Activity.finish() 之后 10s 才 onDestroy ?

Activity 的 onStop/onDestroy 是依赖 IdleHandler 来回调的。正常情况下当主线程空闲时会调用。但是由于某些特殊场景下的问题,导致主线程迟迟无法空闲,onStop/onDestroy 也会迟迟得不到调用。但这并不意味着 Activity 永远得不到回收,系统提供了一个兜底机制,当 onResume 回调 10s 之后,如果仍然没有得到调用,会主动触发。

5.dialog会不会影响Activity生命周期,为什么?

不会影响,Activity 生命周期回调都是 AMS 通过 Binder 通知应用进程调用的;而弹出 DialogToastPopupWindow 本质上都直接是通过 WindowManager.addView 显示的(没有经过 AMS),所以不会对生命周期有任何影响。

除非启动的是Theme为Dialog的Activity

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值