第四章 Android生命周期

参考资料:
《Android应用程序开发》ISBN 9787302283164
参考软件:
Android Studio、Eclipse+ADT、Android SDK、JDK
程序生命周期
Android程序的生命周期是由系统控制而非程序自身直接控制。
Android根据应用程序的组件以及组件当前运行状态将所有的进程按重要性程度从高到低划分了五个优先级:前台进程、可见进程、服务进程、后台进程、空进程。

*图取自网络,Android应用生命周期
前台进程
前台进程是显示在屏幕最前端并与用户正在交互的进程,是Android系统中最重要的进程,包含以下四种情况:
进程中的Activity正在与用户进行交互
进程服务被Activity调用,而且这个Activity正在与用户进行交互
进程服务正在执行声明周期中的回调方法,如onCreate()、onStart()或onDestroy()
进程的BroadcastReceiver正在执行onReceive()方法
Android系统在多个前台进程同时运行时,可能会出现资源不足的情况,此时会清除部分前台进程,保证主要的用户界面能够及时响应。
可见进程
可见进程指部分程序界面能够被用户看见,却不在前台与用户交互,不响应界面事件(其onPause()方法已被调用)的进程。如果一个进程包含服务,且这个服务正在被用户可见的Activity调用,此进程同样被视为可见进程。
Android系统一般存在少量的可见进程,只有在特殊的情况下,Android系统才会为保证前台进程的资源而清除可见进程。
服务进程
服务进程是指包含由startService()方法启动服务的进程。它有以下特性:没有用户界面;在后台长期运行。例如,后台MP3播放器或后台上传下载数据的网络服务。
Android系统除非不能保证前台进程或可见进程所必要的资源,否则不强行清除服务进程
后台进程
后台进程是指不包含任何已经启动的服务,而且没有任何用户可见的Activity的进程。这些进程不直接影响用户的体验。
Android系统中一般存在数量较多的后台进程,因些这些进程会被保存在一个列表中,以保证在系统资源紧张时,系统将优先清除用户较长时间没有见到的后台进程。
空进程
空进程是不包含任何活跃组件的进程。一般保留这些进程,是为了将其做为一个缓存,在它所属的应用的组件下一次需要时,缩短启动的时间
空进程在系统资源紧张时会被首先清除,但为了提高Android系统应用程序的启动速度,Android系统会将空进程保存在系统内存中,在用户重新启动该程序时,空进程会被重新使用。
Android组件
Android系统有四个重要的组件,分别是Activity、Service、BroadcaseReceiver和ContentProvider。
Activity是Android程序的呈现层,显示可视化的用户界面,并接收与用户交互所产生的界面事件。在界面上的呈现形式就是全屏窗体、非全屏悬浮窗体的对话框,与在桌面系统上的独立事业,如办公应用等类似。Activities是可执行的代码块,由用户或者操作系统来进行初始实例化,并在他们被需求时致以运行。Activities可以与用户、请求数据或者其他activity、service的服务通过query或intent进行交互。大部分为Android编写的可执行代码将以Activity的形式执行。对于一个Android应用程序来说,可以包含一个或多个Activity,一般在程序启动后会呈现一个Activity,用于提示用户程序已经正常启动。当它不积极运行时,Activity可以被操作系统终止以节省内存。
Service用于没有用户界面,但需要长时间在后台运行的应用。它类似于桌面应用或者服务器操作系统上的服务或守护进程。Service是在后台运行的可执行的代码块,从它被初始化一直运行到该程序关闭。一个Service的典型的例子是一个MP3播放器,尽管在用户已经使用其他应用程序,但仍然需要持续播放文件。您的应用程序可能需要在没有用户界面的情况下一直执行Service来实现后台任务。
Broadcast and Intent Receivers对从其他的应用程序的服务请求做出一个全系统广播的响应,这些广播响应可能来自于Android系统本身或者是任何在其系统上运行的程序。BroadcaseReceiver是用来接受并响应广播消息的组件。它不包含任何用户界面,但可以通过启动Activity或者Notification通知用户接收到重要信息。

