您的位置:首页 > 移动开发 > Android开发

Android核心组件之Activity

2014-07-13 12:43 351 查看
转载请注明出处:http://blog.csdn.net/yegongheng/article/details/37738013

1、什么是Activity

Activity的官方解释是:Activity是一个为了用户完成某些功能(诸如:打电话、发送信息、照相和查看地图等)而提供一个界面的应用程序组件。Activity作为Android应用程序中四大组件之一,是其中最基本也是最复杂的一个组件。它为用户提供了一个与应用程序交互的一个接口,开发人员通过setContentView(View)方法把UI布局页面绑定并展示在Activity创建的窗口上,因此Activity也可以看做是Android各个组件的容器。为了完成各式功能,手机须有页面之间的相互跳转,因此一个较复杂的应用程序含有多个Activity。一个Activity通过Intent可以启动另一个新的Activity,新的Activity展示在用户界面,旧Activity被压入back
stack中,期间涉及到Activity的生命周期和任务栈的知识,下面将会一一进行分析。

2、Activity的生命周期

Activity 生命周期包含onCreate() 、onStart()、onResume()、onPause()、onStop()、onRestart()和onDestroy()七个方法,当Activity处于不同状态时将调用各个方法。

Activity 生命周期各个回调方法梗概:

onCreate():当Activity第一次创建时调用,主要完成一些标准的静态装配,比如创建视图、绑定数据列表等等。

onStart():当Activity呈现给用户变得可见之前调用,此时用户与界面处于可见不可交互的状态。

onResume():当用户与界面可以相互交互前调用,当前的Activity处于栈系统的顶端,用户可以进行相关的操作。

onPause():当系统将要开启一个新的Activity时调用,该方法主要是为了提交一些未保存的数据并更改为持久性数据,停止动画片和中断消耗CPU资源等操作。

onStop():当前Activity完全不可见时调用。出现此状态的原因可能是新的Activity运行并覆盖了当前Activity或者是此Activity面临出栈被销毁的状况。

onRestart():当Activity处于暂停(onPause())或停止(onStop())时调用,调用该方法后该Activity将处于开启状态(onStart())。

onDestroy():当Activity将要被销毁前调用。销毁出现的原因是,Activity执行结束调用finish()方法,Activity面临出栈被系统销毁掉。



下面通过一个实例来具体分析一下Activity的生命周期:

创建两个Activity,AActivity和BActivity,设置AActivity为主Activity;两个Activity分别继承Activity、重写七个回调方法并打上Log记录执行过程。

AActivity的源代码如下:

public class AActivity extends BaseActivity {
//声明控件对象
private TextView mTextView;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Log.i(TAG, "A --> onCreate()");
//根据ID得到代表该控件的对象
mTextView = (TextView)findViewById(R.id.getactivity_id_textview);

/**
* 得到所属栈的ID值
* 当 android:launchMode="singleinstance"时,获取该Activity所在的Task
*/
//mTextView.setText("This AActivity belong Task's ID: "+ this.getTaskId());

/**
* 当 android:launchMode="standard"时,获取该Activity的实例
*/
mTextView.setText("This AActivity Instance :"+ AActivity.this);
}

public void startAactivity(View view){
intentChange(AActivity.class);
}

public void startBactivity(View view){
intentChange(BActivity.class);
}

@Override
protected void onResume() {
// TODO Auto-generated method stub
super.onResume();
Log.i(TAG, "A --> onResume()");
}
@Override
protected void onStart() {
// TODO Auto-generated method stub
super.onStart();
Log.i(TAG, "A --> onStart()");
}

@Override
protected void onRestart() {
// TODO Auto-generated method stub
super.onRestart();
Log.i(TAG, "A --> onRestart()");
}
@Override
protected void onPause() {
// TODO Auto-generated method stub
super.onPause();
Log.i(TAG, "A --> onPause()");
}
@Override
protected void onStop() {
// TODO Auto-generated method stub
super.onStop();
Log.i(TAG, "A --> onStop()");
}
@Override
protected void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
Log.i(TAG, "A --> onDestroy()");
}

@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
// TODO Auto-generated method stub
//获取字符串
String str = savedInstanceState.getString("parameters");
//显示字符串
Toast.makeText(getApplicationContext(), str, Toast.LENGTH_LONG).show();
super.onRestoreInstanceState(savedInstanceState);
Log.i(TAG, "A --> onRestoreInstanceState(Bundle savedInstanceState)");
}

