一. activity生命周期
按照惯例,先上张 Activity 生命周期图
回答一个问题: Activity是什么?
Activity表示为具有用户界面的单一屏幕,可理解为"界面"。正常情况下,除了Window、Dialog和Toast,我们能见到的界面的确只有Activity。
1.1 activity生命周期分析
1.1.1 生命周期概述
-
onCreate ()
必须实现此回调,该回调在系统创建Activity时被触发。在这个方法中,应该初始化一些基本组件,例如创建视图并进行数据绑定等。最重要的是,调用 setContentView() 去加载界面布局资源。
另外,当 onCreate () 完成后,接下来的回调永远是 onStart ()。 -
onStart ()
Activity 进入 “已启动” 状态,并且对用户可见。但是还没有出现在前台,也不能用户进行交互,这个回调做了最后的一些准备工作。 -
onResume ()
可见、出现在前台、可交互。 系统在Activity开始与用户进行交互之前调用此回调。此时,Activity位于activity堆栈的顶部,并捕获用户的所有输入。
onPause () 回调总是跟在 onResume ()之后。 -
onPause ()
当 activity 失去焦点并进入暂停状态时,系统会调用。 例如,当用户点击 “后退” 或 “最近” 按钮时,会出现此状态。
当系统调用 onPause () 时,从技术上讲,Activity仍然是部分可见的,但通常表示用户正在离开Activity,Activity 将很快进入 Stopped 或者 Resumed 状态。
如果用户期望 UI 更新,则处于 Paused 状态的 Activity 可以继续更新 UI。 例如 导航地图屏幕或媒体播放器播放,即使它们失去了焦点,用户也希望他们的 UI 继续更新。
你不应该在 onPause 里去保存 应用程序或用户数据,进行网络调用,或执行数据库事务等耗重量级操作。
一旦 onPause() 执行完毕,接下来的回调将 onStop() 或 onResume(),根据活动进入暂停状态以后会发生什么决定。 -
onStop ()
当 activity 不再对用户可见时,系统会调用。 这可能是因为 activity 被破坏,或者新 activity 正在开始,又或者 一个存在的activity正在进入 Resumed 状态并且正在覆盖已停止的 activity。
如果这个 activity 准备返回和用户进行交互,则会回调 onRestart()。
如果这个 activity 完全终止,则会回调 onDestroy()
-
onRestart ()
当处于 “Stopped” 状态的 activity 即将重新启动时,系统会调用此方法。 onRestart()会还原 activity 在 stopped时的状态数据。 -
onDestroy ()
系统在activity 销毁之前调用此回调。
此回调是activity 收到的最后一个回调。在这里,可以做一些回收工作和最终的资源释放。
1.1.2 生命周期具体说明
-
针对一个Activity,第一次启动,回调如下:onCreate -> onStart -> onResume。
-
当用户打开新的 activity 或者切换到桌面 或者按最近按钮时,回调如下:onPause -> onStop。
特殊情况, 如果新Activity采用透明主题时,回调如下:onPause。 -
当用户再次回到原 activity 时,回调如下:onRestart -> onStart -> onResume。
-
当用户弹出一个对话框( AlertDialog )时,activity 生命周期不会发生变化。
-
当用户按back回退时,回调如下:onPause -> onStop ->onDestroy。
问题:当前Activity 为A, 此时用户打开一个新的Activity B,那么 B 的 onResume 和 A 的 onPause 哪个先执行?
- 可自己动手试一试,答案是 旧 Activity A 先 onPause,然后 新 Activity B 再启动。其实很好理解,onResume代表着可见、可交互,如果旧的 Activity 不先 onPause,那岂不是会出现两个可见、可交互的界面,不就乱套了。
- 看源码更能加深理解。
1.2 activity状态
只有三个状态是静态的,可以存在较长时间保持状态不变。(其他状态只是过渡状态,系统快速切换并切换到下一状态)
-
运行(Resumed)
- 当前 activity 处于栈顶,用户可以与它进行交互。(通常也被理解为 “running” 状态)
- 此状态由 onResume() 进入,onPause() 退出
-
暂停(Paused)
- 当前 activity 仍然是可见的,但被另一个 activity 处在最上方,最上方的 activity 是半透明的,或者是部分覆盖整个屏幕。被暂停的 activity 不会再接收用户的输入。
- 处于活着的状态 (Activity 对象存留在内存,保持着所有的 状态和成员信息,仍然吸附在 window manager)。
- 当资源内存极度不足时,系统会杀掉该 activity 释放相应资源。
- 此状态由 onPaues() 进入,退出可能是从 onResume() 重新唤醒软件,或者被 onStop() 杀掉。
-
停止(Stopped)
- 当前 activity 完全被隐藏,不被用户可见,可以认为是处于后台。
- 处于活着的状态 (Activity 对象存留在内存,保持着所有的 状态和成员信息,不再吸附在 window manager)。
- 由于对用户不再可见,只要有内存的需要,系统会杀掉该 activity 来释放相应资源。
- 此状态由 onStop() 进入,退出是从 onRestart() 重新唤醒软件,或者被 onDestroy() 彻底死亡。其他状态(Created与 Started )都是短暂的,系统快速执行那些回调函数并通过。
1.3 android进程优先级
具体可参考官方文档 Processes and Application Lifecycle
-
前台进程
一般情况是,在前台与用户进行交互的 activity,或与前台进程 绑定的 service。 -
可见进程
处于 paused 状态,用户可见,但是不能进行交互。 -
服务进程
如果一个进程中运行着 service,这个service 是通过 startService() 开启的,并且不属于上面两种高优先级的情况,那它就是一个服务进程。 -
后台进程
处于 stopped 状态。 -
空进程
如果一个进程不包含任何活跃的应用组件,则认为是空进程。
二. android任务栈
任务栈是一种“后进先出”的栈结构。
任务栈分为 前台任务栈 和 后台任务栈 ,后台任务栈中的 activity 位于暂停状态。
- TaskAffinity 任务相关性, 这个参数标识了一个 Activity 所需要的任务栈的名字。
- 默认情况下,所有 Activity 所需的任务栈的名字为包名。
- 通过指定 TaskAffinity 可以为 Activity 指定新的任务栈的名字,当然必须不能和包名相同。
- TaskAfiinity 主要是和 singleTask 启动模式或者 allowTaskReparenting 属性配对使用。
- 在 AndroidManifest 文件中指定。
- 命令** adb shell dumpsys activity** 可导出 Activity 信息。
三. activity启动模式
-
standard(标准模式)
系统默认模式,每启动一个 Activity 就会重新创建一个新的实例。
这种模式下, Activity A 启动了 Activity B,那么 B 就会进入到 A 所在的任务栈中。 -
singleTop(栈顶复用模式)
如果新 Activity 已经位于栈顶,那么此 Activity 不会再重新创建,同时它的 onNewIntent 方法会被回调。 -
singleTask(栈内复用模式)
只要 Activity 在一个栈中存在,那么多次启动此 Activity 都不会重新创建实例。 -
singleInstance(单实例模式, 加强的 singleTask 模式)
除了具有 singleTask 模式的所有特性外,还加强了一点,那就是具有此种模式的 Activity 只能单独的位于一个任务栈中。
例1
任务栈(com.yjnull.slowdev4android)有个 ExampleActivity ,启动模式为standard。
任务栈(com.yy.task1)有个 ThreeActivity,启动模式为 singleInstance。
启动ExampleActivity,然后启动ThreeActivity,然后两个互相启动,任务栈如下截图。
例2
四. IntentFilter 的匹配规则
隐式启动 Activity 需要 Intent 能够匹配目标组件的 IntentFilter中所设置的过滤信息,如果不匹配则无法启动目标组件。
IntentFilter 的过滤信息有 action、category、data
-
action:
1.action是一个字符串 区分大小写
2.当过滤规则中有 action 时,那么只要 Intent 中的 action 能够和过滤规则中的任何一个相同即可匹配成功。需注意:如果Intent 没有指定 action,将匹配失败。
3.也就是说,当过滤规则有 action 时,Intent 中必须存在 action。 -
category:
1.category 和 action 不同,它不强制要求 Intent 中必须含有 category。
2.如果 Intent 中没有 category,那么可以匹配成功。
3.如果 Intent 中有 category,那么不管有几个 category,都必须和过滤规则中的 category 相同才能匹配成功。
4.为什么不设置 category 也可以匹配成功,因为 startActivity 时会默认为 Intent 加上 “android.intent.category.DEFAULT” 这个 category。
5.所以为了 activity 能够接收隐式调用,必须在intent-filter 中指定 “android.intent.category.DEFAULT” 这个 category。 -
data
1.data 由两部分组成:mimeType 和 URI。
2.mimeType 指媒体类型:如 image/jpeg、video/*等。
3.URI 结构:
<schema>://<host>:<port>/[<path>|<pathPrefit>|<pathPattern>]
4.如果没有指定 URI ,则 URI 的默认值为 content 或 file
5.例如指定 mimeType 为 image/png,未指定 URI 。 则如下代码可匹配过滤规则
intent.setDataAndType(Uri.parse("file://abc"), "image/png");
或者
intent.setDataAndType(Uri.parse("content://abc"), "image/png");
-
总结
1.隐式启动 Activity 时,IntentFilter 一定要指定 “android.intent.category.DEFAULT” 这个 category。
2.action、category、data ,如果匹配了 action,那么其余两个也得匹配成功才能找到 Activity。
3.如果只匹配 data,那么 action 不指定也可以运行成功,不会返回指定 Activity,而是返回ResolverActivity,让你选择默认程序运行。
参考
http://gityuan.com/
Android 开发艺术探索