Activity或Service通过执行一个IntentReceiver为其他应用程序提供了访问其功能的功能。Intent Receiver是一段可执行代码块,对其他activity的数据或服务请求做出响应。请求的activity(即客户端)生成一个Intent,把其添加至Android Framework中,来指出哪些应用程序(即目标程序)接受并对其做出响应。Intent是Android的主要构成元素之一,它从现有的应用程序中创造新的应用程序。Intent实现了应用程序和其他的应用程序和服务交换所需信息的功能。
ContentProvider是Android系统提供的一种标准的共享数据的机制,应用程序可以通过ContentProvider访问其他应用程序的私有数据(私有数据可以是存储在文件系统中的文件,也可以是SQLite中的数据库)。Android系统内部也提供一些内置的ContentProvider,能够为应用程序提供重要的数据信息。
所有Android组件都具有自己的生命周期,是从组件建立到组件销毁的整个过程。在生命周期中,组件会在可见、不可见、活动、非活动等状态中不断变化。
Activity简介
在Android系统中,用户与程序的交互是通过Activity完成的。Activity是Android应用程序的四大组件之一,它负责管理Android应用程序的界面。一个应用程序一般会包含若干个Activity,每一个Activity组件负责一个界面的展现。
下面列举几个Activity的常用事件,具体如下:
• onKeyDown(int keyCode,KeyEvent event):对应按键按下事件
• onKeyUp(int keyCode,KeyEvent event):对应按键松开事件
• onTouchEvent(MotionEvent event):对应点击屏幕事件
然后在MainActivity中重写上述方法,运行程序进行测试。
示例代码:Android触屏控制
package com.tmsoft.ch31;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.MotionEvent;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
//手指按下的点为(x1, y1)手指离开屏幕的点为(x2, y2)
float x1 = 0;
float x2 = 0;
float y1 = 0;
float y2 = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
//继承了Activity的onTouchEvent方法,直接监听点击事件
if(event.getAction() == MotionEvent.ACTION_DOWN) {
//当手指按下的时候
x1 = event.getX();
y1 = event.getY();
}
if(event.getAction() == MotionEvent.ACTION_UP) {
//当手指离开的时候
x2 = event.getX();
y2 = event.getY();
if(y1 - y2 > 50) {
Toast.makeText(MainActivity.this, "向上滑", Toast.LENGTH_SHORT).show();
} else if(y2 - y1 > 50) {
Toast.makeText(MainActivity.this, "向下滑", Toast.LENGTH_SHORT).show();
} else if(x1 - x2 > 50) {
Toast.makeText(MainActivity.this, "向左滑", Toast.LENGTH_SHORT).show();
} else if(x2 - x1 > 50) {
Toast.makeText(MainActivity.this, "向右滑", Toast.LENGTH_SHORT).show();
}
}
return super.onTouchEvent(event);
}
}
Activity生命周期
Activity生命周期指Activity从启动到销毁的过程。Activity表现为四种状态,分别是活动状态、暂停状态、停止状态和非活动状态。
活动状态,Activity在用户界面中处于最上层,完全能被用户看到,能够与用户进行交互
暂停状态,Activity在界面上被部分遮挡,该Activity不再处于用户界面的最上层,且不能够与用户进行交互;或者屏幕被锁定。
停止状态,Activity在界面上完全不能被用户看到,也就是说这个Activity被其他Activity全部遮挡
非活动状态不在以上三种状态中的Activity则处于非活动状态
这四种状态是可以相互转换的。变换关系图如下:

*图取自网络,Activity的四种状态的变换关系图
下面这个图解释了状态之间转化的可能路径。其中着色的椭圆表示活动的主要状态,矩形表示当活动在状态之间转换时会被调用的回调方法。

*图取自网络,Activity活动周期

Android调用以下的事件回调方法通知activity从某一状态转变到另一状态:
代码清单:事件的回调方法
public class MyActivity extends Activity {
protected void onCreate(Bundle savedInstanceState);
//Activity启动后第一个被调用的方法,常用来进行Activity的初始化,例如创建View、绑定数据或恢复信息等。该方法不可终止。
protected void onStart();
//当Activity显示在屏幕上时,该方法被调用。该方法不可终止。
protected void onRestart();//当Activity从停止状态进入活动状态前,调用该方法。该方法不可终止。
protected void onResume();//当Activity能够与用户交互,接受用户输入时,该方法被调用。此时的Activity位于Activity栈的栈顶。该方法不可终止。
protected void onPause();//当Activity进入暂停状态时,该方法被调用。一般用来保存持久的数据或释放占用的资源。该方法可终止。
protected void onStop();//当Activity进入停止状态时,该方法被调用。该方法可终止。
protected void onDestroy();//在Activity被终止前,即进入非活动状态前,该方法被调用。该方法可终止。
}