@Override
protected void onSaveInstanceState(Bundle outState) {
// TODO Auto-generated method stub
//保存一个字符串
outState.putString("parameters", "Save somethings");
super.onSaveInstanceState(outState);
Log.i(TAG, "A --> onSaveInstanceState(Bundle outState)");
}
}
BActivity的源代码如下:

public class BActivity extends BaseActivity {
/** Called when the activity is first created. */
//声明控件对象
private TextView mTextView;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.b_main);
Log.i(TAG, "B --> onCreate()");

//根据ID得到代表该控件的对象
mTextView = (TextView)findViewById(R.id.getactivity_id_textview2);
/**
* 得到所属栈的ID值
* 当 android:launchMode="singleinstance"时,获取该Activity所在的Task
*/
//mTextView.setText("This BActivity belong Task's ID: "+ this.getTaskId());
/**
* 当 android:launchMode="standard"时,获取该Activity的实例
*/
mTextView.setText("This BActivity Instance :"+ BActivity.this);
}

public void startAactivity(View view){
intentChange(AActivity.class);
}

public void startCactivity(View view){
intentChange(CActivity.class);
}

@Override
protected void onResume() {
// TODO Auto-generated method stub
super.onResume();
Log.i(TAG, "B --> onResume()");
}
@Override
protected void onStart() {
// TODO Auto-generated method stub
super.onStart();
Log.i(TAG, "B --> onStart()");
}

@Override
protected void onRestart() {
// TODO Auto-generated method stub
super.onRestart();
Log.i(TAG, "B --> onRestart()");
}
@Override
protected void onPause() {
// TODO Auto-generated method stub
super.onPause();
Log.i(TAG, "B --> onPause()");
}
@Override
protected void onStop() {
// TODO Auto-generated method stub
super.onStop();
Log.i(TAG, "B --> onStop()");
}
@Override
protected void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
Log.i(TAG, "B --> onDestroy()");
}

@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onRestoreInstanceState(savedInstanceState);
Log.i(TAG, "B --> onRestoreInstanceState(Bundle savedInstanceState)");
}

@Override
protected void onSaveInstanceState(Bundle outState) {
// TODO Auto-generated method stub
super.onSaveInstanceState(outState);
Log.i(TAG, "B --> onSaveInstanceState(Bundle outState)");
}


(布局文件内容较为简单,在此代码不列出)

当应用程序开始运行,AActivity开启,系统将依次调用onCreate() 、onStart()和onResume()方法,AActivity处于运行状态。



而当开启另一个新的BActivity时,系统调用onPause()方法,AActivity暂停,之后系统将依次调用onCreate() 、onStart()和onResume()方法,BActivity处于运行状态。



随着新的BActivity逐渐覆盖AActivity直至被用户完全不可见,系统调用onStop()方法,AActivity处于停止状态。然后点击模拟器的回退键,BActivity被移出栈系统 ,系统调用onPause()方法,BActivity暂停,之后系统调用依次onRestart()、onStart()、onResume()方法,AActivity重新恢复运行。直到AActivity完全覆盖BActivity,系统将调用onStop()和onDestroy()方法,BActivity停止并被销毁。

再次点击回退键,系统依次调用onPause()、onStop()和onDestroy()方法,AActivity停止并被销毁,页面回到Home Screen界面。



当然,在运行多个应用程序手机内存不够用时,处于onPause()和onStop()状态的Activity有可能被Kill掉使其释放内存供其它资源使用。需要注意的是,被Kill掉的Activity不会调用onDestroy()方法。

3、Activity的状态保存和恢复

当AActivity被BActivity覆盖之后,AActivity会相继调用onPause()和onStop()方法,AActivity将处于后台的停止状态(AActivity对象并未被销毁而是在系统后台内存中保存),然后点击回退,BActivity出栈被销毁,AActivity将调用onRestart()方法重新恢复被覆盖前的状态,一般情况下当AActivity界面中的某些值在被覆盖前定义的某些值(例如在AActivity中Edittext控件输入的内容)依然会保存并获得恢复。这是为什么呢?原因在于在AActivity被覆盖之前,即调用onPause()方法之前,系统会自动调用onSaveInstanceState()方法来保存当前Activity的状态。Google官网列出了以下一张关系图,如图:



