Android四大组件之——Activity
Activity概述
移动应用体验与桌面体验的不同之处在于,用户与应用的互动并不总是在同一位置开始,而是经常以不确定的方式开始。例如,如果您从主屏幕打开电子邮件应用,可能会看到电子邮件列表,如果您通过社交媒体应用启动电子邮件应用,则可能会直接进入电子邮件应用的邮件撰写界面。
Activity 类的目的就是促进这种范式的实现。当一个应用调用另一个应用时,调用方应用会调用另一个应用中的 Activity,而不是整个应用。通过这种方式,Activity 充当了应用与用户互动的入口点。
Activity 提供窗口供应用在其中绘制界面。此窗口通常会填满屏幕,但也可能比屏幕小,并浮动在其他窗口上面。通常,一个 Activity 实现应用中的一个屏幕。
Activity的生命周期和状态变更
当用户浏览、退出和返回应用时,应用中的 Activity 实例会在其生命周期的不同状态间转换。Activity 类会提供许多回调,这些回调会让 Activity 知晓某个状态已经更改:系统正在创建、停止或恢复某个 Activity,或者正在销毁该 Activity 所在的进程。
生命周期的典型变更途径
附上官方的Activity生命周期图示。
需要注意一个点,是前一个Activity的onPause执行完,才会执行下一个Activity的创建,所以在onPause中不能执行较重操作。
由于关于生命周期是一个老生常谈的问题了,其他生命周期在此就不再做拆分解释了。
保存和恢复界面状态
当发生配置变更(屏幕旋转或切换到多窗口)和由于内存不足系统对后台进程进行回收时,会导致Activity被销毁。此种情况发生时,系统会回调被销毁Activity的onSaveInstanceState(),通过Bunlde存储状态信息,以保存销毁瞬间当前Activity的状态。
当这些被销毁的Activity得到重建时,系统会将之前保存着状态信息的Bundle,通过方法onCreate或则onRestoreInstanceState传递给重建的Activity,以使其能够完成状态的恢复。
在使用onCreate方法获取Bundle数据时需要进行null判断,而onRestoreInstanceState则不需要,且后者调用时机在onStart生命周期之后。
启动模式
任务和回退栈
任务是用户在执行某项工作时与之互动的一系列 Activity 的集合。这些 Activity 按照每个 Activity 打开的顺序排列在一个返回堆栈中。返回堆栈按照“后进先出”的对象结构运作。
也就是说,任务是整体的概念——“外”,回退栈指代内部Activity的堆叠——“内”。
关于任务和回退栈,Activity可以这样要求
- 进入任务前 在清单文件的activity标签中,可以通过taskAffinity来标识自己要进入的任务是哪个(亲和性)。
- 进入任务中 当Activity被开启,然后在存放于回退栈的过程中,Activity可以通过清单文件中设置的启动模式launchMode,来设置回退栈对它的入栈安排。是直接进入(standard)、栈顶复用(singleTop)、栈内单例(singleTask),还是独享一个任务(singleInstance)。
除了清单文件,当然还可以通过Intent设置参数,来标识Activity与任务的关联方式:
FLAG_ACTIVITY_NEW_TASK == singleTask
FLAG_ACTIVITY_SINGLE_TOP == singleTop
FLAG_ACTIVITY_CLEAR_TOP,如果要启动的 Activity 已经在当前任务中运行,则不会启动该 Activity 的新实例,而是会销毁位于它之上的所有其他 Activity,并通过 onNewIntent() 将此 intent 传送给它的已恢复实例(现在位于堆栈顶部)。FLAG_ACTIVITY_CLEAR_TOP 最常与 FLAG_ACTIVITY_NEW_TASK 结合使用。将这两个标记结合使用,可以查找其他任务中的现有 Activity,并将其置于能够响应 intent 的位置。
关于Activity的亲和性
我们知道,通过在清单文件中配置Activity的taskAffinity,可以修改其亲和性(Activity 倾向于属于哪个任务)。
taskAffinity 属性采用字符串值,该值必须不同于 <manifest> 元素中声明的默认软件包名称,因为系统使用该名称来标识应用的默认任务亲和性。
亲和性可在两种情况下发挥作用:
- 当启动 Activity 的 intent 包含 FLAG_ACTIVITY_NEW_TASK 标记时: 默认情况下,新 Activity 会启动到调用 startActivity() 的 Activity 的任务中。它会被推送到调用方 Activity 所在的返回堆栈中。但是,如果传递给 startActivity() 的 intent 包含 FLAG_ACTIVITY_NEW_TASK 标记,则系统会寻找其他任务来容纳新 Activity。通常会是一个新任务,但也可能不是。如果已存在与新 Activity 具有相同亲和性的现有任务,则会将 Activity 启动到该任务中。如果不存在,则会启动一个新任务。
- 当 Activity 的 allowTaskReparenting 属性设为 "true" 时: 在这种情况下,一旦和 Activity 有亲和性的任务进入前台运行,Activity 就可从其启动的任务转移到该任务。
清除返回堆栈
默认情况下,如果用户离开任务较长时间,系统会清除任务中除根 Activity 以外的所有 Activity。当用户再次返回到该任务时,只有根 Activity 会恢复。 但是,可通过以下属性修改这一默认行为:
- alwaysRetainTaskState 如果在任务的根 Activity 中将该属性设为 "true",则不会发生上述默认行为。即使经过很长一段时间后,任务仍会在其堆栈中保留所有 Activity。
- clearTaskOnLaunch 如果在任务的根 Activity 中将该属性设为 "true",那么只要用户离开任务再返回,堆栈就会被清除到只剩根 Activity。也就是说,它与 alwaysRetainTaskState 正好相反。用户始终会返回到任务的初始状态,即便只是短暂离开任务也是如此。
- finishOnTaskLaunch 该属性与 clearTaskOnLaunch 类似,但它只会作用于单个 Activity 而非整个任务。它还可导致任何 Activity 消失,包括根 Activity。如果将该属性设为 "true",则 Activity 仅在当前会话中归属于任务。如果用户离开任务再返回,则该任务将不再存在。对于那些您不希望用户能够返回到 Activity 的情况,可以使用此属性进行设置。
Activity的启动
显示启动
val intent = Intent(this, SignInActivity::class.java)
startActivity(intent)
or
startActivityForResult(intent,requestCode)
如果是startActivityForResult开启:
- onActivityResult接收结果
- 被开启Activity关闭时在必要时候通过setResult(resultCode,intentData)传回结果
隐式启动
通过在<activity>标签下,配置<intent-filter>过滤器,来标识此Activity可以支持哪些意图。
<activity android:name="ShareActivity">
<intent-filter>
<action android:name="android.intent.action.SEND"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="text/plain"/>
<data android:mimeType="image/*"/>
</intent-filter>
</activity>
针对从后台启动Activity的限制
Android 10 (API 级别 29) 及更高版本对后台应用可启动 Activity 的时间施加限制。这些限制有助于最大限度地减少对用户造成的中断,并且可以让用户更好地控制其屏幕上显示的内容。
几乎在所有情况下,后台应用都应显示有时效性的通知,以便向用户提供紧急信息,而非直接启动 Activity。
限制的例外情况
在 Android 10 或更高版本上运行的应用只有在满足以下一项或多项条件时,才能启动 Activity:
- 应用具有可见窗口,例如前台 Activity。
- 应用在前台任务的返回栈中拥有 Activity。
- 应用在 Recents 屏幕上现有任务的返回栈中拥有 Activity。
- 应用的某个 Activity 刚在不久前启动。
- 应用最近为某个 Activity 调用了 finish()。这仅适用于在调用 finish() 时,应用在前台或前台任务的返回栈中拥有 Activity 的情况。
- 应用具有受系统约束的服务。此情况仅适用于以下服务,这些服务可能需要启动界面:AccessibilityService、AutofillService、CallRedirectionService、HostApduService、InCallService、TileService、VoiceInteractionService 和 VrListenerService。
- 应用中的某个服务受另一个可见应用约束。请注意,绑定到服务的应用必须保持可见,以便后台应用成功启动 Activity。
- 应用收到系统的 PendingIntent 通知。对于服务和广播接收器的挂起 Intent,应用可在该挂起 Intent 发送几秒钟后启动 Activity。
- 应用收到另一个可见应用发送的 PendingIntent。
- 应用收到它应该在其中启动界面的系统广播。
- 应用通过 CompanionDeviceManager API 与配套硬件设备相关联。此 API 支持应用启动 API,以响应用户在配对设备上执行的操作。
- 应用是在设备所有者模式下运行的设备政策控制器。示例用例包括完全托管的企业设备,以及数字标识牌和自助服务终端等专用设备。
- 用户已向应用授予 SYSTEM_ALERT_WINDOW 权限(在 Android 10设备上运行的应用无法获得 SYSTEM_ALERT_WINDOW 权限。这是因为绘制叠加层窗口会使用过多的内存,这对低内存 Android 设备的性能十分有害。如果在搭载 Android 9 或更低版本的设备上运行的应用获得了 SYSTEM_ALERT_WINDOW 权限,则即使设备升级到 Android 10,也会保留此权限。不过,尚不具有此权限的应用在设备升级后便无法获得此权限了)。