*图取自网络Activity事件回调方法的调用顺序
*Activity的生命周期可分为全生命周期、可视生命周期和活动生命周期。每种生命周期中包含不同的事件回调方法。
全生命周期
全生命周期是从Activity建立到销毁的全部过程,始于onCreate(),结束于onDestroy()。
使用者通常在onCreate()中初始化用户界面,分配引用类变量,绑定数据控件,并创建服务和线程等Activity所能使用的全局资源和状态,并在onDestroy()中释放这些资源,并确保所有外部连接被关闭,例如网络或数据库的联系等;在一些极端的情况下,Android系统会不调用onDestroy()方法,而直接终止进程。
为了避免创造短期对象和增加垃圾收集的时间,以致对用户体验产生直接影响。如果你的Activity需要创建一些对象的话,最好在onCreate方法中创建,因为它仅调用一次在一个Actvity的完整生命周期中。
可视生命周期
可视生命周期是Activity在界面上从可见到不可见的过程,开始于onStart(),结束于onStop()。
onStart()一般用来初始化或启动与更新界面相关的资源
onStop()一般用来暂停或停止一切与更新用户界面相关的线程、计时器和服务
onRestart()方法在onSart()前被调用,用来在Activity从不可见变为可见的过程中,进行一些特定的处理过程
onStart()和onStop()会被多次调用
nStart()和onStop()也经常被用来注册和注销BroadcastReceiver或者传感器
在onStart()和onStop()这两个方法中间, Actvity将会对用户是可见的,尽管它可能部分被遮挡着。在一个Activity完整的生命周期中可能会经过几个Activity可见的生命周期,因为你的Activity可能会经常在前台和后台之间切换。在极端情况下,系统将销毁掉一个Activity即使它在可见状态并且并不调用onStop方法。
活动生命周期
活动生命周期是Activity在屏幕的最上层,并能够与用户交互的阶段,开始于onResume(),结束于onPause()。在Activity的状态变换过程中onResume()和onPause( )经常被调用,因此这两个方法中应使用更为简单、高效的代码。
onPause()是第一个被标识为"可终止"的方法
在onPause()返回后,onStop()和onDestroy()随时能被Android系统调用
onPause()常用来保存持久数据,如界面上的用户的输入信息等
当系统而不是用户关闭一个活动来节省内存时,用户可能希望返回到活动且是它之前的状态。为了获得活动被杀之前的状态,你可以执行活动的onSaveInstanceState()方法。Android在活动容易被销毁前调用这个方法,也就是调用onPause()之前。该方法的参数是一个Bundle对象,在这个对象你可以以名值对记录活动的动态状态。当活动再次启动时,Bundle同时被传递到onCreate()和调用onCreate()之后的方法onRestoreInstanceState()。
因为onSaveInstanceState()方法不总是会被调用,你应该仅使用onSaveInstanceState()它来记录活动的临时状态,而不是持久的数据。应该使用onPause()来存储持久数据。
横竖屏切换时的生命周期
1、当手机横竖屏切换时,Activity会销毁重建(模拟器中横竖屏切换可以使用ctrl+F11)。
2、如果不希望在横竖屏切换时Activity被销毁重建,可以在AndroidManifest.xml文件中设置Activity的android:configChanges的属性,具体代码如下所示:
android:configChanges="orientation|keyboardHidden|screenSize“
3、如果希望某一个界面一直处于竖屏或者横屏状态,可以在清单文件中通过设置Activity的属性来完成,具体代码如下所示:
竖屏:android:screenOrientation="portrait"
横屏:android: screenOrientation="landscape"
设置View的点击事件监听
Android程序中,设置View的点击事件监听共有四种,分别是:
1)在布局文件中为控件设置onClick属性指定点击方法;
2)创建一个内部类实现OnClickListener接口并重写onClick()方法,
之后需要为控件设置setOnClickListener(Listener listener);
3)主类中实现OnclickListener接口,然后重写onClick()方法;
4)创建匿名内部类,即在为控件设置监听时直接创建一个
OnClickListener实例,不为该实例指定名称。
Activity的四种启动模式
一、Android中的任务栈
Android系统采用任务栈(Task)的方式来管理Activity的实例,当启动一个应用时,Android就会为之创建一个任务栈。先启动的Activity压在栈底,后启动的Activity放在栈顶,通过启动模式可以控制Activity在任务栈中的加载方式。
Android系统中的任务栈,类似于一个容器,用于管理所有的Activity实例。在存放Activity时,满足“先进后出(First-In/Last-Out)”的原则。