分析图可知,当当前Activity被另外一个Activity覆盖之前系统会自动调用onSaveInstanceState()方法用来保存当前Activity的状态,Activity处于不可见的状态之后,一般会有两种情况,第一种是系统可能会遇到内存不足的情况,其它高优先级别的应用程序需要内存使用,所以系统有可能会Kill掉后台处于停止状态的Activity对象,然后用户点击回退,系统会相继调用onCreate()、onStart()、onResume()和onRestoreInstanceState()方法新创建一个Activity,调用onRestoreInstanceState()方法是为了恢复并获取在调用onSaveInstanceState()方法时保存的相关状态;第二种情况就是后台的Activity实例对象完好无损没有被销毁,不需要恢复Activity的状态,用户点击后退,该Activity会调用相继onRestart()、onStart()和onResume()方法,该Activity处于前台。下面将了解几个有用的方法:

onSaveInstanceState():一般两种情况下会调用该方法:

(1)当前Activity跳转到另外一个Activity或点击Home键当前Activity退居后台,该方法将调用;

(2)横竖屏切换时,该方法将调用。

第一种情况调用该方法时,是为了保存当前窗口各个View视图组件的状态,第二种情况横竖屏切换时,首先销毁当前的Activity,然后再重新创建一个,然后调用onRestoreInstanceState()进行数据恢复,调用onSaveInstance()是为了保存一些临时数据。

onRestoreInstanceState():一般也是两种情况下会调用该方法:

(1)当前Activity跳转到另外一个Activity或点击Home键当前Activity退居后台,并且系统内存资源不足,将该Activity对象Kill掉,然后用户点击回到该Activity时,在数据恢复过程中会调用该方法;

(2)横竖屏切换时,该方法会调用。

第一种情况调用该方法时,是为了恢复Activity被销毁前的数据状态,第二种情况上面讲onSaveInstanceState()时已经说明了。

下面来做两个实验,首先去掉上面对onSaveInstance()和onSaveInstanceState()方法的代码注释。

(1)开启应用程序,打印Log如下:



点击跳转,从AActivity跳转到BActivity,打印的Log信息如下:



点击back回退键,从BActivity返回到AActivity,打印的Log信息如下:



由Log信息可知在AActivity被覆盖退居后台之前调用了onSaveInstance()方法,用于保存一些临时数据。

(2)开启应用程序,点击Ctrl+F12,从竖屏切换成横屏,打印的Log信息如下:



显示的视图效果图为:



由Log信息可知横竖屏切换是首先Kill掉当前Activity对象,再重新创建,并在重新创建过程中,在onStart()方法后会自动调用onRestoreInstanceState()方法,用于恢复AActivity视图组件的状态信息。

4、 Activity的 Tasks and Back Stack

任务是由多个Activity组成的业务逻辑,Android利用数据结构栈的方式来管理一个任务中的多个Activity。栈是按照后进先出的原则进行定义的。如图:



应用程序开启时,Back stack 中Activity1创建,系统将Activity1压入Back Stack中,然后开启另一个新的Activity2,系统将Activity2压入Back Stack 中并覆盖了Activity1,Activity1处于停止状态。开启Activity3依然如此,而当点击模拟器的回退按钮(Back)时,系统将Activity3移出Back Stack,Activity3停止并被销毁掉,且Activity3无法再回复到原来的状态。需要注意的是,进程是纵向的,而任务是横向的,因此任务可以是由不同进程间的Activity组成的。

刚才讨论了一个栈中各个Activity之间的进出关系和存储原理,下面将着眼分析Android系统中的多个应用程序的多个栈的管理。启动一个应用程序时,系统建立一个任务栈TaskA,



里面存放有ActivityX和ActivityY,当用户点击Home键回退到Home Screen界面,再开启一个新的应用程序创建任务栈TaskB,里面存放有ActivityY和ActivityZ,此时TaskB成为前端的任务栈,其中的ActivityZ展现在用户的眼前与用户进行相关的交互,而TaskA在后台运行,栈中各个Activity状态保持不变不会被改变。当用户在此点击Home键并切换到TaskA时,TaskA的当前Activity重新恢复可与用户进行交互,TaskB被放在后台了。

在一个栈中可能会有多个相同的Activity在不同的位置存在,设置其是否可以存在可以在AndroidManiFest.xml或Intent中进行设定。



在AndroidManifest.xml中设置对Activity栈的管理方式,需在每个<activity>中设置android:launchMode=""属性值,一般该属性的值分为四种;

Standard:总是允许创建新的相同的Activity(系统默认值);

