大家好,我叫石头
Activity是什么?
相信大家都知道Android中的4大组件(Activity
活动,Service
服务,ContentProvider
内容提供者,BroadcastReceiver
广播接收器),Activity
是我们使用最多的也是最基本的组件,Activity
提供窗口和用户进行交互.
Android中的activity
全都归属于task
管理(task
是一个具有栈结构的容器),task
是多个activity
的集合,android默认情况会为每个App
维持一个task
来存放app
的所有activity
(当然这只是默认情况),task
的默认name
为该app
的packagename
(包名)
当然我们也可以在AndroidMainfest.xml
中申明activity
的 taskAffinity 属性来自定义task
,但是不建议使用,因为如果其他app也申明了同样的task
,那个app
可能启动到你的activity
,这样会带来各种安全问题(比如拿到你的Intent
).
Activity的内部调用
上面我们介绍过了,系统是通过task
(栈)的方式来管理activity的,当一个新的activity开始的时候,该activity
会被放置在堆栈(back stack
)的顶部,成为正在运行的活动,之前的activity始终保持低于它在堆栈,而不会出现在前台.
官方activity_lifecycle:
当我们打开一个新的activity
实例的时候,系统会以此调用
onCreate() -> onStart() -> onResume() 然后开始running
当activity
在running的时候如果被覆盖(打开新的activity,或者被锁屏,但是它依然在前台运行,lost focus but is still visible),系统就会调用onPause()
;
在
onpause()
方法中,我们通常会提交未保存的更改到持久化数据,停止动画和其他东西.但是我们要知道现在这个activity还是完全活着(它保存所有的状态和成员信息,并且保存到窗口管理器的连接)
- 1.启动
Activity
:系统会先调用onCreate
方法,然后调用onStart
方法,最后调用onResume
,Activity
进入运行状态。 - 2.当前
Activity
被其他Activity
覆盖其上或被锁屏:系统会调用onPause
方法,暂停当前Activity
的执行。 - 3.当前
Activity
由被覆盖状态回到前台或解锁屏:系统会调用onResume
方法,再次进入运行状态。 - 4.当前
Activity
转到新的Activity
界面或按Home
键回到主屏,自身退居后台:系统会先调用onPause
方法,然后调用onStop
方法,进入停滞状态。 - 5.用户后退回到此
Activity
:系统会先调用onRestart
方法,然后调用onStart
方法,最后调用onResume
方法,再次进入运行状态。 - 6.当前
Activity
处于被覆盖状态或者后台不可见状态,即第2步和第4步,系统内存不足,杀死当前Activity,而后用户退回当前Activity
:再次调用onCreate
方法、onStart
方法、onResume
方法,进入运行状态。 - 7.用户退出当前
Activity
:系统先调用onPause
方法,然后调用onStop
方法,最后调用onDestory
方法,结束当前Activity
。
onSaveInstanceState
:(1)在Activity被覆盖或退居后台之后,系统资源不足将其杀死,此方法会被调用;(2)在用户改变屏幕方向时,此方法会被调用;(3)在当前Activity
跳转到其他Activity
或者按Home
键回到主屏,自身退居后台时,此方法会被调用。第一种情况我们无法保证什么时候发生,系统根据资源紧张程度去调度;第二种是屏幕翻转方向时,系统先销毁当前的Activity
,然后再重建一个新的,调用此方法时,我们可以保存一些临时数据;第三种情况系统调用此方法是为了保存当前窗口各个View
组件的状态。onSaveInstanceState
的调用顺序是在onPause
之前。
onRestoreInstanceState
:(1)在Activity
被覆盖或退居后台之后,系统资源不足将其杀死,然后用户又回到了此Activity
,此方法会被调用;(2)在用户改变屏幕方向时,重建的过程中,此方法会被调用。我们可以重写此方法,以便可以恢复一些临时数据。onRestoreInstanceState
的调用顺序是在onStart
之后。
activity被回收的状态和信息保存和恢复过程
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
if(savedInstanceState!=null){ //判断是否有以前的保存状态信息
savedInstanceState.get("Key");
}
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
protected void onSaveInstanceState(Bundle outState) {
// TODO Auto-generated method stub
//可能被回收内存前保存状态和信息,
Bundle data = new Bundle();
data.putString("key", "last words before be kill");
outState.putAll(data);
super.onSaveInstanceState(outState);
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
// TODO Auto-generated method stub
if(savedInstanceState!=null){ //判断是否有以前的保存状态信息
savedInstanceState.get("Key");
}
super.onRestoreInstanceState(savedInstanceState);
}
}
onSaveInstanceState()
方法,在activity
被回收之前调用,用来保存自己的状态信息,以便回收后重建时恢复数据(在onCreate()
或onRestoreInstanceState()
中恢复).旋转屏幕重建activity会调用该方法,但其他情况在onRause()
和onStop()
状态的activity不一定会调用,
下面是官方文档说明:
One example of when onPause and onStop is called and not this method is when a user navigates back from activity B to activity A: there is no need to call onSaveInstanceState on B because that particular instance will never be restored, so the system avoids calling it. An example when onPause is called and not onSaveInstanceState is when activity B is launched in front of activity A: the system may avoid calling onSaveInstanceState on activity A if it isn't killed during the lifetime of B since the state of the user interface of A will stay intact.
也就是说,系统灵活的来决定会不会调用该方法,但是如果要调用就一定发生在onStop
方法之前,但不保证发生在onPase的前面还是后面.
onRestoreInstanceState()
该方法在onstart
和onPostCreate
之间调用,当然我们也可以在onCreat中恢复之前我们在onSaveInstanceState()
中保存下来的数据,但是我们有时候需要在初始化布局完成之后再恢复数据,这是就可以在onRestoreInstanceState()
中恢复数据.
onPostCreate()
:一般我们都没有实现这个方法,它的作用是在代码开始运行之前,调用系统做最后的初始化工作.
关于启动模式
启动模式是什么?
简单点说就是:定义activity实例与task的关联方式
为什么我们要定义启动模式呢?
为了实现默认启动(
standard
)模式之外的需求:
- 让某个
activity
启动一个新的task
(而不是被放入当前task
) - 让activity启动时只调用已有的某个实例(而不是在back stack(之前我们有提到过哟) 顶创建一个新的实例)
- 当用户离开task时只想保留根
activity
,而back stack
中的其他activity都要清空.
怎样定义启动模式
定义启动模式的方法有2种:
- 使用
manifest
文件定义 - 使用
Intent
标志定义
使用manifest文件定义启动模式:
在 manifest 文件中activity声明时,利用 activity 元素的 launchMode 属性来设定 activity 与 task 的关系。
.......
注意: 你用 launchMode 属性为 activity 设置的模式可以被启动 activity 的 intent 标志所覆盖,代码的优先级最高。
现在我们知道了怎么定义启动模式了,但是有哪些启动模式呢?
-
standard
(默认模式)
当通过这种模式启动activity时,Android总会为目标 Activity创建一个新的实例(之前有过也会重新创建),并将该Activity添加到当前Task栈中。这种方式不会启动新的Task,只是将新的 Activity添加到原有的Task中。
-
singleTop
该模式和standard模式基本一致,但有一点不同:当将要被启动的Activity已经位于Task栈顶时,系统不会重新创建目标Activity实例,而是直接复用Task栈顶的Activity。
-
singleTask
Activity在同一个Task内只有一个实例。
如果将要启动的Activity不存在,那么系统将会创建该实例,并将其加入Task栈顶;
如果将要启动的Activity已存在,且存在栈顶,直接复用Task栈顶的Activity。
如果Activity存在但是没有位于栈顶,那么此时系统会把位于该Activity上面的所有其他Activity全部移出Task,从而使得该目标Activity位于栈顶。
-
singleInstance
无论从哪个Task中启动目标Activity,只会创建一个目标Activity实例且会用一个全新的Task栈来装载该Activity实例(全局单例).
如果将要启动的Activity不存在,那么系统将会先创建一个全新的Task,再创建目标Activity实例并将该Activity实例放入此全新的Task中。
如果将要启动的Activity已存在,那么无论它位于哪个应用程序,哪个Task中;系统都会把该Activity所在的Task转到前台,从而使该Activity显示出来。
和"singleTask"类似,唯一不同的是系统不会在这个activity的实例所在的task中启动任何其他activity。
这个activity的实例永远是这个task中的唯一一个成员,这个activity启动的任何其他activity都将在另外的task中打开。
使用Intent标识定义启动模式:
FLAG_ACTIVITY_NEW_TASK
和之前讨论过的"singleTask"相同,在新的task中启动activity,如果一个你需要的activity的task已经存在,则将它推向前台,恢复其上一个状态,它通过onNewIntent()收到这个新的intent。FLAG_ACTIVITY_SINGLE_TOP
和"singleTop"行为相同,如果被启动的activity是当前顶部的activity,则已经存在的实例收到 onNewIntent()
,而不是新建实例。FLAG_ACTIVITY_CLEAR_TOP
如果被启动的activity已经在当前task运行,不创建它的新实例,而是销毁在它之上的其他所有activities,然后通过 onNewIntent()
传递一个新的intent给这个恢复了的activity。
这个行为在 launchMode
中没有对应的属性值。
注意,如果activity的启动模式是"standard",它自己也将被移除,然后一个新的实例将被启动。
这是因为当启动模式是"standard"时,为了接收新的intent必须创建新的实例。
处理affinities
Affinity指示了activity更倾向于属于哪个task。
默认情况下,同一个应用的activities倾向于在同一个task中。你可以通过
来修改这种行为。
详细内容请查看:API Guides: Tasks and Back Stack
http://developer.android.com/guide/components/tasks-and-back-stack.html
开启一个task
你可以通过给activity一个intent filter(action是"android.intent.action.MAIN",category是"android.intent.category.LAUNCHER"),让这个activity是一个task的进入点。
如下:
...
一个这样的intent filter会使得这个activity的icon和label显示在程序启动处,提供了一种方法,使得用户可以启动这个activity,当它启动后,用户也可以通过它来返回到这个task。
第二个能力是很重要的:用户必须能够离开一个task,然后通过activity launcher返回到它。
因为这个原因,两个让activity永远实例化一个task的启动模式:"singleTask" 和"singleInstance",应该仅在activity有一个 ACTION_MAIN 和CATEGORY_LAUNCHER filter的时候用它们。