二、Activity的四种启动模式
Activity的启动模式有四种,分别是standard、singleTop、singleTask和singleInstance。在AndroidManifest.xml中,通过<activity>标签的android:launchMode属性可以设置启动模式。
1) standard标准模式
standard是Activity默认的启动模式。在standard模式下,每当启动一个新的Activity,它就会进入任务栈,并处于栈顶的位置,对于使用standard模式的Activity,系统不会判断该Activity在栈中是否存在,每次启动都会创建一个新的实例。

2) singleTop启动模式
singleTop启动模式与standard类似,不同的是,当启动的Activity已经位于栈顶时,则直接使用它不创建新的实例。如果启动的Activity没有位于栈顶时,则创建一个新的实例位于栈顶。

3) singleTask模式
如果希望Activity在整个应用程序中只存在一个实例,可以使用singleTask模式,当Activity的启动模式指定为singleTask,每次启动该Activity时,系统首先会检查栈中是否存在该活动的实例,如果发现已经存在则直接使用该实例,并将当前Activity之上的所有Activity出栈,如果没有发现则创建一个新的实例。

4)singleInstance模式
在程序开发中,如果需要Activity在整个系统中都只有一个实例,这时就需要用到singleInstance模式,不同于上述三种模式,指定为singleInstance模式的Activity会启动一个新的任务栈来管理这个Activity。
singleInstance模式加载Activity时,无论从哪个任务栈中启动该Activity,只会创建一个Activity实例,并且会使用一个全新的任务栈来装载该Activity实例。采用这种模式启动Activity会分为以下两种情况,具体如下:
第一种:如果要启动的Activity不存在,系统会先创建一个新的任务栈,再创建该Activity的实例,并把该Activity加入栈顶。

第二种:如果要启动的Activity已经存在,无论位于哪个应用程序或者哪个任务栈中。系统都会把该Activity所在的任务栈转到前台,从而使该Activity显示出来。
多窗口切换例子:
在该例中涉及到Fx_Main、Activity2以及Activity3三个Activity。
首先,介绍一下例子中涉及到的三个Activity及其界面:
首先是Fx_Main.Activity:

Fx_Main的界面
由上图所示的界面中,点击"跳转到AC2"按钮之后,跳转至Activity2。具体代码如下:
代码清单4-3. Fx_Main.Activity
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
public class Fx_Main extends Activity {
/** Called when the activity is first created. */
private Button b;
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
TextView tv=(TextView)findViewById(R.id.TextView01);
tv.setText("Main---->"+getTaskId());
Log.i("System.out", "Main---->"+this.toString()+"Task ID---->"+getTaskId());
b=(Button)findViewById(R.id.Button01);
b.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
Intent i=new Intent(Fx_Main.this,Activity2.class);
startActivity(i);
}});
}
@Override
protected void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
Log.i("System.out", "Fx_Main--->Destory");
}
}其次为Activity2,其界面如下:

Activity2的界面
在该界面中,点击"跳回到Main"按钮,则跳转至Fx_Main,而点击"跳转本页面"则仍显示Activity2的界面,点击"跳到AC3"则跳转到Activity3.具体代码如下:
代码清单4-4. Activity2.Activity
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
public class Activity2 extends Activity {
private Button b;
private Button b2;
private Button b3;
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.activity2);
b=(Button)findViewById(R.id.Button02);
b2=(Button)findViewById(R.id.Button03);
b3=(Button)findViewById(R.id.Button04);
TextView tv=(TextView)findViewById(R.id.TextView02);
tv.setText("Ac2---->"+getTaskId());
Log.i("System.out", "Ac2---->"+this.toString()+"Task ID---->"+getTaskId());
b.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
Intent i=new Intent(Activity2.this,Fx_Main.class);
startActivity(i);
}});
b2.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
Intent i=new Intent(Activity2.this,Activity2.class);
startActivity(i);
}});
b3.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
Intent i=new Intent(Activity2.this,Activity3.class);
startActivity(i);
}});
}
@Override
protected void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
Log.i("System.out", "Ac2--->destory");
}
}最后是Activity3,其界面如下:

Activity3的界面
如上图所示,点击"返回Main"则跳转至Fx_Main,点击"返回AC2"则跳转到Activity2.具体代码如下:
代码清单4-5. Activity3.Activity
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
public class Activity3 extends Activity {
private Button b;
private Button b2;
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.activity3);
b=(Button)findViewById(R.id.Button03);
b2=(Button)findViewById(R.id.Button04);
TextView tv=(TextView)findViewById(R.id.TextView03);
tv.setText("Ac3---->"+getTaskId());
Log.i("System.out", "Ac3---->"+this.toString()+"Task ID---->"+getTaskId());
b.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
Intent i=new Intent(Activity3.this,Fx_Main.class);
startActivity(i);
}});
b2.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
Intent i=new Intent(Activity3.this,Activity2.class);
startActivity(i);
}});
}
@Override
protected void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
Log.i("System.out", "Ac3--->Destory");
}
}
窗体间数据传递
第一个窗体com.tmsoft.ch42;android.content.Intent;android.support.v7.app.AppCompatActivity;android.os.Bundle;android.view.View;android.widget.EditText;android.widget.TextView;MainActivity AppCompatActivity { EditText ; TextView ; onCreate(Bundle savedInstanceState) { .onCreate(savedInstanceState); setContentView(R.layout.); = (EditText) .findViewById(R.id.); = (TextView) .findViewById(R.id.); } toSec(View view){ Intent intent = Intent(,Main2Activity.); intent.putExtra(,.getText().toString().trim()); .startActivityForResult(intent,); } onActivityResult(requestCode, resultCode, Intent data) { .setText(data.getStringExtra().toString()); } } <?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="com.tmsoft.ch42.MainActivity"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello World!" android:id="@+id/tv3" /> <EditText android:layout_width="wrap_content" android:layout_height="wrap_content" android:inputType="textPersonName" android:text="Name" android:ems="10" android:layout_below="@+id/textView" android:layout_alignParentStart="true" android:layout_marginStart="42dp" android:layout_marginTop="72dp" android:id="@+id/editText" /> <Button android:text="打开第二窗体并传递数据" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/editText" android:layout_toEndOf="@+id/textView" android:layout_marginStart="30dp" android:layout_marginTop="63dp" android:onClick="toSec" android:id="@+id/button" /></RelativeLayout>
第二个窗体com.tmsoft.ch42;android.content.Intent;android.support.v7.app.AppCompatActivity;android.os.Bundle;android.view.View;android.widget.EditText;android.widget.TextView;Main2Activity AppCompatActivity { TextView ; EditText ; onCreate(Bundle savedInstanceState) { .onCreate(savedInstanceState); setContentView(R.layout.); = (TextView) .findViewById(R.id.); Intent intent = .getIntent(); .setText(intent.getStringExtra().toString()); = (EditText) .findViewById(R.id.); } toFirst(View view){ Intent intent = Intent(,MainActivity.); intent.putExtra(,.getText().toString()+.getText().toString()); .setResult(,intent); .finish(); }}
重点:
1、Android程序的生命周期是由系统控制而非程序自身直接控制。
Android应用程序五个优先级:前台进程、可见进程、服务进程、后台进程、空进程。
2、Android四大组件:Activity、Service、BroadcaseReceiver和ContentProvider。
3、Activity常用事件:
• onKeyDown(int keyCode,KeyEvent event):对应按键按下事件
• onKeyUp(int keyCode,KeyEvent event):对应按键松开事件
• onTouchEvent(MotionEvent event):对应点击屏幕事件
4、Activity生命周期
Activity表现为四种状态,分别是活动状态、暂停状态、停止状态和非活动状态。

5、Activity的四种启动模式
Activity的启动模式有四种,分别是standard、singleTop、singleTask和singleInstance
6、横竖屏切换时的生命周期
不希望在横竖屏切换时Activity被销毁重建:
android:configChanges="orientation|keyboardHidden|screenSize“
一直处于竖屏或者横屏状态:
竖屏:android:screenOrientation="portrait"
横屏:android: screenOrientation="landscape"