SingleTop:当栈的最顶端有相同的Activity时,只创建一个,当不同层次有相同的Activity,允许创建新的 Activity;

SingleTask:每开启一个新的Activity都新建一个栈,(一个栈中只允许一个Activity存在);

SingleInstance:只在本栈中存在一个实例;

下面将举几个实例来分析:

(1)Standard:

Standard模式为<activity>默认启动模式,即在栈中不管有没有已存在的Activity的实例,每次都新创建一个实例。接下来做一个实验,为AActivity和BActivity都设置android:launchMode="Standard"属性或不设置(默认),然后多次点击按钮跳转,如下图:



经过分析,Standard模式的Activities在栈中的管理图示:



由上图我们可以看到,使用Standard模式的Activity在栈中不管有没有实例,每次新开启一个Activity时都新建一个实例对象。

(2)SingleTop:

同理,我们将AActivity的启动模式设置为android:launchMode="singleTop",BActivity的启动模式设置为android:launchMode="standard",然后多次点击按钮跳转,效果如下图所示:



经过分析,SingleTop模式的Activities在栈中的管理图示:



由上图我们可以看到,在栈顶的AActivity经过多次创建,但只保持一个实例对象,由此我们可知使用SingleTop模式的Activity在栈中若在栈顶有该Activity实例,则不重新创建,保持原有对象实例。

(3)SingleTask:

接下来设置AActivity的启动模式为android:launchMode="singleTask",BActivity的启动模式设置为android:launchMode="standard",多次点击按钮。效果如下图所示:



依次点击按钮,给各个Activity的生命周期方法调用,如下图所示:



分析周期方法,当开启应用程序,AActivity相继调用onCreate()、onStart()和onResume()方法,显示主AActivity,然后点击AActivity中“Start B Activity”按钮,跳转到BActivity,AActivity相继调用onSaveInstanceState()、onPause()和onStop()方法,BActivity相继调用onCreate()、onStart()和onResume()方法,BActivity覆盖AActivity成为前台Activity,再点击BActivity中“Start
A Activity”按钮,AActivity相继调用onRestart()、onStart()和onResume()方法,而BActivity则调用onPause()、onStop()和onDestory()方法销毁掉了。按常理分析,点击BActivity中“Start A Activity”按钮应该是AActivity重新创建然后BActivity调用onPaue()和onStop()方法退居后台,为什么BActivity会自动销毁掉呢?原因就在于AActivity的栈的开启模式设置为了SingleTask。我们先来看看该应用中Activities在栈中的排列方式:



由上图我们可以看到,使用singleTask为开启模式的Activity在单个栈中只允许一个实例对象存在,如以上例子,首先开启了AActivity和BActivity实例,然后BActivity再点击跳转至AActivity,系统会将BActivity对象实例移出栈,然后恢复第一次创建AActivity对象实例的状态。

(4)SingleInstance:

android:launchMode="singleInstance",BActivity的启动模式设置为android:launchMode="standard",多次点击按钮。效果如下图所示:

(1)开启应用程序,显示主界面,点击“Start B Activity”按钮,跳转至图2:



我接下来分析以上实验中Activities在栈中存储的变化过程和状态,下面列出图示:



结合实验和以上栈的管理图示分析,使用singleInstance开启模式的AActivity在栈中只允许一个实例存在,并且该栈也只能存在一个必须是AActivity的实例对象。注意,使用singleTask和singleInstance为开启模式的Activity很相似,但它们之间是有区别的,举例来讲,当一个Activity使用singleTask时,装载它的Task中只允许一个该实例存在,不过该Task也可以装载其它的Activity实例;当一个Activity使用singleInstance时,装载它的Task中只允许一个该实例存在,并且该Task不允许装载其它的Activity实例,如要从此Activity跳转至其它的Activity,系统会创建新的Task用于装载其它Activity,也就是说使用singleInstance为开启模式的Activity(假设为AActivity)只属于一个Task,且该Task也总共只包含一个Activity实例,就是AActivity。

<activity>中其它几个与Task相关的常用属性介绍:

(翻译自Android开发者官网,英文好的读者可以直接阅读官网)
1、android:taskAffinity:

