Activity
4 种状态
- Active
- Paused
- Stopped
- Killed
生命周期
Activity正常生命周期
onCreate()
首次创建onStart()
用户可见onResume()
用户交互onPause()
另一个应用开启,该应用暂停onStop()
用户不可见(还在内存)–》onRestart()
–》onStart()
或者process is killed on
–》onStart()
onDestroy()
销毁命令:当内存不足原先不用的会自动销毁onRestart()
重新启动onRestart()
后会调用onNewIntent()
(即,onNewIntent
和onCreate
是不会被同时调用)
onNewIntent()
被调用两次
1 | Intent intent = new Intent(WebTestActivity.this, MainActivity.class); |
以上面的方式经过onRestart()
启动的话onNewIntent()
会被调用两次。
解决:只设置intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
onNewIntent的生命周期
- 只对singleTop , singleTask , singlelnstance有效,因为
standard
每次都是新建,所以不存在onNewlntent
; - 只对startActivity有效,对于从
Navigation
切换回来的恢复无效;
异常情况下的生命周期
onSaveInstanceState
onRestoreInstanceState
一些特殊情况下的生命周期
Activity
的横竖屏切换- 什么时候单独走
onPause()
不走onStop()
当Activity
被另一个透明或者Dialog
样式的Activity
覆盖时走onPause()
不走onStop()
。 此时它依然和WindowManager
保持连接,系统继续维护其内部状态,所以它仍然可见,但它已失去焦点不能与用户交互。 - 什么情况下导致
onDestory()
不执行- 极端情况下:系统内存不足时被杀死是不会调
onDestroy()
。 - 在
MainActivity
中调System.exit(0);
则MainActivity
不会调用onDestroy
- 极端情况下:系统内存不足时被杀死是不会调
IdleHandler比onResume精确
1 |
|
上面例子将IdleHandler
添加到主线程MessageQueue
中,queueIdle()
方法回调,说明UI第一帧绘制完成,可以理解为UI首次可见,这个比onResume
精确的多,因为onResume
回调的时候界面还没有开始绘制,此时界面是不可见的。
IdleHandler应用场景
- 在应用启动时我们可能希望把一些优先级没那么高的操作延迟一点处理,一般会使用
Handler.postDelayed(Runnable r, long delayMillis)
来实现,但是又不知道该延迟多少时间比较合适,因为手机性能不同,有的性能较差可能需要延迟较多,有的性能较好可以允许较少的延迟时间。所以在做项目性能优化的时候可以使用IdleHandler
,它在主线程空闲时执行任务,而不影响其他任务的执行。 - 想要在一个
View
绘制完成之后添加其他依赖于这个View
的View
,当然这个用View.post()
也能实现,区别就是前者会在消息队列空闲时执行 - 发送一个返回
true
的IdleHandler
,在里面让某个View
不停闪烁,这样当用户发呆时就可以诱导用户点击这个View
,这也是种很酷的操作IdelHandler注意事项
MessageQueue
提供了add/remove IdleHandler
方法,但是我们不一定需要成对使用它们,因为IdleHandler.queueIdle()
的返回值返回false
的时候可以移除IdleHanlder
。- 不要将一些不重要的启动服务放到
IdleHandler
中去管理,因为它的处理时机不可控,如果MessageQueue
一直有待处理的消息,那么它的执行时机会很靠后。 - 当
mIdleHanders
一直不为空时,为什么不会进入死循环?- 只有在
pendingIdleHandlerCount
为 -1 时,才会尝试执行mIdleHander
; pendingIdleHandlerCount
在next()
中初始时为 -1,执行一遍后被置为 0,所以不会重复执行;
- 只有在
启动模式
Activity 的任务栈
AndroidManifest.xml
的activity
标签中加taskAffinity="xxx"
,不同taskAffinity
创建不同栈
使用Intent标志
FLAG_ACTIVITY_NEW_TASK
同singleTask
FLAG_ACTIVITY_SINGLE_TOP
同singleTop
FLAG_ACTIVITY_CLEAR_TOP
如果正在启动的activity
已在当前task
中运行,则不会启动该activity
的新实例,而是销毁其上的activity
,并调用其onNewIntent()
启动模式的类型和特性
standard
系统在启动它的任务中创建activity
的新实例
singleTop
如果activity
的实例已存在于当前任务的顶部,则系统通过调用其onNewIntent()
singleTask
系统创建新task
并在task
的根目录下实例化activity
。但如果activity
的实例已存在于单独的任务中,则调用其onNewIntent()
方法。一次只能存在一个activity
实例
singleInstance
相同”singleTask
“,activity
始终是其task
的唯一成员; 任何由此开始的activity
都在一个单独的task
中打开
Activity 组件之间的通信
Activity–》Activity
Intent/Bundle
- 类的静态变量
- 全局变量
Activity–》Service
- 绑定服务,利用
ServiceConnection
类 Intent
Callback+Handler
,监听Service
进程变化
Activity–》Fragment
Bundle
- 直接调用方法
Activity之间通过Intent的通信
Intent
有两个最重要的部分:动作和动作对应的数据
- 典型的动作类型:
MAIN
(activity
的门户)VIEW
PICK
EDIT
- 动作对应的数据则以
URI
的形式进行表示。例如:要查看一个人的联系方式,需要创建一个动作类型为VIEW
的intent
,以及一个表示这个人的URI
- 与之有关系的一个类
IntentFilter
,用于描述一个activity
(或者IntentReceiver
)能够操作哪些intent
Scheme跳转协议
AndroidManifest.xml
清单文件中定义:
1 | ... |
跳转链接:
- 简历页面 https://m.597.com/download/app/?act=resume&rid=d5de934017675&userType=2
- 职位管理页面 https://m.597.com/download/app/?act=jobManage&userType=2
代码中处理:
1 |
|
源码解读 startActivity 都做了什么
Android启动过程
Activity生命周期解析
activity
:直译为“活动”,翻译成“界面”会更好理解。
典型情况下的生命周期分析
**onCreate
**:activity
正被创建,生命周期第一个方法。可做些初始化工作:setContentView
、初始化activity
所需数据。
**onRestart
**:activity
正被重新启动。一般从当前activity
不可见重新变为可见时触发。如:用户按Home切换到桌面再回来。
**onStart
**:activity
正被启动,activity
可见了,未出现在前台,还无法与用户交互。
**onResume
**:activity
可见了,出现在前台并开始活动,可与用户交互了。
**onPause
**:activity
正在停止,正常情况会紧接着调用onStop
。特殊情况,快速再回到当前activity
(用户很难重现这一场景)。可做些存储数据、停止动画等工作,但不能太耗时(因为onPause
走完才执行新activity
的onResume
。太耗时会影响新activity
的显示)
**onStop
**:activity
即将停止。可做些稍微重量级的回收工作,不能太耗时。
**onDestory
**:activity
即将被销毁,activity
生命周期的最后一个回调。可做些回收工作和最终的资源释放。
正常情况下启动
Activity A
启动另一个Activity B
,回调如下:
Activity A
的onPause()
→Activity B
的onCreate()
→onStart()
→onResume()
→Activity A
的onStop()
;如果B是透明主题又或则是个DialogActivity
,则不会回调A的onStop
;- 使用
onSaveInstanceState()
保存简单,轻量级的UI状态1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21lateinit var textView: TextView
var gameState: String? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
gameState = savedInstanceState?.getString(GAME_STATE_KEY)
setContentView(R.layout.activity_main)
textView = findViewById(R.id.text_view)
}
override fun onRestoreInstanceState(savedInstanceState: Bundle?) {
textView.text = savedInstanceState?.getString(TEXT_VIEW_KEY)
}
override fun onSaveInstanceState(outState: Bundle?) {
outState?.run {
putString(GAME_STATE_KEY, gameState)
putString(TEXT_VIEW_KEY, textView.text.toString())
}
super.onSaveInstanceState(outState)
}
几种情况:
- 针对一个特定的
Activity
,第一次启动,回调:onCreate
–>onStart
–>onResume
- 当打开新
activity
或切换到桌面时,回调:onPause
–>onStop
。若新Activity
采用==透明主题(或是个DialogActivity
)==,那么当前Activity
不会回调onStop
- 当再次回到原
Activity
时,回调:onStart
–>onStart
–>onResume
- 当按
back
回退时,回调:onPause
–>onStop
–>onDestory
- 当
Activity
被系统回收后再次打开,生命周期与1一样。注意,只是生命周期方法一样,不代表所有过程都一样 - 从整个生命周期看
onCreate
和onDestory
是配对的,只调一次。从Activity
是否可见看,onStart
和onStop
是配对的,随用户的操作或设备息屏亮屏会调用多次。从Activity
是否在前台看,onResume
和onPause
是配对的,随用户操作和息屏亮屏会调用多次。
两个问题
onStart
和onResume
、onPause
和onStop
从描述上来看差不多,对我们来说有什么实质的不同呢?- 假设当前
Activity为A
,如果这是用户打开一个新ActivityB
,那么B的onResume
和A的onPause
哪个先执行呢?
答案:
- 第一个问题:实际使用我们甚至只保留其中一对。从设计层面分析,
onStart
和onStop
应用于Activity
是否可见这个角度;onResume
和onPause
应用于Activity
是否位于前台这个角度。 - 第二个问题:
Activity
启动的请求会由Instrumentation
来处理,然后它通过Binder
向ActivityManagerServie
(简称AMS
)发请求,AMS
内部维护一个ActivityStack
并负责栈内的Activity
的状态同步,AMS
通过ActivityThread
去同步Activity
的状态从而完成生命周期方法的调用。
异常情况下的生命周期分析
比如当资源相关的系统配置发生改变以及系统内存不足时,Activity
就可能被杀死。下面由这两种情况进行具体分析:
情况1:资源相关的系统配置发生改变导致
Activity
被杀死并重新创建
比如旋转屏幕,由于系统配置发生了改变,默认情况下Activity
会被销毁并且重新创建,当然我们也可以阻止系统重新创建我们的Activity
。
会调用onPause
、onStop
、onDestroy
,其中可能onPause
–>onSaveInstanceState
–>onStop
,也可能onSaveInstanceState
–>onPause
–>onStop
。onRestoreInstanceState
在onStart
之后Activity
被意外终止时,Activity
会调用onSaveInstanceState
去保存数据,然后Activity
会委托Window
去保存数据,接着Window
再委托它上面的顶级容器(是一个ViewGroup
很可能是DecorView
)去保存数据。最后顶层容器再去意义通知它的子元素来保存数据。–》典型的委托思想:上层委托下层、父容器委托子元素去处理一件事情,这种思想还应用在View
的绘制过程、事件分发等。数据恢复过程也是类似。onCreate
和onRestoreInstanceState
中都可以恢复数据,官方推荐用onRestoreInstanceState
资源内存不足导致低优先级的
Activity
被杀死Activity
优先级:前台Activity
–正在和用户交互的activity
,优先级最高- 可见但非前台
activity
–比如activity
中弹出一个对话框,导致activity
可见但是位于后台无法和用户直接交互 - 后台
activity
–已经被暂停的activity
,比如执行了onStop
,优先级最低
如果一个进行中如果没有四大组件在执行,此进程将很快被系统杀死,故一些后台工作不适合脱离四大组件而独自运行在后台中。较好的方式是放入
Service
中
不想重新创建Activity
给Activity
指定configChanges
属性。比如不想让Activity
在旋转时重建 android:configChanges="orientation"
我们常用到的configChanges
属性有locale
、orientation
、keyboardHidden
注意screenSize
和smallestScreenSize
笔记特殊,它们的行为与编译选项有关,与运行环境无关。
如:minSdkVersion
和targetSdkVersion
有一个大于13,在旋转屏幕时不重建Activity
要加orientation
还要加screenSize
,最终调onConfigurationchanged
方法
Activity的启动模式
- standard:标准模式。不管此实例是否存在都会重建一个新的实例。当用
ApplicationContext
(没有所谓的任务栈)去启动standard
模式的Activity
会报错“android.util.AndroidRuntimeException:Calling startActivity from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?
”。如果加了“FLAG_ACTIVITY_NEW_TASK
“其实是以singleTask
模式启动的。 - singleTop:栈顶复用模式。若新
Activity
已位于栈顶,则不会重建,会回调onNewIntent
方法(可以取出当前请求的信息),注意:这个Activity的onCreate、onStart不会被系统调用 - singleTask:栈内复用模式。若
Activity
在栈中存在则不会重建,会回调onNewIntent
。
具体例子:
1. 任务栈S1中有ABC,此时ActivityD
以singleTask
模式请求启动,其所需要的任务栈为S2,由于S2和D实例都不存在,那么会先创建S2,再创建D并将其入栈到S2
2. 任务栈S1中有ABC,此时ActivityD
以singleTask
模式请求启动,其所需要的任务栈为S1,由于S1已存在,那么系统直接创建D并将其入栈到S1
3. 任务栈S1中有ADBC,此时D不会重建,系统会把D切换到栈顶并调用其onNewIntent,同时singleTask
具有clearTop效果。最终S1中是AD - singleInstance:单实例模式。一种加强的
singleTask
模式,具有singleTask
所有特性外,加强了一点,此模式的activity
只能单独位于一个任务栈中。由于栈内复用的特性,后续的请求均不会重建新activity
,除非此任务栈被系统销毁了。
LaunchMode | 说明 |
---|---|
standard | 系统在启动它的任务中创建activity 的新实例 |
singleTop | 如果activity 的实例已存在于当前任务的顶部,则系统通过调用其onNewIntent() |
singleTask | 系统创建新task并在task的根目录下实例化activity 。但如果activity 的实例已存在于单独的任务中,则调用其onNewIntent()方法。一次只能存在一个activity 实例 |
singleInstance | 相同”singleTask “,activity 始终是其task的唯一成员; 任何由此开始的activity 都在一个单独的task中打开 |
使用Intent标志 | 说明 |
---|---|
FLAG_ACTIVITY_NEW_TASK |
同singleTask |
FLAG_ACTIVITY_SINGLE_TOP |
同singleTop |
FLAG_ACTIVITY_CLEAR_TOP |
如果正在启动的activity 已在当前task 中运行,则不会启动该activity 的新实例,而是销毁其上的activity ,并调用其onNewIntent() |
注意:
- 上面说的不同栈,要在
AndroidManifest.xml
的activity
中加taskAffinity="xxx"
,不同taskAffinity
会创建不同栈。(==注意其中xxx要直接写,不能写@string/xxx
==) - 打印
taskId
的代码1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
protected void onResume() {
super.onResume();
getActivityTaskInfo();
}
protected void getActivityTaskInfo() {
ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
ActivityManager.RunningTaskInfo runningTaskInfo = manager.getRunningTasks(1).get(0);
//栈内activity数量
int numActivities = runningTaskInfo.numActivities;
//taskId
int id = runningTaskInfo.id;
ComponentName topActivity = runningTaskInfo.topActivity;
//栈顶activity信息
String className = topActivity.getClassName();
Log.e("activityTask", "id == " + id + "\n" + "numActivity == " + numActivities + "\n" + "className == " + className);
}
IntentFilter的匹配规则
启动Activity
:显式调用、隐式调用。理论上两者不共存,若共存则以显式调用为主。
隐式调用需Intent能匹配目标组件的IntentFilter
中所设置的过滤信息。IntentFilter
过滤信息有action
、category
、data
举例:
1 | <activity |
可以有多个
action
、category
、data
。一个Intent
只有同时匹配action
、category
、data
才能进行跳转。Intent
必须要有一个action
与过滤规则中的某个action
相同。Intent
可以没有category
。
若过滤规则中定义了data
(两部分组成:mimeType
和URI
),那么Intent
中必须也要定义可匹配的data
。
mimeType
指媒体类型比如image/jpeg
、audio/mpeg4-generic
、video/
*等。
多个data
的内容可以写到同一个data
内
Intent-filter
的匹配规则对于Service
和BroadcastReceiver
也是同样的道理,不过对于Service
建议使用显式调用方式来启动服务。
隐式方式启动Activity
时还需要加判断:PackageManager
的resolveActivity()
或Intent
的resolveActivity()
(返回最佳匹配的Activity
),否则找不到匹配的Activity
会返回null
。PackageManager
的queryIntentActivitites
会返回所有成功匹配的Activity
信息。
Activity启动过程
ActivityThread.java
1 | private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) { |
Activity的其他笔记
android属性之clearTaskOnLaunch
需求:每次从桌面进入app都是打开根Activity
要求:在AndroidManifest.xml
中,在根Activity
中添加 clearTaskOnLaunch
属性
1 | <?xml version="1.0" encoding="utf-8"?> |
android属性之alwaysRetainTaskState
常用于浏览器,用来保存浏览状态(打开很多tab),用户再次启动浏览器还能看到之前的状态
原文链接
android:allowTaskReparenting
这个属性用来标记一个Activity
实例在当前应用退居后台后,是否能从启动它的那个task
移动到有共同affinity
的task
,“true”表示可以移动,“false”表示它必须呆在当前应用的task
中,默认值为false。如果一个这个Activity
的元素没有设定此属性,设定在上的此属性会对此Activity
起作用。例如在一个应用中要查看一个web页面,在启动系统浏览器Activity
后,这个Activity
实例和当前应用处于同一个task
,当我们的应用退居后台之后用户再次从主选单中启动应用,此时这个Activity
实例将会重新宿主到Browser应用的task
内,在我们的应用中将不会再看到这个Activity
实例,而如果此时启动Browser应用,就会发现,第一个界面就是我们刚才打开的web页面,证明了这个Activity
实例确实是宿主到了Browser应用的task
内。android:alwaysRetainTaskState
这个属性用来标记应用的task
是否保持原来的状态,“true”表示总是保持,“false”表示不能够保证,默认为“false”。此属性只对task
的根Activity
起作用,其他的Activity
都会被忽略。 默认情况下,如果一个应用在后台呆的太久例如30分钟,用户从主选单再次选择该应用时,系统就会对该应用的task
进行清理,除了根Activity
,其他Activity
都会被清除出栈,但是如果在根Activity
中设置了此属性之后,用户再次启动应用时,仍然可以看到上一次操作的界面。 这个属性对于一些应用非常有用,例如Browser应用程序,有很多状态,比如打开很多的tab,用户不想丢失这些状态,使用这个属性就极为恰当。android:clearTaskOnLaunch
这个属性用来标记是否从task
清除除根Activity
之外的所有的Activity
,“true”表示清除,“false”表示不清除,默认为“false”。同样,这个属性也只对根Activity
起作用,其他的Activity
都会被忽略。 如果设置了这个属性为“true”,每次用户重新启动这个应用时,都只会看到根Activity
,task
中的其他Activity
都会被清除出栈。如果我们的应用中引用到了其他应用的Activity
,这些Activity
设置了allowTaskReparenting
属性为“true”,则它们会被重新宿主到有共同affinity
的task
中。android:finishOnTaskLaunch
这个属性和android:allowReparenting
属性相似,不同之处在于allowReparenting
属性是重新宿主到有共同affinity
的task中,而finishOnTaskLaunch
属性是销毁实例。如果这个属性和android:allowReparenting
都设定为“true”,则这个属性好些。
Android-Activity原理
hencoder当我按下 Home 键再切回来,会发生什么?
任务Task(hencoder)
任务的概念
任务其实就是activity
的栈,它由一个或多个Activity
组成的共同完成一个完整的用户体验。换句话说任务就是 “应用程序” (可以是一个也可以是多个,比如假设你想让用户看到某个地方的街道地图。而已经存在一个具有此功能的activity
了,那么你的activity
所需要做的工作就是把请求信息放到一个Intent
对象里面,并把它传递给startActivity()
。于是地图浏览器就会显示那个地图。而当用户按下BACK
键的时候,你的activity
又会再一次的显示在屏幕上,此时任务是由2个应用程序中的相关activity
组成的)栈底的是启动整个任务的Activity
,栈顶的是当前运行的用户可以交互的Activity
,当一个activity
启动另外一个的时候,新的activity
就被压入栈,并成为当前运行的activity
。而前一个activity
仍保持在栈之中。当用户按下BACK
键的时候,当前activity
出栈,而前一个恢复为当前运行的activity
。栈中保存的其实是对象,栈中的Activity
永远不会重排,只会压入或弹出,所以如果发生了诸如需要多个地图浏览器的情况,就会使得一个任务中出现多个同一Activity
子类的实例同时存在。
任务中的所有activity
是作为一个整体进行移动的。整个的任务(即activity 栈
)可以移到前台,或退至后台。举个例子说,比如当前任务在栈中存有四个activity
──三个在当前activity
之下。当用户按下HOME
键的时候,回到了应用程序加载器,然后选择了一个新的应用程序(也就是一个新任务)。则当前任务遁入后台,而新任务的根activity
显示出来。然后,过了一小会儿,用户再次回到了应用程序加载器而又选择了前一个应用程序(上一个任务)。于是那个任务,带着它栈中所有的四个activity
,再一次的到了前台。当用户按下BACK
键的时候,屏幕不会显示出用户刚才离开的activity
(上一个任务的根activity
)。取而代之,当前任务的栈中最上面的activity
被弹出,而同一任务中的上一个activity
显示了出来。
Activity栈
:先进后出规则
Task工作模型、LaunchMode
观看扔物线《Android面试黑洞–当我按下Home键再切回来,会发生什么?》
视频
文章
Task
和回退栈
- 按方块键查看最近任务,看到的就是一个个
Task
、任务 - 在桌面点击App图标时,配置了
MAIN
+LAUNCHER
的intent-filter
的Activity
会被启动并放进刚创建的一个Task
里 - 每个
Task
都有一个回退栈,它会按顺序记录用户打开的每个Activity
,按回退键时会依次关闭Activity
,当最后一个Activity
被关闭则此Task
声明也就结束了。但不会在最近任务列表里消失,仍会保留一个残影,方便下次切回去(要走App的重新创建流程)。–》“在最近任务里看见的Task
未必还活着”1
2
3Standard、SingleTop:针对App内
SingleInstance:用于多App
SingleTask:兼顾多App和App内
Standard
App2的BActivity
是标准模式。
- App2打开A、B;App1启动App2的
BActivity
,则App1的回退栈中有M、B(复制过来的一个新实例)。App2的回退栈中有A、B。即,标准模式下的Activity会复制多份,分别放到不同的Task
中
allowTaskReparenting
App2的BActivity
是Standard
,且加属性allowTaskReparenting
- App1启动App2的
BActivity
,会在创建BActivity
并把它挪到App1的Task
栈顶。按下Home键,再切换到App2,此时BActivity
会回到App2的Task
栈顶。再切回App1看已经看不到BActivity
了。 - 在Android9、10失效;在Android11上已修复。
SingleTask(唯一性:全局只有一个对象)
App2的BActivity
是SingleTask
。
- App1启动App2的
BActivity
,此时BActivity
会在App1自己的Task
栈顶【此时会有切换App的动画】,而且App2的Task
栈会叠加到App1的Task
栈上面【Task
叠加适用于前台Task
】 - 当App1变成后台时(按方块键查看最近任务、按Home键),叠加的
Task
会拆开。
注意:前台Task
在最近任务列表显示出来的时候就已经进入后台,而不是在切换到其他应用之后 - App1启动App2的
BActivity
,若BActivity
已经存在【复用】【调用onNewIntent
】,则会把App2的Task
栈叠加到App1的Task
栈上,而且App2会移除BActivity
上面的Activity,让BActivity
处于栈顶
SingleInstance(唯一性,独占性)
App2的BActivity
是SingleInstance
。
- 具有
SingleTask
所有特性,而且BActivity
独占一个Task
- App1启动App2的
BActivity
,此时BActivity
会独占一个Task
,也会叠加到App1的Task
上 - 当App1变成后台时(按方块键查看最近任务、按Home键),叠加的
Task
会拆开,当在显示BActivity
界面按回退键,再去看最近任务列表这个BActivity
在最近任务里不可见了。但是它并没有被杀死,如果再次调起BActivity
则走onNewIntent
方法。–》”在最近任务里看不见的Task
,也未必就死了“ SingleInstance
模式的BActivity
被藏起来是因为taskAffinity
冲突了。
taskAffinity
- 在Android中,一个App默认只能有一个
Task
显示在最近任务列表里【由taskAffinity
来甄别】。 - 每个
Activity
都有个taskAffinity
,它的值取其所在Application
的taskAffinity
【默认取App的包名】 - 每个
Task
都有一个taskAffinity
,取值自栈底Activity
的taskAffinity
。即,从打开的Activity界面调起个新的Activity【不管新Activity来自哪】,新Activity的taskAffinity
会被忽略。但若新Activity是SingleTask
,则会和当前Task
的taskAffinity
比较- 若相同,依然正常入栈
- 若不同,
Activity
会寻找和它的taskAffinity
值相同的Task
,然后整个Task
入栈。- 或者如果找不到,系统会为它创建一个新的
Task
- 或者如果找不到,系统会为它创建一个新的
- 最近任务列表中出现的
Task
(任务)的taskAffinity
值是不一样的。相同taskAffinity
的Task
可以被创建多个,但只会在最近任务列表里显示一个。即上面的SingleInstance
在最近任务列表里不可见是因为被相同taskAffinity
替代了。
SingleTop
与Standard
比较相近,直接在当前Task
(任务)上入栈。唯一区别:若要启动的SingleTop
的Activity
已经打开,则调用其onNewIntent
。
A:AActivity(Standard
),B:BActivity(SingleTop
)
- A–>B–>B–>A–>B–>A–>A:栈中有ABABAA
Activity的setContentView过程
代码:
重启Activity及状态保存
- 保存状态、重启、取出原状态,主题切换
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49public class MainActivity extends FragmentActivity implements OnClickListener {
private Button btn;
private int mTheme;
private String THEME = "theme";
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//恢复销毁前状态
if (savedInstanceState != null) {
mTheme = savedInstanceState.getInt(THEME);
switchTheme(mTheme);
}
setContentView(R.layout.activity_main);
btn = (Button) findViewById(R.id.btn);
btn.setOnClickListener(this);
}
protected void onSaveInstanceState(Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
Log.e(MainActivity.class.getName(), "onSaveInstanceState");
//保存销毁前状态
savedInstanceState.putInt(THEME, mTheme);
}
private void switchTheme(int theme) {
switch (mTheme) {
case android.R.style.Theme_Holo_Light:
mTheme = android.R.style.Theme_Black_NoTitleBar;
break;
case android.R.style.Theme_Black_NoTitleBar:
mTheme = android.R.style.Theme_Holo_Light;
break;
default:
mTheme = android.R.style.Theme_Holo_Light;
break;
}
setTheme(mTheme);
}
public void onClick(View v) {
//切换主题
recreate();
}
}
注意:
recreate()方法是在Android3.0引入的,所以如果在3.0之前使用会出现错误
- 调用
1
2
3Intent intent = getIntent();
finish();
startActivity(intent);
集中管理Activity
有时候在设计软件的时候布局复杂的话不利于查看跟更改,这时候我们可以在新建几个Activity
,然后用ActivityGroup来管理这些Activity
(把Activity
当成一个View来显示)
代码如下:
1 | public class MyMain extends ActivityGroup { |
禁用返回键
1 |
|
相比于onBackPressed和onKeyDown方法有时候没有效果,这个方法能保证禁用手机的返回键。