Activity简介
Activity
是基础的应用组件,每个Activity
都会获得一个用于绘制用户界面的窗口,用户可与其提供的界面进行交互。
两个Activity之间的导航
Activity启动
调用startActivity()
启动Activity。
根据目标Activity
是否已知,Activity
有两种启动方式:
Activity
。如: Intent intent = new Intent(this, SignInActivity.class);startActivity(intent);复制代码
2.隐式启动:启动未知类名、具有某种功能的Activity
。如发邮件:
Intent intent = new Intent(Intent.ACTION_SEND);intent.putExtra(Intent.EXTRA_EMAIL, recipientArray);startActivity(intent);复制代码
Activity启动获取返回值
有时,需要从启动的Activity
返回结果。这种情况下,需要使用startActivityForResult()
启动。要想收到返回结果,需要实现onActivityResult()
回调方法。当启动的Activity
完成后,会使用Intent
向onActivityResult()
返回结果。
private void pickPicture(){ Intent intent = new Intent(this,PickPictureActivity.class); startActivityForResult(intent,PICK_PICTURE_CODE);}@Overrideprotected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (resultCode == RESULT_OK && requestCode == PICK_PICTURE_CODE){ // 做其他操作 }}复制代码
private void setResult(){ Intent intent = new Intent(); intent.putExtra("path","/mnt/picture/home.png"); setResult(PICK_PICTURE_CODE,intent);}复制代码
Activity之间的协作
当一个activity
启动另一个activity
时,它们都经历了生命周期的变化,变化顺序如下:
1. Activity
A的onPause()
方法执行
2. Activity
B的onCreate()
、onStart()
、onResume()
依次执行(Activity
B获取焦点)
3. 如果Activity
A不再可见的话,Activity
A的onStop()
方法执行
结束Activity
可以直接调用Activity的finish()
方法结束Activity
,也可以调用finishActivity(int requestCode)
结束之前使用startActivityForResult()
启动的Activity
多数情况下,不需要调用这些方法结束Activity
,Android系统会自动管理这些Activity
的生命周期。调用这些方法可能会对用户的预期体验有所影响,所以除非确实不想让用户返回此Activity
实例时才主动结束。
Activity生命周期
通过回调方法管理Activity
的生命周期,可以灵活的控制与用户的交互。Activity
的生命周期会直接受到其他Activity
、任务及返回栈的关联性的影响。
Activity的四种主要状态
1. 运行态:activity
位于屏幕最前面(位于栈顶)。
2. 暂停态:activity
失去焦点但仍然可见(如一个非全屏或透明的activity
位于该activity
表面),位于该状态的activity
仍处于活跃状态(保存所有状态和变量信息,和窗口管理器关联),但是在系统内存极度紧缺的情况下,可能会被系统杀死。
3. 停止态:activity
完全被其他activity
掩盖,不可见。仍然保留所有状态和变量信息,但是在系统其他地方需要内存的情况下,可能会被系统杀死。
4. 恢复态:处于暂停态和停止态的activity
,系统可能会通过让activity
自己调用finish()
方法或直接杀死该activity
所在进程的方式结束该activity
。当重新展示给用户的时候,必须完整地重新启动并恢复之前的状态。
Activity的三种关键循环
1. 完整生命周期:发生在onCreate()
和onDestroy()
之间。一般在onCreate()
中做全局状态的设置,在onDestroy()
销毁所有保留的资源。如在后台下载网络数据的线程,在onCreate()
中创建,在onDestroy()
中销毁。
2. 可见生命周期:发生在onStart()
和onStop()
之间。期间,Activity虽然可见,但是不在前台并与用户交互。在这两个方法里,需要维护Activity
要展示给用户的资源。如在onStart()中注册监听UI改变的广播,在onStop()
中要注销该广播。onStart()
和onStop()
可能会被调用多次,因为activity
或许会不断的可见或隐藏。
3. 前台生命周期:发生在onResume()
和onPause()
之间。期间,activity
可见并与用户交互。activity
会在这两个状态之间不断切换—例如设备休眠、传输新的Intent
—所以这些方法应该非常轻量级,不要处理耗时任务。
Activity状态和进程状态的关系
系统不会直接杀死activity
,相反,会杀死该activity
所在的进程。系统在需要释放RAM
时会杀死进程,被杀死的可能性依赖于进程的状态,而进程的状态依赖于在该进程中运行的activity
的状态。
配置改变对activity的影响
如果设备的配置(横竖屏、语言等)发生变化,展现给用户的界面也要被更新以匹配变化。除非特别指定,否则的话配置变化(横竖屏、语言等)必然会导致activity
被销毁。
如果activity
处于前台或可见状态,一旦onDestroy()
被调用就会创建一个新的activity实例,并伴随着之前activity
的onSaveInstanceState(Bundle)
中保存的状态,以便恢复数据。
特殊情况下,配置发生变化,我们不希望重新创建activity
实例,可以在Manifest
中定义android:configChanges
属性,这样的配置变化时,就会调用onConfigurationChanged(Configuration)
,不会重启启动activity
。如下:
android:configChanges="orientation|screenSize“复制代码
activity数据保存和恢复
activity状态数据保存
当activity
被系统主动销毁时(如内存不足,系统回收资源),系统会调用onSaveInstanceState()
(onPause()
之后,onStop()
之前执行)方法以键值对集合的方式保存activity
的view
层的信息(比如EditText
的内容、CheckBox
的选中状态)。所以必须在onPause()
方法后实现onSaveInstanceState()
方法:
static final String STATE_SCORE = "playerScore";static final String STATE_LEVEL = "playerLevel";...@Overridepublic void onSaveInstanceState(Bundle savedInstanceState) { // Save the user's current game state savedInstanceState.putInt(STATE_SCORE, mCurrentScore); savedInstanceState.putInt(STATE_LEVEL, mCurrentLevel); // Always call the superclass so it can save the view hierarchy state super.onSaveInstanceState(savedInstanceState);}复制代码
注:为了系统能恢复每一个view
的状态,view
必须有唯一的id(android:id=""
)
activity持久数据保存
activity
主要处理两种持久数据:共享文件—如数据(使用Content Provider
保存在SQLite
)和内部状态—如用户偏好(preference)。
对于content provider
数据,建议采用“就地编辑”模型,即随时随地保存,不用请求用户确认。一般需要遵守两个规则:
1. 创建新文件时,同时在后台数据库或文件中对该文件进行备份。如写邮件时,只要用户输入数据,就创建备份,便于用户调到其他activity再返回该界面时,内容还在。
2. 当activity
的onPause()
方法调用时,应该把用户的改动及时保存到后台content provider
或文件中。
“就地编辑”模型可以避免用户在两个activity
之间切换的时候数据丢失,同时也可以使得系统安全的杀死activity
(内存不足时,系统会回收资源)。记住,当用户点击“back”时,并不意味着取消输入的内容—它意味着保存当前的内容,取消编辑必须在activity
中明确声明,如revert。
activity
也保存用户偏好数据,如用户喜好的时钟时间或web浏览器的主页。保存内部数据使用Context.getSharedPreferences()
,可以实现多组件(activity
、service
,receiver
,provider
)公用,但是不支持多应用之间访问(需要使用content provider
)。
Activity状态数据恢复
当activity
被重新创建时,可以从系统传递给activity
的bundle
中恢复之前保存的数据。onCreate()
和onRestoreInstanceState()
都会收到该bundle
,我们可以自由选择在哪个方法中恢复数据。但是由于onCreate()
是类创建时必须调用的,所以bundle
可能为空(第一次创建该activity
),所以在解析bundle
之前必须校验非空:
@Overrideprotected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Always call the superclass first // Check whether we're recreating a previously destroyed instance if (savedInstanceState != null) { // Restore value of members from saved state mCurrentScore = savedInstanceState.getInt(STATE_SCORE); mCurrentLevel = savedInstanceState.getInt(STATE_LEVEL); } else { // Probably initialize members with default values for a new instance } ...}复制代码
如果在onRestoreInstanceState()
(在onStart()
后调用)中恢复数据,系统只有在bundle
中有需要恢复数据的话才会调用该方法;如果没有需要恢复的数据的话不会调用该方法。
public void onRestoreInstanceState(Bundle savedInstanceState) { // Always call the superclass so it can restore the view hierarchy super.onRestoreInstanceState(savedInstanceState); // Restore state members from saved instance mCurrentScore = savedInstanceState.getInt(STATE_SCORE); mCurrentLevel = savedInstanceState.getInt(STATE_LEVEL);}复制代码
注:总是调用onRestoreInstanceState()
方法,以便系统能自动恢复view
状态
activity启动模式
task
是activity
集合,这些activity
被放入返回栈中。启动模式可以定义新Activity
和task
是如何关联的。可以使用两种方式定义启动模式
1. 使用Manifest
文件
2. 使用intent flags
使用manifest文件定义启动模式
使用<activity>
元素的launchModel
属性定义启动模式,共有四种:
1.standard
:默认模式,每一次启动都会创建一个新的activity
实例,并和创建者位于同一个Task
内。过程如下:
2. singleTop
:如果activity
不存在或不在栈顶,就创建一个实例;如果activity
位于栈顶,就调用onNewIntent()
方法。被启动的activity
始终和启动activity
在同一个Task
内。用途:QQ信息提示框,不同时间接收的消息全部显示在一个提示框中。
3. singleTask
:系统唯一,但是位于可能包括多个Activity
实例的task
中
应用内启动:如果task中不存在该activity
实例,就会创建该activity
实例;存在并位于栈顶,调用onNewInten()
方法;存在但不位于栈顶,会销毁该实例以上的所有activity
,并调用onNewIntent()
方法。
应用外启动:如果activity
实例已存在一个task
中的话,启动后,系统会将这个task
切换到前台,并将该activity
实例上的activity
全部销毁;如果activity
实例不存在,会另起一个task
(目标activity
所在应用的task
,如果不存在会创建),并创建activity
实例位于栈顶。
4. singleInstance
:系统单例模式,位于一个独享的Task
中。如果系统中不存在该activity
实例,就另起一个task
,并创建唯一activity
实例位于栈底;如果系统中存在该Activity实例,就直接调用onNewIntent()
方法启动实例。
使用Intent flags
启动Activity
时,可通过intent.setFlags()
设置启动模式;共有三种:
1. :创建一个新的Task
(如果不指定TaskAffinity
,默认放到与包名相同的任务栈中),并把启动的Activity
置于栈顶。官网说和singleTask
模式功能相同,实际测试并非如此:
Activity
A启动Activity
B,Activity
B启动Activity
C,Activity
C再启动Activity
B。以两种模式分别启动Activity
B,结果如下:
:,栈的结构(从底到顶):A—B—C—B;
singleTask:栈的结构(从底到顶):A—B
2. :如果activity
位于栈顶,则调用onNewIntent()
,而非重建;否则的话重新创建;效果和singleTop
相同
3. :要启动的Activity
已经运行在当前的task
中,就会销毁该activity
上的所有activity
,并调用onNewIntent()
;
taskAffinity
taskAffinity
表示Activity
和task
之间的相关性(默认是应用包名)。默认情况下,同一app中所有activity
都具有相同的taskAffinity
,即都在同一个task中。但是,activity
的taskAffinity
可以在manifest
文件中<activity>
元素的taskAffinity
属性中修改。不同app的activity
可以共享同一个taskAffinity
,同一app中的activity
可以具有不同的taskAffinity
。
taskAffinity在两种情况下发挥作用:
1. 以模式启动Activity
时:以该模式启动activity
时,系统经常会寻求一个不同的task
存放activity
;但是如果已存在一个task
和该activity
具有相同taskAffinity
(activity
和task
的默认taskAffinity
都是包名),该activity
就会被加载到这个task
中。
2. allowTaskReparenting
属性被设置为true:该属性表示activity
能否从启动task
中迁移到与其具有相同taskAffinity
的task
中。常见的如在app中打开浏览器的activity
A,当退出app,打开浏览器时,显示的也是activity
A。
清除返回栈
如果用户离开一个task
太长时间,系统会自动把该task
中除了根activity
以外的所有activity
销毁。用户返回该task时,也只有根activity
能被恢复。系统之所以这样做的原因是它认为你已经放弃了该task
。通过以下熟悉可以修改系统行为:
1. :如果该属性在task
的根activity
中设置为true的话,task
会很长时间保留所有的activity
,避免被系统杀死。
2. :如果该属性在task
的根activity
中设置为true
的话,一旦离开task
,系统会立刻销毁除根activity
之外的其他activity
。
3. :该属性和clearTaskOnLaunch
类似,但是只作用于单个activity
(包括根Activity
)。也就是说,如果一个activity
设置该属性为true
,一旦离开该activity
,该activity
就会被离开销毁。