Activity为Task拥有的一个affinity。拥有相同的affinity的Activity理论上属于相同的Task(在用户的角度是相同的“应用程序”)。Task的affinity是由它的根Activity决定的。affinity决定两件事情——Activity重新宿主的Task(参考allowTaskReparenting特性)和使用FLAG_ACTIVITY_NEW_TASK标志启动的Activity宿主的Task。默认情况,一个应用程序中的所有Activity都拥有相同的affinity。也可以设定这个特性来重组它们,甚至可以把不同应用程序中定义的Activity放置到相同的Task中。为了明确Activity不宿主特定的Task,设定该特性为空的字符串。如果这个特性没有设置,Activity将从应用程序的设定那里继承下来(参考<application>元素的taskAffinity特性)。应用程序默认的affinity的名字是<manifest>元素中设定的package名。

2、android:allowTaskReparenting:

该属性是用来标记一个Activity实例的应用在退居台后,能否从启动它的Task移动到具有共同affinity的Task中,设置属性"true"可以移动,设置"false"不可以移动。该方法一般在不同应用的Activity中使用,比如在使用一个邮件App时,需要启动一个浏览器App的主Activity,启动完成后,浏览器App主Activity是属于邮件App的Task中的。若为浏览主Activity设置android:allowTaskReparenting,当将邮件App退居后台,再在主菜单开启邮件App,会发现浏览器App主Activity已经不在Task中,然后再在主菜单开启浏览器App,会发现主Activity是刚刚用邮件App开启的那个Activity实例。由此我们可知,在邮件App中开启的浏览器主Activity移动到了浏览器App的Task中,下面用图来直观地分析,如下图示:



上图形象地描述了设置了android:allowTaskReparenting属性的BrowserMainActivity在邮件App退居后台并再次开启后,系统自动将其移动到了具有共同affinity的Browser Task中。

3、android:alwaysRetainTaskState:

该属性是用来标记Activity所在的Task的状态是否总是由系统来保持。“true”,表示总是;“false”,表示在某种情形下允许系统恢复Task到它的初始化状态。默认值是“false”。这个特性只针对Task的根Activity有意义;对其它Activity来说,忽略之。一般来说,特定的情形如当用户从主画面重新选择这个Task时,系统会对这个Task进行清理(从stack中删除位于根Activity之上的所有Activivity)。典型的情况,当用户有一段时间没有访问这个Task时也会这么做,例如30分钟。然而,当这个特性设为“true”时,用户总是能回到这个Task的最新状态,无论他们是如何启动的。这非常有用,例如,像Browser应用程序,这里有很多的状态(例如多个打开的Tab),用户不想丢失这些状态。

4、android:clearTaskOnLaunch

该属性是用来标记是否从Task中清除所有的Activity,除了根Activity外(每当从主画面重新启动时)“true”,表示总是清除至它的根Activity,“false”表示不。默认值是“false”。这个特性只对启动一个新的Task的Activity(根Activity)有意义;对Task中其它的Activity忽略。当这个值为“true”,每次用户重新启动这个Task时,都会进入到它的根Activity中,不管这个Task最后在做些什么,也不管用户是使用BACK还是HOME离开的。当这个值为“false”时,可能会在一些情形下(参考alwaysRetainTaskState特性)清除Task的Activity,但不总是。假设,某人从主画面启动了Activity
P,并从那里迁移至Activity Q。接下来用户按下HOME,然后返回Activity P。一般,用户可能见到的是Activity Q,因为它是P的Task中最后工作的内容。然而,如果P设定这个特性为“true”,当用户按下HOME并使这个Task再次进入前台时,其上的所有的Activity(在这里是Q)都将被清除。因此,当返回到这个Task时,用户只能看到P。

如果这个特性和allowTaskReparenting都设定为“true”,那些能重新宿主的Activity会移动到共享affinity的Task中;剩下的Activity都将被抛弃,如上所述。

5、android:finishOnTaskLaunch

该属性用来标记当用户再次启动它的Task(在主画面选择这个Task)时已经存在的Activity实例是否要关闭(结束)“true”,表示应该关闭,“false”表示不关闭。默认值是“false”。如果这个特性和allowTaskReparenting都设定为“true”,这个特性胜出。Activity的affinity忽略。这个Activity不会重新宿主,但是会销毁。

6、android:noHistory

用于标记当用户从Activity上离开并且它在屏幕上不再可见时Activity是否从Activity stack中清除并结束(调用finish()方法)——“true”,表示它应该关闭,“false”,表示不需要。默认值是“false”。“true”值意味着Activity不会留下历史痕迹。因为它不会在Activity stack的Task中保留,因此,用户不能返回它。比如启用界面的就可以借用这个。

源代码下载,请戳这里!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: