项目5理解App的活动
5.1项目目标: 理解App的活动机制与状态
本项目的内容是创建和设计App必备的基础理论知识。任何一个App都像人的生命一样,有开始、运行、暂停和消亡,在这个过程中,可以用各种方法对程序进行控制。本项目设定的目标为掌握Activity的状态、生命周期及相关属性,熟练编写代码实现创建Activity,设置Activity以及启动、关闭Activity等操作。
5.2项目准备
Android具有四大组件,分别为Activity、Service(服务)、Content Provider(内容提供)、BroadcastReceiver(广播接收器)。Activity是Android组件中最基本也是最常用的四大组件之一,其本质是一个应用程序组件,专门为人机交互提供可视化屏幕、交互接口,并完成交互任务。Activity可以通过setContentView(View)来显示指定控件。在一个Android应用中,一个Activity通常就是一个单独的屏幕,它上面可以显示一些控件,也可以监听并对用户的事件做出响应。Activity之间通过Intent进行通信。
5.2.1介绍Activity的状态
在Android中,Activity拥有以下4种基本状态。
(1) Active/Running(运行状态): 一个新Activity启动入栈后,它在屏幕最前端,处于栈的最顶端,此时它处于可见并可与用户交互的激活状态。
(2) Paused(暂停状态):  当Activity被另一个透明或者Dialog样式的Activity覆盖时,处于Paused状态。此时它依然与窗口管理器保持连接,系统继续维护其内部状态,所以它仍然可见,但它已经失去了焦点,故不可与用户交互。
(3) Stopped(停止状态): 当Activity被另外一个Activity覆盖、失去焦点并不可见时,处于Stopped状态。
(4) Killed(死亡状态): Activity被系统杀死回收或者没有被启动时,处于Killed状态。
当一个Activity实例被创建、销毁或者启动另外一个Activity时,它在这4种状态之间进行转换,这种转换的发生依赖于用户程序的动作。图51说明了Activity在不同状态之间转换的时机和条件。


图51Activity的4种状态的转换


值得注意的是,开发Android的程序员开启一个 Activity,却不能够通过Activity.finish()方法结束它,仅能使其被回收,即从Active/Running状态转到Paused状态。


5.2.2介绍Activity的生命周期
根据官方文档对Activity生命周期的说明(如图52所示),可以对任一款App的生命周期活动进行描述。首先,在第一次打开某款App时,当前的FirstActivity即被执行,随后Android系统依次调用onCreate()方法、onStart()方法、onResume()方法,使得FirstActivity完全被启动,并显示到前台。如果Intent同时启动了SecondActivity,则会通过onPause()方法暂停之前的FirstActivity。如果又选择了Back键,则会通过onResume()方法重新返回到FirstActivity,而SecondActivity则被onStop()方法所销毁。如果想继续SecondActivity,则需要通过onRestart()方法来实现。如果内存不够,或者结束了Activity,那么就会通过onDestroy()方法结束整个Activity,必须重新创建才能重新启动该App。


图52Activity生命周期循环图



对于Activity生命周期而言,有7个方法能够帮助Activity进入循环流程。这7个方法分别为onCreate()、onRestart()、onStart()、onResume()、onPause()、onStop()和onDestroy()。其具体的使用情境、详细说明以及方法之间的承接方法如表51所示。



表51Activity生命周期方法及使用说明




生命周期所用方法使 用 情 境详 细 说 明承 接 方 法

onCreate()

首次创建Activity时调用
用户应该在此方法中执行所有正常的静态设置,例如创建视图、将数据绑定到列表等。 系统向此方法传递一个Bundle对象,其中包含Activity的上一状态,不过前提是捕获了该状态
始终后接onStart()

onRestart()

在Activity已停止并即将再次启动前调用Activity被重新激活时,就会调用onRestart方法

始终后接onStart()

onStart()

在Activity即将对用户可见之前调用
onStart()是Activity界面被显示出来的时候执行的

如果Activity转入前台,则后接onResume(); 如果Activity转入隐藏状态,则后接onStop()

onResume()

在Activity即将开始与用户进行交互之前调用
此时,Activity处于Activity堆栈的顶层,并具有用户输入焦点
始终后接onPause()
onPause()

当系统即将开始继续另一个Activity时调用
此方法通常用于确认对持久性数据的未保存更改、停止动画以及其他可能消耗CPU的内容,诸如此类。 它应该非常迅速地执行所需操作,因为它返回后,下一个Activity才能继续执行

如果Activity返回前台,则后接onResume(); 如果Activity转入对用户不可见状态,则后接onStop(); 如果它在后台仍然可见,则不会停止

onStop()

在Activity对用户不再可见时调用
如果Activity被销毁,或另一个Activity(一个现有Activity或新Activity)继续执行并将其覆盖,就可能发生这种情况

如果Activity恢复与用户的交互,则后接onRestart(); 如果Activity被销毁,则后接onDestroy()

onDestroy()

在Activity被销毁前调用
这是Activity将收到的最后调用。 当Activity结束(有人对Activity调用了finish()),或系统为节省空间而暂时销毁该Activity实例时,可能会调用它。 用户可以通过isFinishing()方法区分这两种情形此方法为生命周期的结束方法


根据Activity官方文档说明,Activity的生命周期有3个重要的循环对。
(1) 整个生命周期: 从调用onCreate()方法直到调用onDestroy()方法的整个过程。Activity需要通过onCreate()方法准备启动全局状态,再通过onDestroy()方法释放所有的资源。例如,当一个线程在后台通过网络下载数据时,则通过onCreate()方法创建线程,再通过onDestroy()方法停止线程。
(2) 可见生命周期: 从调用onStart()方法到调用onStop()方法之间的过程。之所以称之为可见生命周期,是因为用户可以直观地在屏幕上看到这个过程,并且能够保持需要展示给用户的资源。例如,通过调用onStart()方法注册一个BroadcastReceiver,用于监视使UI产生变化的内容,再通过调用onStop()方法取消监视。onStart()方法和onStop()方法可以在被用户可见和隐藏两种方式切换的时候被多次调用。
(3) 前台生命周期: 从调用onResume()方法到调用onPause()方法之间的过程。在此过程中,Activity通过前台与用户产生交互。Activity从onResume()到onPause()进行了十分频繁的切换。例如,当设备处于休眠状态时,将调用onPause()方法; 当result和一个新的Intent发送给Activity时,将调用onResume()方法。
5.2.3介绍Activity的属性
Activity作为Android的对象,需要通过各种属性的设置来实现。Activity的属性类型多样,表52列出了相关属性及其描述和调用方法。


表52Activity的属性详表




属性描述调 用 方 法

android: allowTaskReparenting
是否允许Activity更换从属的任务,例如从短信息任务切换到浏览器任务
android: allowTaskReparenting=["true"|"false"]

android: alwaysRetainTaskState
是否保留状态不变,例如切换回home,再重新打开,Activity处于最后的状态
android: alwaysRetainTaskState=["true"|"false"]
android: clearTaskOnLaunch
例如P是Activity,Q是被P触发的Activity,然后返回Home,重新启动P,是否显示Q
android: clearTaskOnLaunch=["true"|"false"]


续表


属性描述调 用 方 法

android: configChanges
当配置List发生修改时,是否调用onConfigurationChanged()方法,例如"locale|navigation|orientation"
android: configChanges=[oneormoreof: "mcc""mnc""locale""touchscreen""keyboard""keyboardHidden""navigation""orientation""fontScale"]

android: enabled
Activity是否可以被实例化
android: enabled=["true"|"false"]
android: excludeFromRecents
是否可被显示在最近打开的Activity列表里
android: excludeFromRecents=["true"|"false"]
android: exported
是否允许Activity被其他程序调用
android: exported=["true"|"false"]
android: finishOnTaskLaunch
是否关闭已打开的Activity(当用户重新启动这个任务的时候)
android: finishOnTaskLaunch=["true"|"false"]
android: icon
调用图标
android: icon="drawableresource"
android: label
调用标签
android: label="stringresource"
android: launchMode
Activity启动方式: 

"standard""singleTop""singleTask""singleInstance"

其中前两个为一组,后两个为一组
android: launchMode=["multiple"|"singleTop"|"singleTask"|"singleInstance"]
android: multiprocess
可以多实例
android: multiprocess=["true"|"false"]
android: name
采用类名的简写方式,查看文档类名的简写格式
android: name="string"
android: noHistory
是否需要移除这个Activity(当用户切换到其他屏幕时)。这个属性是在APIlevel3中引入的
android: noHistory=["true"|"false"]
android: permission
权限与安全机制解析
android: permission="string"
android: process
一个Activity运行时所在的进程名,所有程序组件运行在应用程序默认的进程中,这个进程名跟应用程序的包名一致
android: process="string"

android: screenOrientation
Activity显示的模式,"unspecified"为默认值; "landscape"为风景画模式,宽度比高度大一些; "portrait"为肖像模式,高度比宽度大; "user"为用户的设置; 另外还有"behind""sensor""nonsensor"
android: screenOrientation=["unspecified"|"user"|"behind"|"landscape"|"portrait"|"sensor"|"nonsensor"]


续表


属性描述调 用 方 法

android: stateNotNeeded
是否Activity被销毁和成功重启并不保存状态
android: stateNotNeeded=["true"|"false"]
android: taskAffinity
Activity的亲属关系,默认同一个应用程序下的Activity有相同的关系
android: taskAffinity="string"
android: theme
Activity的样式主题,如果没有设置,则Activity的主题样式从属于应用程序,请参见<application>元素的theme属性
android: theme="resourceortheme"
android: windowSoftInputMode
Activity主窗口与软键盘的交互模式,自APIlevel3被引入
android: windowSoftInputMode=[oneormoreof: "stateUnspecified""stateUnchanged""stateHidden""stateAlwaysHidden""stateVisible""stateAlwaysVisible""adjustUnspecified""adjustResize""adjustPan"]>
5.3项目运行
前面对Activity生命周期进行了讲解,本节将通过一个实例让大家生动地体验项目的生命周期是如何进行的,即如何在App项目中创建Activity、设置Activity,以及启动Activity、关闭Activity。
在Android开发中,在src文件夹下会自动生成一个MainActivity.java文件,其中实现了对Activity的定义及onCreate()方法的调用,具体代码如下: 

----------------------------------------------------------------------

public class MainActivity extends ActionBarActivity {



@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

}

通过调用onCreate()方法实现savedInstanceState,即每次启动一个Activity后,能够保持在一个界面内容不变,直到启动另一个Activity才能修改界面内容视图。
5.3.1创建新的Activity
【例5.1】创建一个新的Activity,并通过代码实现新建Activity的设置、启动。
除了新建项目时自带的Activity以外,还需要另外新建其他Activity,以实现其他界面内容的布局、调用与交互。为了定义新创建的Activity,并在生命周期进行运转时告知Android系统,还需在本项目根目录下的AndroidManifest.xml文件中进行标注,具体代码如下: 

---------------------------------------------------------------------

 <application

android:label="@string/app_name" >

<activity

android:label="@string/app_name"

android:name=".MainActivity" >

<intent-filter >

<action android:name="android.intent.action.MAIN" />



<category android:name="android.intent.category.LAUNCHER" />

</intent-filter>

</activity>


<activity 

android:name=".NewActivity"

android:label="新建Activity"

>

</activity>

</application>


将新建的Activity定义为NewActivity的名字后,继续通过Android Studio创建NewActivity活动。具体过程如下: 
步骤1: 右击layout文件夹,选择New→Activity→Basic Activity命令,如图53所示。
步骤2: 在弹出的New Android Activity对话框中填写新建Activity的相关配置,包括Activity Name、Layout Name、Title、Package Name等信息,如图54所示。




图53新建Activity




图54填写新建Activity的相关配置


由Android Studio自动生成的代码如下: 

----------------------------------------------------------------------

package com.mingrisoft;



import android.app.Activity;

import android.os.Bundle;



public class NewActivity extends Activity {



@Override

protected void onCreate(Bundle savedInstanceState) {

// TODO Auto-generated method stub

super.onCreate(savedInstanceState);

}



}
步骤3: 为了使新建的NewActivity运行时有新的界面可以调用,需要为NewActivity新建一个布局文件,命名为newactivity.xml,如图55所示。然后在NewActivity.java文件中添加一句脚本,内容为调用setContentView()方法为NewActivity指定布局文件newactivity.xml,具体代码如下: 


图55在项目中创建新活动与新布局的结构



-----------------------------------------------------

package com.mingrisoft;



import android.app.Activity;

import android.os.Bundle;



public class NewActivity extends Activity {



@Override

protected void onCreate(Bundle savedInstanceState) {

// TODO Auto-generated method stub

super.onCreate(savedInstanceState);


setContentView(R.layout.newactivity);

}



}

5.3.2为新建Activity设置属性
为了避免新建Activity启动时抛出异常,需要在AndroidManifest.xml文件中对NewActivity进行属性设置,具体代码如下: 


----------------------------------------------------------------------

<manifest xmlns:android="http://schemas.android.com/apk/res/android"

package="com.mingrisoft"

android:versionCode="1"

android:versionName="1.0" >



<uses-sdk android:minSdkVersion="15" />



<application

android:icon="@drawable/ic_launcher"

android:label="@string/app_name" >

<activity

android:label="@string/app_name"

android:name=".MainActivity" >

<intent-filter >

<action android:name="android.intent.action.MAIN" />



<category android:name="android.intent.category.LAUNCHER" />

</intent-filter>

</activity>




<activity 

android:icon="@drawable/ic_launcher"

android:name=".NewActivity"

android:label="新建Activity"

android:launchMode="singleTask"

android:screenOrientation="portrait"

android:windowSoftInputMode="stateHidden" 

>

</activity>

</application>



</manifest>
5.3.3启动Activity
在本项目中不止一个Activity,因此在启动Activity时需要调用startActivity()方法,其语法格式为: 



Public void startActivity(Intent newintent)



其中,Intent用于Activity之间的数据传递,每个Intent都要与一个Activity相对应。
该方法应编写于layout文件夹下新建的newactivity.xml文件中,具体代码如下: 

----------------------------------------------------------------------

 <?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:orientation="vertical" >


<TextView

android:id="@+id/textView1"

android:layout_width="wrap_content"

android:layout_height="wrap_content"



/>

Intent intent =new Intent(MainActivity.this,ToStartActivity.class); 

startActiviity(newintent);

</LinearLayout>
5.3.4关闭Activity
关闭Activity的方法较为简单,如果只有一个Activity,只需调用finish()方法即可,其语法格式如下: 



Public void finish() 



但如果有多个Activity,则需要调用finishActivity()方法来指定关闭Activity对象,SDK的官方说明文档如下: 





----------------------------------------------------------------------

package com.mingrisoft;



import android.app.Activity;

import android.os.Bundle;



public class NewActivity extends Activity {



@Override

protected void onCreate(Bundle savedInstanceState) {

// TODO Auto-generated method stub

super.onCreate(savedInstanceState);

setContentView(R.layout.newactivity);

Button btn1 = (Button)findViewById(R.id.btn1);

btn1.setOnClickListener(new OnClickListener() {



@Override

public void onClick(View v) {

ActivityA.this.finishActivity(1);



}

});

}
5.4项目结案
本项目通过实例的方式从Activity(活动类)的4种基本状态谈起,再梳理Activity的生命周期,并在此基础上演示了如何创建新的Activity、设置Activity以及控制Activity状态等操作。总结本项目内容,需要大家对以下3个部分有较为深刻的理解: 
(1) 创建新Activity的流程。
(2) 启动Activity与关闭Activity的方法。
(3) Activity的生命周期运转方式。
5.5项目练习
1. 开发一款App,并在其中设置两个Activity,调用startActivity()方法实现两个Activity的启动。
2. 开发一款App,并在其中创建3个Activity,调用finishActivity()方法实现依次结束3个Activity。
3. 请用一款App的程序代码,对照Activity生命周期示意图,详细解析App的生命周期运转情况。