目录
一、简介
二、启动方式
三、生命周期
四、临时数据保存
五、清单文件属性
六、启动模式
七、Activity启动流程源码解析
Activity是Android的四大组件之一,是和用户交互的重要桥梁。
有两种启动方式:显式启动和隐式启动
1、显示启动
使用场景:通常用于在同一应用下启动另一Activity,下面是几种常用的示例:
① public Intent setClass (Context packageContext, Class> cls)
Intent intent =new Intent(this,TwoActivity.class);
startActivity(intent);
② public Intent setClassName (String packageName, String className)
Intent intent =new Intent();
intent.setClassName("com.douli.study","com.douli.study.TwoActivity");
startActivity(intent);
③ public Intent setClassName (Context packageContext, String className)
Intent intent =new Intent();
intent.setClassName(this,"com.douli.study.TwoActivity");
startActivity(intent);
④ public Intent setComponent (ComponentName component)
Intent intent =new Intent();
ComponentName componentName =new ComponentName(this,TwoActivity.class);
intent.setComponent(componentName);
startActivity(intent);
2、隐式启动:
使用场景:通常用于启动别的应用中的Activity
隐式启动是通过IntentFilter来过滤启动条件,使用action、category、data来匹配目标组件。下面举例说明:
① action的匹配规则
action是一个字符串,系统预置了一些,我们也可以自己定义比如com.douli.study.action.one,要全局唯一。过滤器可以配置多个action,只要Intent的action与过滤器中的任何一个action匹配上,就算匹配成功。注意隐式启动至少要设置一个action,除LaucherActivity以外,其他activity都得设置一个DEFAULT的categroy。示例如下:
//清单文件
//代码
Intent intent = new Intent();
intent.setAction("com.douli.study.action.one");
startActivity(intent);
② category匹配规则
和action一样,categroy也是个字符串,系统预置了一些,我们也可以自己定义com.douli.study.category.one。可以配置单个或多个,Intent设置的categroy只要和过滤器中的一个匹配上就算成功,需要注意,如果Intent不设置categroy,系统也会默认添加一个category,以对应android.intent.category.DEFAULT。另外categroy要么不设置,要设置就得设置对才行。示例:
//清单文件
//java代码
Intent intent = new Intent();
intent.setAction("com.douli.study.action.one");
intent.addCategory("com.douli.study.category.two");
intent.addCategory("com.douli.study.category.one");
startActivity(intent);
③ data匹配规则
如果过滤器中声明了data,则java代码中必须配置data。data有mimetype和uri组成。
mimeType: 常见类型text/html、image/jpg,也可以自定义比如douli/aa
URI:包含scheme、host、port、path
scheme:协议,比如http
host:主机,比如com.douli.study
port:端口,比如8080
path:路径,比如 /etc
注意:Ⅰ、过滤器未指定任何URI或MIME时,Intent不设置URI和MIME才能匹配
Ⅱ、过滤器只指定URI或MIME时,Intent也要设置对应只设置URI或MIME
Ⅲ、过滤器同时指定Uri和MIME时,intent必须使用setDateAndType,因为单独使用setDate或setType会彼此覆盖掉。
示例:
//清单文件
//java代码
Intent intent = new Intent();
intent.setAction("com.douli.study.action.one");
intent.setDataAndType(Uri.parse("http://com.douli.study:8080"),"douli/aa");
startActivity(intent);
这里借用官网的一张图
生命周期说明:
方法 | 说明 |
onCreate | 首次创建Activity时调用,可以在这里做视图和数据的初始化工作。如果之前保存了Activity状态,该方法还会传递一个Bundel对象。 |
onRestart | Activity已停止,再次启动前调用。 |
onStart | 在Activity即将对用户可见之前调用 |
onResume | Activity对即将开始于用户交互之前调用,这时Activity处于栈顶,并具有用户输入焦点。 |
onPause | 当用户失去焦点时调用,这个方法通常用于确认未保存数据,停止动画等,不要在这里做耗时工作。 |
onStop | Activity对用户不可见时调用 |
onDestroy | Activity被销毁前调用,当调用finish(),或系统为节省内存而销毁Activity实例时,可能会调用,注意是可能。不保证一定会调用。 |
Activity异常情况下的生命周期说明:
1、当配置发生变更时,如果没有提前声明,那么Activity会被销毁并重建。比如屏幕的横竖屏切换。因为是意外销毁系统会主动通过onSaveInstanceState保存临时信息,"Activity临时信息保存相关"后面有详细介绍
如果不想Activity销毁重建,可以配置:android:configChanges="orientation|keyboardHidden|screenSize",之后旋转屏幕,Activity不会重新调用各个生命周期,只执行onConfigurationChanged回调。这里只配置了方向,键盘,屏幕尺寸,还有其他属性,下面列出:
值 | 说明 |
---|---|
“mcc ” |
IMSI 移动国家/地区代码 (MCC) 发生了变化 - 检测到了 SIM 并更新了 MCC。 |
“mnc ” |
IMSI 移动网络代码 (MNC) 发生了变化 - 检测到了 SIM 并更新了 MNC。 |
“locale ” |
语言区域发生了变化 — 用户为文本选择了新的显示语言。 |
“touchscreen ” |
触摸屏发生了变化。(这种情况通常永远不会发生。) |
“keyboard ” |
键盘类型发生了变化 — 例如,用户插入了一个外置键盘。 |
“keyboardHidden ” |
键盘无障碍功能发生了变化 — 例如,用户显示了硬件键盘。 |
“navigation ” |
导航类型(轨迹球/方向键)发生了变化。(这种情况通常永远不会发生。) |
“screenLayout ” |
屏幕布局发生了变化 — 这可能是由激活了其他显示方式所致。 |
“fontScale ” |
字体缩放系数发生了变化 — 用户选择了新的全局字号。 |
“uiMode ” |
用户界面模式发生了变化 — 这可能是因用户将设备放入桌面/车载基座或夜间模式发生变化所致。 请参阅 UiModeManager 。 此项为 API 级别 8 中新增配置。 |
“orientation ” |
屏幕方向发生了变化 — 用户旋转了设备。 注:如果您的应用面向 API 级别 13 或更高级别(按照 |
“screenSize ” |
当前可用屏幕尺寸发生了变化。它表示当前可用尺寸相对于当前纵横比的变化,因此会在用户在横向与纵向之间切换时发生变化。 不过,如果您的应用面向 API 级别 12 或更低级别,则 Activity 始终会自行处理此配置变更(即便是在 Android 3.2 或更高版本的设备上运行,此配置变更也不会重新启动 Activity)。 此项为 API 级别 13 中新增配置。 |
“smallestScreenSize ” |
物理屏幕尺寸发生了变化。它表示与方向无关的尺寸变化,因此只有在实际物理屏幕尺寸发生变化(如切换到外部显示器)时才会变化。 对此配置的变更对应于smallestWidth 配置的变化。 不过,如果您的应用面向 API 级别 12 或更低级别,则 Activity 始终会自行处理此配置变更(即便是在 Android 3.2 或更高版本的设备上运行,此配置变更也不会重新启动 Activity)。 此项为 API 级别 13 中新增配置。 |
“layoutDirection ” |
布局方向发生了变化。例如,从从左至右 (LTR) 更改为从右至左 (RTL)。 此项为 API 级别 17 中新增配置。 |
2、系统内存不足时,导致低优先级的Activity被杀死
Activity的优先级可以分为三类:
① 前台Activity,正在与用户交互,优先级最高
②可见但已失去焦点,比如弹出Dialog主题的Activity弹窗
③后台Activity,Activity已不可见运行与后台。
当系统内存不足时,会按照优先级销毁Activity,并通过onSaveInstanceState和onRestoreInstanceState保存恢复临时数据。
在上面已经介绍了Activity通过onSaveInstanceState和onRestoreInstanceState来保存恢复临时数据,这里有几点需要特别注意:
1、Activity的保存策略是,当有可能发生在用户不知情下销毁Activity时,系统才会调用onSaveInstanceState保存数据,注意是“用户不知情”,如果用户主动按返回键back,那么Activity销毁时,并不会调用onSaveInstanceState。
2、配置发生变更、内存不足、用户按下Home键,都有不知情销毁的风险,所以Activity要触发onSaveInstanceState
3、按下Home键,Activity进入后台,触发onSaveInstanceState,Activity此时有2个走向,一个是Activity立马又返回前台,onRestoreInstanceState就不会被调用,如果Activity久久没有返回,被系统回收了,那么下次重建时才会调用onRestoreInstanceState恢复数据。从这里可以看出onSaveInstanceState 和onRestoreInstanceState并不保证会成对出现。
4、onSaveInstanceState是用来保存简单数据的,比如输入框内容,ListView的位置等,如果是大量数据就要用数据库等其他方式了。另外onSaveInstanceState系统默认实现了View的数据恢复,如果我们想额外保存点其他的东西,重写改函数时要记得调用super.onSaveInstanceState()。
下面列出了清单文件中Activity标签下的所有属性:
. . .
下面介绍下其常用的属性:
configChanges,icon,label,launchMode,name,theme,screenOrientation,windowSoftInputMode。
configChanges :指明的属性发生变更,Activity不会重新生成实例,而是走onConfigChanged回调。常用的属性有 keyboardHidden orientation screenSize,前面已经介绍过了,这里不再叙述。
exported: Activity是否可以由外部调用,Intent-filter决定他的默认值,有过滤器默认值是ture,反之为false。
hardwareAccelerated:是否启动硬件加速,优点是动画、绘制速度加快,缺点是将占用较多内存。
icon: Activity图标,用的较少,一般是集成Application的,如果App有多个启动Activity可以设置。
label:Activity桌面显示名称,用法同icon
name:Activity的名称,可以是全类名,也可以是 .xx
screenOrientation:常用的属性有portrait landscape
theme: 设置主题,如果不设置则集成Application的主题,Application也不设置则使用系统主题
windowSoftInputMode:下面列出的是配置不同值时,键盘的行为:
值 | 说明 |
---|---|
"stateUnspecified " |
不指定软键盘的状态(隐藏还是可见)。 将由系统选择合适的状态,或依赖主题中的设置。 这是对软键盘行为的默认设置。 |
“stateUnchanged ” |
当 Activity 转至前台时保留软键盘最后所处的任何状态,无论是可见还是隐藏。 |
“stateHidden ” |
当用户选择 Activity 时 — 也就是说,当用户确实是向前导航到 Activity,而不是因离开另一 Activity 而返回时 — 隐藏软键盘。 |
“stateAlwaysHidden ” |
当 Activity 的主窗口有输入焦点时始终隐藏软键盘。 |
“stateVisible ” |
在正常的适宜情况下(当用户向前导航到 Activity 的主窗口时)显示软键盘。 |
“stateAlwaysVisible ” |
当用户选择 Activity 时 — 也就是说,当用户确实是向前导航到 Activity,而不是因离开另一 Activity 而返回时 — 显示软键盘。 |
“adjustUnspecified ” |
不指定 Activity 的主窗口是否调整尺寸以为软键盘腾出空间,或者窗口内容是否进行平移以在屏幕上显露当前焦点。 系统会根据窗口的内容是否存在任何可滚动其内容的布局视图来自动选择其中一种模式。 如果存在这样的视图,窗口将进行尺寸调整,前提是可通过滚动在较小区域内看到窗口的所有内容。 这是对主窗口行为的默认设置。 |
“adjustResize ” |
始终调整 Activity 主窗口的尺寸来为屏幕上的软键盘腾出空间。 |
“adjustPan ” |
不调整 Activity 主窗口的尺寸来为软键盘腾出空间, 而是自动平移窗口的内容,使当前焦点永远不被键盘遮盖,让用户始终都能看到其输入的内容。 这通常不如尺寸调正可取,因为用户可能需要关闭软键盘以到达被遮盖的窗口部分或与这些部分进行交互。 |
启动模式有4种,standard、singleTop、singleTask、singleInstance。
standard:默认模式,每次启动Activity都会生成新的实例放到任务栈。
singleTop:栈顶复用,如果要启动的Activiy在当前任务栈顶已经有了则复用,走onNewIntent回调。否则同standard.
singleTask:栈内复用,如果要启动的Activity在当前任务栈中已经有了实例,则会把当前Activity上所有的其他Activity清空,然后复用 ,走onNewIntent回调,否则同Standard。
singleInstance:会开启新的任务栈,栈中只有一个实例,就是当前Activity。
Intent的常用Flag参数:
FLAG_ACTIVITY_CLEAR_TOP:例如现在的栈情况为:A B C D 。D此时通过intent跳转到B,如果这个intent添加FLAG_ACTIVITY_CLEAR_TOP 标记,则栈情况变为:A B。如果没有添加这个标记,则栈情况将会变成:A B C D B。也就是说,如果添加了FLAG_ACTIVITY_CLEAR_TOP标记,并且目标Activity在栈中已经存在,则将会把位于该目标activity之上的activity从栈中弹出销毁。这跟上面把B的Launch mode设置成singleTask类似。
FLAG_ACTIVITY_NEW_TASK:例如现在栈1的情况是:A B C。C通过intent跳转到D,并且这个intent添加了FLAG_ACTIVITY_NEW_TASK 标记,如果D这个Activity在Manifest.xml中的声明中添加了Task affinity,并且和栈1的affinity不同,系统首先会查找有没有和D的Task affinity相同的task栈存在,如果有存在,将D压入那个栈,如果不存在则会新建一个D的affinity的栈将其压入。如果D的Task affinity默认没有设置,或者和栈1的affinity相同,则会把其压入栈1,变成:A B C D,这样就和不加FLAG_ACTIVITY_NEW_TASK 标记效果是一样的了。 注意如果试图从非activity的非正常途径启动一个activity,比如从一个service中启动一个activity,则intent就要添加FLAG_ACTIVITY_NEW_TASK 标记。
FLAG_ACTIVITY_NO_HISTORY:例如现在栈情况为:A B C。C通过intent跳转到D,这个intent添加FLAG_ACTIVITY_NO_HISTORY标志,则此时界面显示D的内容,但是它并不会压入栈中。如果按返回键,返回到C,栈的情况还是:A B C。如果此时D中又跳转到E,栈的情况变为:A B C E,此时按返回键会回到C,因为D根本就没有被压入栈中。
FLAG_ACTIVITY_SINGLE_TOP:和上面Activity的 Launch mode的singleTop类似。如果某个intent添加了这个标志,并且这个intent的目标activity就是栈顶的activity,那么将不会新建一个实例压入栈中。
具体请看Android之Activity启动流程源码分析,看完这篇就够了