文章目录
- 一、生命周期
- 二、启动模式
- 1.标准模式:`android:launchMode="standard"`
- 2.栈顶复用模式:`android:launchMode="singleTop"`
- 3.栈内复用模式:`android:launchMode="singleTask"`
- 4.单实例模式:`android:launchMode="singleInstance"`
- 三、隐式启动Activity
- 1.action匹配规则
- 2.category匹配规则
- 3.data匹配规则
- 4.隐式跳转的例子
- 5.启动Activity的标准的Action常量及对应的字符串
- 6.标准的Category常量及对应的字符串
- 参考文章
一、生命周期
Google官方公布的Activity生命周期图:
1.正常流程
PS:打印生命周期的代码时,可以在当前方法中使用Thread.currentThread().stackTrace[2].methodName
获取当前方法名。或提取出方法,使用stackTrace[3]:
fun getCurrentMethod(): String {
return Thread.currentThread().stackTrace[3].methodName
}
2.跳转到另一个页面再返回
需要注意的是,onPause
是在页面销毁时立即执行,onPause
执行完后第二个页面才能执行onCreate -> onStart -> onResume
流程,onStop
是在第二个页面onResume
之后才执行。
由此可知:onPause
中不应做太耗时的操作,如果onPause
太耗时会影响新Activity的显示。
3.旋转手机
如果当前Activity没有在AndroidManifest中配置android:configChanges="orientation"
,旋转手机时页面将重建,生命周期如下:
这和调用Activity的recreate
方法的生命周期是一样的。
如果当前Activity在AndroidManifest中配置了android:configChanges="orientation"
,旋转手机时页面不会重建,生命周期如下:
每旋转一次,就会调用一次onConfigurationChanged
。可以通过判断newConfig?.orientation获取当前手机屏幕方向。
override fun onConfigurationChanged(newConfig: Configuration?) {
super.onConfigurationChanged(newConfig)
when (newConfig?.orientation) {
Configuration.ORIENTATION_PORTRAIT -> {
// 现在是垂直方向
}
Configuration.ORIENTATION_LANDSCAPE -> {
// 现在是横置方向
}
}
}
其他配置也是一样,当配置发生改变时,默认重建Activity。如果不希望Activity在配置发生改变时重建,在AndroidManifest中配置相应的configChanges
属性即可。
4.onSaveInstanceState() 和 onRestoreInstanceState()触发时机
onSaveInstanceState
和 onRestoreInstanceState
并不是生命周期方法,不一定被触发。以下是onSaveInstanceState
调用时机:
(1)、按下HOME键时;
(2)、查看最近运行的程序时;
(3)、息屏时;
(4)、跳转到另一个Activity时;
这四个调用时机都属于同一种情况:当前 Activity 可能因为长时间不使用或者系统内存不足被销毁,如果Activity确实被销毁了,再次回到当前页面时Activity会重建,onRestoreInstanceState
才会被调用,否则不会被调用。
(5)、Activity重建时。
此时 Activity 必然被销毁,onRestoreInstanceState
必然被调用。
总而言之,onSaveInstanceState
的调用遵循一个重要原则,即当系统存在 未经你许可销毁你的activity的可能时, onSaveInstanceState
会被系统调用,因为这是系统的责任,它必须要提供一个机会让你保存你的数据。如果Activity确实被销毁了,再次回到当前页面时Activity会重建, onRestoreInstanceState
会被调用,并且把onSaveInstanceState
中保存的Bundle对象作为参数传递给 onRestoreInstanceState
和 onCreate
方法。
如果是用户正常销毁Activity,onSaveInstanceState
不会被调用。例如用户按下返回键或程序调用finish
方法销毁Activity时,不会触发onSaveInstanceState
。
另外,和Activity一样,每个View都有onSaveInstanceState
和onRestoreInstanceState
方法,android自带的UI控件都恰当的实现了onSaveInstanceState
和onRestoreInstanceState
方法。因此,当activity被摧毁和重建时,这些UI控件会自动保存和恢复状态数据。比如EditText控件会自动保存和恢复输入的数据,CheckBox控件会自动保存和恢复选中状态。开发者只需要为这些控件指定一个唯一的ID(通过设置android:id属性即可),剩余的事情就可以自动完成了。如果没有为控件指定ID,这个控件不会进行自动的数据保存和恢复。
二、启动模式
1.标准模式:android:launchMode="standard"
这是默认的启动模式,每次启动Activity时都会创建一个新的Activity,无论同样的Activity是否已经存在。
2.栈顶复用模式:android:launchMode="singleTop"
如果要启动的新Activity已经在栈顶,此Activity不会被重建,生命周期回调如下:
在onNewIntent
中可以获取到新启动请求的Intent信息。
3.栈内复用模式:android:launchMode="singleTask"
在这种模式下,只要Activity在任何一个Activity栈中存在,那么多次启动此Activity都不会重新创建实例,和singleTop
一样,系统也会回调其onNewIntent
。
singleTask
默认具有clearTop
的效果,即:如果启动的Activity在栈中已经存在,启动时会将位于其上面的Activity全部出栈。
4.单实例模式:android:launchMode="singleInstance"
这是一种加强的singleTask
模式,它除了具有singleTask
模式的所有特性外,还加强了一点,那就是具有此种模式的Activity只能单独地位于一个任务栈中。
注:如果需要指定Activity运行在一个另一个栈中,除了使用singleInstance
外,还可以使用taskAffinity
属性+singleTask
启动模式。taskAffinity
属性默认为包名,指定时至少需要包含一个分隔符"."
。例如:
<activity
android:name=".SecondActivity"
android:taskAffinity="com.myTask"
android:launchMode="singleTask" />
查看Activity栈信息的代码如下:
val activityManager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
// maxNum表示最多获取多少个Activity栈
val maxNum = 10
val runningTasks = activityManager.getRunningTasks(maxNum)
Log.d("~~~","taksNums = ${runningTasks.size}")
runningTasks.forEach {
Log.d("~~~", "numActivities = ${it.numActivities},topActivityName = ${it.topActivity.shortClassName}
}
使用前需要先在AndroidManifest中添加权限:
<!--获取Activity任务栈 权限-->
<uses-permission android:name="android.permission.GET_TASKS" />
此权限和getRunningTasks
方法都已经标记过时了,此方法的注释上解释道因为不安全所以限制了使用,但使用时仍然可以获取到自己程序的任务栈,stackoverflow上已有过讨论,所以请放心使用。
三、隐式启动Activity
使用IntentFilter隐式启动Activity时,需要action、category、data信息同时匹配成功才能启动。
一个Activity中可以有多个intent-filter,只要Intent能够匹配任何一组intent-filter,即可成功启动Activity。
1.action匹配规则
Intent中必须有action,且必须和intent-filter中的其中一个action相同,action区分大小写;
2.category匹配规则
每一个Intent在 startActivity 或 startActivityForResult 时都会默认添加android.intent.category.DEFAULT
这个category,所以自定义intent-filter时必须添加android.intent.category.DEFAULT
。
Intent中可以不指定category,因为android.intent.category.DEFAULT
这个category已经能够匹配intent-filter,如果指定了其他category,则必须和intent-filter中的category其中一个相同。
3.data匹配规则
如果intent-filter中定义了data,与action类似,Intent中必须有data,且必须和intent-filter中的其中一个data相同才能匹配。
data语法如下:
<data
android:scheme="string"
android:host="string"
android:port="8080"
android:path="/string"
android:pathPattern="string"
android:pathPrefix="/string"
android:mimeType="string"/>
含义是:
<scheme>://<host>:<port>/[<path>|<pathPrefix>|<pathPattern>]
例如:
content://com.example.project:200/folder/subfolder/etc
http://www.baidu.com:80/search/info
4.隐式跳转的例子
AndroidManifest中定义intent-filter:
<activity android:name=".SecondActivity">
<intent-filter>
<action android:name="action" />
<category android:name="category" />
<category android:name="android.intent.category.DEFAULT" />
<data
android:mimeType="image/*"
android:scheme="content" />
</intent-filter>
</activity>
Intent设置action、category、data隐式跳转:
val intent = Intent()
intent.action = "action"
intent.addCategory("category")
intent.setDataAndType(Uri.parse("content://abc"), "image/png")
startActivity(Intent.createChooser(intent, "Choose app"))
注:隐式启动时,如果没有任何Activity匹配此Intent,程序会报android.content.ActivityNotFoundException: No Activity found to handle Intent
异常,所以推荐做法是:
(1)使用
Intent.createChooser(intent, "Choose app")
(2)或检查
packageManager.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY) != null
(3)或检查
intent.resolveActivity(packageManager) != null
5.启动Activity的标准的Action常量及对应的字符串
Action常量 | 对应字符串 | 简单说明 |
---|---|---|
ACTION_MAIN | android.intent.action.MAIN | 应用程序入口 |
ACTION_VIEW | android.intent.action.VIEW | 显示指定数据 |
ACTION_ATTACH_DATA | android.intent.action.ATTACH_DATA | 指定某块数据将被附加到其他地方 |
ACTION_EDIT | android.intent.action.EDIT | 编辑指定数据 |
ACTION_PICK | android.intent.action.PICK | 从列表中选择某项并返回所选的数据 |
ACTION_CHOOSER | android.intent.action.CHOOSER | 显示一个Activity选择器 |
ACTION_GET_CONTENT | android.intent.action.GET_CONTENT | 让用户选择数据,并返回所选数据 |
ACTION_DIAL | android.intent.action.DIAL | 显示拨号面板 |
ACTION_CALL | android.intent.action.CALL | 直接向指定用户打电话 |
ACTION_SEND | android.intent.action.SEND | 向其他人发送数据 |
ACTION_SENDTO | android.intent.action.SENDTO | 向其他人发送消息 |
ACTION_ANSWER | android.intent.action.ANSWER | 应答电话 |
ACTION_INSERT | android.intent.action.INSERT | 插入数据 |
ACTION_DELETE | android.intent.action.DELETE | 删除数据 |
ACTION_RUN | android.intent.action.RUN | 运行数据 |
ACTION_SYNC | android.intent.action.SYNC | 执行数据同步 |
ACTION_PICK_ACTIVITY | android.intent.action.PICK_ACTIVITY | 用于选择Activity |
ACTION_SEARCH | android.intent.action.SEARCH | 执行搜索 |
ACTION_WEB_SEARCH | android.intent.action.WEB_SEARCH | 执行Web搜索 |
ACTION_FACTORY_TEST | android.intent.action.FACTORY_TEST | 工厂测试的入口点 |
6.标准的Category常量及对应的字符串
Category常量 | 对应字符串 | 简单说明 |
---|---|---|
CATEGORY_DEFAULT | android.intent.category.DEFAULT | 默认的Category |
CATEGORY_BROWSABLE | android.intent.category.BROWSABLE | 指定该Activity能被浏览器安全调用 |
CATEGORY_TAB | android.intent.category.TAB | 指定该Activity作为TabActivity的Tab页 |
CATEGORY_LAUNCHER | android.intent.category.LAUNCHER | Activity显示顶级程序列表中 |
CATEGORY_INFO | android.intent.category.INFO | 用于提供包信息 |
CATEGORY_HOME | android.intent.category.HOME | 设置该Activity随系统启动而运行 |
CATEGORY_PREFERENCE | android.intent.category.PREFERENCE | 该Activity是参数面板 |
CATEGORY_TEST | android.intent.category.TEST | 该Activity是一个测试 |
CATEGORY_CAR_DOCK | android.intent.category.CAR_DOCK | 指定手机被插入汽车底座(硬件)时运行该Activity |
CATEGORY_DESK_DOCK | android.intent.category.DESK_DOCK | 指定手机被插入桌面底座(硬件)时运行该Activity |
CATEGORY_CAR_MODE | android.intent.category.CAR_MODE | 设置该Activity可在车载环境下使用 |
Category常量 | 对应字符串 | 简单说明 |
CATEGORY_DEFAULT | android.intent.category.DEFAULT | 默认的Category |
CATEGORY_BROWSABLE | android.intent.category.BROWSABLE | 指定该Activity能被浏览器安全调用 |
CATEGORY_TAB | android.intent.category.TAB | 指定该Activity作为TabActivity的Tab页 |
CATEGORY_LAUNCHER | android.intent.category.LAUNCHER | Activity显示顶级程序列表中 |
CATEGORY_INFO | android.intent.category.INFO | 用于提供包信息 |
CATEGORY_HOME | android.intent.category.HOME | 设置该Activity随系统启动而运行 |
CATEGORY_PREFERENCE | android.intent.category.PREFERENCE | 该Activity是参数面板 |
CATEGORY_TEST | android.intent.category.TEST | 该Activity是一个测试 |
CATEGORY_CAR_DOCK | android.intent.category.CAR_DOCK | 指定手机被插入汽车底座(硬件)时运行该Activity |
参考文章
Android开发之InstanceState详解
获取Activity栈,判断当前Activity位置
《Android开发艺术探索》