当前位置: > 华清远见教育科技集团 > 嵌入式学习 > 讲师博文 > 第4章 Android生命周期
第4章 Android生命周期
时间:2016-12-13作者:华清远见

经过上一章节的学习,我们了解了Android应用程序设计的基础知识,对Android程序的开发有了一定的了解。在此基础上,本章我们将对Android系统的进程优先级的变化方式、Android系统的四大基本组件、Activity的生命周期中各个状态的变化关系、Android应用程序的调试方法和工具进行学习。

4.1. 程序生命周期

所谓的应用程序生命周期就是应用程序进程从创建到消亡的整个过程。在Android中,多数情况下每个程序都是在各自独立的Linux进程中运行的。当一个程序或其某些部分被请求时,它的进程就"出生"了;当这个程序没有必要再运行下去且系统需要回收这个进程的内存用于其他程序时,这个进程就"死亡"了。可以看出,Android程序的生命周期是由系统控制而非程序自身直接控制。这和我们编写桌面应用程序时的思维有一些不同,一个桌面应用程序的进程也是在其他进程或用户请求时被创建,但是往往是在程序自身收到关闭请求后执行一个特定的动作(比如从main方法中 return)而导致进程结束的。

简而言之,程序的生命周期是在Android系统中进程从启动到终止的所有阶段,也就是Android程序启动到停止的全过程,程序的生命周期由Android系统进行调度和控制的。

但是,一个不容忽视的问题就是,手机的内存只有那么多,随着我们打开的应用程序数量的增多,随之而来的可能会是应用程序响应时间过长或者系统假死的糟糕情况。所以,若将Android应用程序生命周期便交由系统处理的话,那么在系统内存不足的情况下,便由Android系统舍车保帅,选择性的来中止一些重要性较次的应用程序,以便回收内存供更重要的应用程序使用。

那么,系统是根据一个怎样的重要性标准来中止Android应用程序的呢?

Android根据应用程序的组件以及组件当前运行状态将所有的进程按重要性程度从高到低划分了五个优先级:前台进程、可见进程、服务进程、后台进程、空进程。


图4-1     Android系统进程优先级

以下就从优先级由高到低的顺序介绍一下Android系统中的进程。

4.1.1. 前台进程

前台进程是显示在屏幕前端并与用户正在交互的进程,是Android系统中重要的进程,包含以下四种情况:

  • 进程中的Activity正在与用户进行交互
  • 进程服务被Activity调用,而且这个Activity正在与用户进行交互
  • 进程服务正在执行声明周期中的回调方法,如onCreate()、onStart()或onDestroy()
  • 进程的BroadcastReceiver正在执行onReceive()方法

Android系统在多个前台进程同时运行时,可能会出现资源不足的情况,此时会清除部分前台进程,保证主要的用户界面能够及时响应。

4.1.2. 可见进程

可见进程指部分程序界面能够被用户看见,却不在前台与用户交互,不响应界面事件(其onPause()方法已被调用)的进程。如果一个进程包含服务,且这个服务正在被用户可见的Activity调用,此进程同样被视为可见进程。

Android系统一般存在少量的可见进程,只有在特殊的情况下,Android系统才会为保证前台进程的资源而清除可见进程。

4.1.3. 服务进程

服务进程是指包含由startService()方法启动服务的进程。它有以下特性:没有用户界面;在后台长期运行。例如,后台MP3播放器或后台上传下载数据的网络服务。

Android系统除非不能保证前台进程或可见进程所必要的资源,否则不强行清除服务进程

4.1.4. 后台进程

后台进程是指不包含任何已经启动的服务,而且没有任何用户可见的Activity的进程。这些进程不直接影响用户的体验。

Android系统中一般存在数量较多的后台进程,因些这些进程会被保存在一个列表中,以保证在系统资源紧张时,系统将优先清除用户较长时间没有见到的后台进程。

4.1.5. 空进程

空进程是不包含任何活跃组件的进程。一般保留这些进程,是为了将其做为一个缓存,在它所属的应用的组件下一次需要时,缩短启动的时间

空进程在系统资源紧张时会被首先清除,但为了提高Android系统应用程序的启动速度,Android系统会将空进程保存在系统内存中,在用户重新启动该程序时,空进程会被重新使用。

4.2. 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的生命周期。

4.3. Activity生命周期

Activity生命周期指Activity从启动到销毁的过程。Activity表现为四种状态,分别是活动状态、暂停状态、停止状态和非活动状态。

  • 活动状态,Activity在用户界面中处于上层,完全能被用户看到,能够与用户进行交互
  • 暂停状态,Activity在界面上被部分遮挡,该Activity不再处于用户界面的上层,且不能够与用户进行交互;或者屏幕被锁定。
  • 停止状态,Activity在界面上完全不能被用户看到,也就是说这个Activity被其他Activity全部遮挡
  • 非活动状态不在以上三种状态中的Activity则处于非活动状态

这四种状态是可以相互转换的。变换关系图如下:


图4-2     Activity的四种状态的变换关系图

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


图4-3     Activity活动周期

Android调用以下的事件回调方法通知activity从某一状态转变到另一状态:

代码清单4-1. 事件的回调方法

public class MyActivity extends Activity {
          protected void onCreate(Bundle savedInstanceState);
          protected void onStart();
          protected void onRestart();
          protected void onResume();
          protected void onPause();
          protected void onStop();
          protected void onDestroy();
    }

表4-1对各个事件回调方法作出说明:

方法 是否可终止 说明
onCreate() Activity启动后第一个被调用的方法,常用来进行Activity的初始化,例如创建View、绑定数据或恢复信息等。
onStart() 当Activity显示在屏幕上时,该方法被调用。
onRestart() 当Activity从停止状态进入活动状态前,调用该方法。
onResume() 当Activity能够与用户交互,接受用户输入时,该方法被调用。此时的Activity位于Activity栈的栈顶。
onPause() 当Activity进入暂停状态时,该方法被调用。一般用来保存持久的数据或释放占用的资源。
onStop() 当Activity进入停止状态时,该方法被调用。
onDestroy() 在Activity被终止前,即进入非活动状态前,该方法被调用。
onSaveInstanceState() Android系统因资源不足终止Activity前调用该方法,用以保存Activity的状态信息,供onRestoreInstanceState()或onCreate()恢复之用。
onRestoreInstanceState() 恢复onSaveInstanceState()保存的Activity状态信息,在onStart()和onResume ()之间被调用。

表4-1. Activity生命周期的事件回调方法


图4-4     Activity事件回调方法的调用顺序

Activity的生命周期可分为全生命周期、可视生命周期和活动生命周期。每种生命周期中包含不同的事件回调方法。

4.3.1 全生命周期

全生命周期是从Activity建立到销毁的全部过程,始于onCreate(),结束于onDestroy()。

使用者通常在onCreate()中初始化用户界面,分配引用类变量,绑定数据控件,并创建服务和线程等Activity所能使用的全局资源和状态,并在onDestroy()中释放这些资源,并确保所有外部连接被关闭,例如网络或数据库的联系等;在一些极端的情况下,Android系统会不调用onDestroy()方法,而直接终止进程。

为了避免创造短期对象和增加垃圾收集的时间,以致对用户体验产生直接影响。如果你的Activity需要创建一些对象的话,好在onCreate方法中创建,因为它仅调用一次在一个Actvity的完整生命周期中。

4.3.2 可视生命周期

可视生命周期是Activity在界面上从可见到不可见的过程,开始于onStart(),结束于onStop()。

  • onStart()一般用来初始化或启动与更新界面相关的资源
  • onStop()一般用来暂停或停止一切与更新用户界面相关的线程、计时器和服务
  • onRestart()方法在onSart()前被调用,用来在Activity从不可见变为可见的过程中,进行一些特定的处理过程
  • onStart()和onStop()会被多次调用
  • nStart()和onStop()也经常被用来注册和注销BroadcastReceiver或者传感器

在onStart()和onStop()这两个方法中间, Actvity将会对用户是可见的,尽管它可能部分被遮挡着。在一个Activity完整的生命周期中可能会经过几个Activity可见的生命周期,因为你的Activity可 能会经常在前台和后台之间切换。在极端情况下,系统将销毁掉一个Activity即使它在可见状态并且并不调用onStop方法。

4.3.3 活动生命周期

活动生命周期是Activity在屏幕的上层,并能够与用户交互的阶段,开始于onResume(),结束于onPause()。在Activity的状态变换过程中onResume()和onPause( )经常被调用,因此这两个方法中应使用更为简单、高效的代码。

  • onPause()是第一个被标识为"可终止"的方法
  • 在onPause()返回后,onStop()和onDestroy()随时能被Android系统调用
  • onPause()常用来保存持久数据,如界面上的用户的输入信息等

当系统而不是用户关闭一个活动来节省内存时,用户可能希望返回到活动且是它之前的状态。为了获得活动被杀之前的状态,你可以执行活动的onSaveInstanceState()方法。Android在活动容易被销毁前调用这个方法,也就是调用onPause()之前。该方法的参数是一个Bundle对象,在这个对象你可以以名值对记录活动的动态状态。当活动再次启动时,Bundle同时被传递到onCreate()和调用onCreate()之后的方法onRestoreInstanceState()。

因为onSaveInstanceState()方法不总是会被调用,你应该仅使用onSaveInstanceState()它来记录活动的临时状态,而不是持久的数据。应该使用onPause()来存储持久数据。

当一个活动启动另一个活动,这两个活动都经历生命周期转换。一个暂停或许是停止,然而被启动的活动则启动。有时,这些活动可能需要协调。当这两个活动在同一个进程中,生命周期的回调顺序是明确界定的:调用当前活动的onPause()方法;然后,按序调用启动活动的onCreate()、onStart()、onResume()方法;之后,如果该活动不需再在屏幕上可见,则调用它的onStop()方法。下面我们就来详细学习一下关于Android如何管理多个Activity。

  1. Android用Activity Stack来管理多个Activity,因此,同一时刻只会有顶上的Activity是处于active或者running状态。其它的Activity都被压在下面。
  2. 如果非活动的Activity仍是可见的(即如果上面压着的是一个非全屏的Activity或透明的Activity),它是处于paused状态的。在系统内存不足的情况
            下,paused状态的Activity是有可被系统销毁掉的
  注意
      因为Android应用程序的生存期并不是由应用本身直接控制的,而是由Android系统平台进行管理的,
  所以,对于我们开发者而言,需要了解不同的组件Activity、Service和IntentReceiver的生命,切记的
  是:如果组件的选择不当,很有可能系统会杀掉一个正在进行重要工作的进程。

4.4. Activity启动模式

Activity作为Android中重要一环,它有四种不同的启动模式,类似于C语言中的局部变量、全局变量以及静态变量等。这四种启动模式如下:

  • standard: 标准模式,一调用startActivity()方法就会产生一个新的实例。
  • singleTop: 检查是否已经存在了一个实例位于Activity Stack的顶部,如果存在就不产生新的实例,反之则调用Activity的newInstance()方法产生一个新实例。
  • singleTask: 在一个新的Task中产生这个实例,以后每次调用都会使用此实例,而避免产生新的实例
  • singleInstance: 这个基本上跟singleTask一样,只是有一点不同,那就是在这个模式下的Activity实例所处的Task中,只能有这一个Activity实例,而不能有其他的实例。

这些启动模式可以Android清单文件AndroidManifest.xml中,通过<activity>中的launchMode属性进行设置,如:

代码清单4-2. AndroidManifest.xml

<activity android:name=".Activity2"
              android:launchMode="singleTask"></activity>

也可以在Eclipse ADT中图形界面中编辑,如下图所示:


图4-5     设置Activity启动模式

下面通过一个简单的例子——LaunchMode_Test来简要对四种启动模式进行分析。在该例中涉及到Fx_Main、Activity2以及Activity3三个Activity。

首先,介绍一下例子中涉及到的三个Activity及其界面:

首先是Fx_Main.Activity:


图4-6     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
      &nnbsp;   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,其界面如下:


图4-7     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,其界面如下:


图4-8     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");
      }
    }

4.4.1 standard标准模式

在standard模式,也就是默认模式下,不需要配置launchMode。此时的AndroidManifest.xml如下:

代码清单4-6. AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="//schemas.android.com/apk/res/android"
            package="feixun.com.jiang"
            android:versionCode="1"
            android:versionName="1.0">
          <application  android:icon="@drawable/icon"  android:label="@string/app_name">
              <activity android:name=".Fx_Main"
                          android:label="@string/app_name">
                  <intent-filter>
                      <action android:name="android.intent.action.MAIN" />
                      <category android:name="android.intent.category.LAUNCHER" />
                  </intent-filter>
              </activity>
            <activity android:name=".Activity2" android:label="@string/Ac2"/ >
            <activity android:name=".Activity3" android:label="@string/Ac3/>

          </application>
          <uses-sdk android:minSdkVersion="4" />

    </manifest>

运行例子从Fx_Main---->Activity2---->Activity2---->Activity2可见,一直点回到Activity2按钮时,Log信息如下:


图4-9     Standard启动模式下log信息

发现每次都创建了Activity2的新实例。standard的加载模式就是这样的,intent将发送给新的Activity实例。

现在点Android设备的回退键,可以看到是按照刚才创建Activity实例的倒序依次出现,类似退栈的操作,而刚才操作跳转按钮的过程是压栈的操作。

4.4.2 singleTop

singleTop和standard模式,都会将intent发送新的实例(singleTask模式和singleInstance模式不发送到新的实例,如果已经有了的话)。不过,singleTop要求如果创建intent的时候栈顶已经有要创建的Activity的实例,则将intent发送给该实例,而不发送给新的实例。

还是用刚才的示例,只需将Activity2的launchMode改为singleTop,就能看到区别。修改后AndroidManifest.xml中代码如下:

代码清单4-7. AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="//schemas.android.com/apk/res/android"
            package="feixun.com.jiang"
            android:versionCode="1"
            android:versionName="1.0">
          <application  android:icon="@drawable/icon"  android:label="@string/app_name">
              <activity android:name=".Fx_Main"
                          android:label="@string/app_name" >
                  <intent-filter>
                      <action android:name="android.intent.action.MAIN" />
                      <category android:name="android.intent.category.LAUNCHER" />
                  </intent-filter>
              </activity>
            <activity android:name=".Activity2" android:label="@string/Ac2"
                          android:launchMode="singleTop"/ >
            <activity android:name=".Activity3" android:label="@string/Ac3/>

          </application>
          <uses-sdk android:minSdkVersion="4" />

    </manifest>

运行Fx_Main--->跳转到Activity2---->Actvity2的时候会发现,按多少遍按钮,都是相同的Activity2实例,因为该实例在栈顶,因此不会创建新的实例。如果回退,回到Fx_Main,然后将退出应用。


图4-10     singleTop模式下"跳转至AC2"的log信息

singleTop模式,可用来解决栈顶多个重复相同的Activity的问题。

如果是Fx_Main跳转到Activity2,再跳转到Fx_Main,行为就和standard一样了,会在Activity2跳转到Fx_Main的时候创建Fx_Main的新实例,因为当时的栈顶不是Activity2实例。


图4-11     singleTop模式下"跳转至AC2"后"跳回到Main"的log信息

4.4.3 singleTask

singleTask模式和后面的singleInstance模式都是只创建一个实例的。

当intent到来,需要创建singleTask模式Activity的时候,系统会检查栈里面是否已经有该Activity的实例。如果有直接将intent发送给它。(注意此时原在此Activity栈中上面的Activity将会被杀死)。

把Activity2的启动模式改成singleTask,修改后AndroidManifest.xml中代码如下:

代码清单4-8. AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="//schemas.android.com/apk/res/android"
            package="feixun.com.jiang"
            android:versionCode="1"
            android:versionName="1.0">
          <application  android:icon="@drawable/icon"  android:label="@string/app_name">
              <activity android:name=".Fx_Main"
                        android:label="@string/app_name" >
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
              </activity>
            <activity android:name=".Activity2" android:label="@string/Ac2"
                        android:launchMode="singleTask"/ >
            <activity android:name=".Activity3" android:label="@string/Ac3/>

          </application>
          <uses-sdk android:minSdkVersion="4" />

    </manifest>

启动Fx_Main--->跳转到Activity2---->Activity3---->Actvity2此时看Log信息。


图4-12     singleTask启动模式下log信息

可见从Ac3再跳转到Ac2时,因为Ac2之前在栈中是存在的所以不生成新的Ac2实例,而是在栈中找到此Ac2,并将在Ac2上面的Ac3杀死,所以此时栈中只有Fx_Main和Ac2,在Ac2点返回会直接退到Fx_Main然后退出。

4.4.4 singleInstance

在singleInstance模式下,加载该Activity时如果没有实例化,他会创建新的Task后,实例化入栈,如果已经存在,直接调用onNewIntent,该Activity的Task中不允许启动其它的Activity,任何从该Activity启动的其他Activity都将被放到其他task中,先检查是否有本应用的task,没有的话就创建。

在这里介绍一下Task(任务)的概念。按照字面意思,任务就是自己要实现的一个目的,而在Android中的Task的定义是一系列Activity的集合,即要达到自己终要到的Actvity,之前所有经历过的Actvity的集合。它可以是同一个应用内部的,也可以是两个不同应用的。Task可以认为是一个栈,可放入多个Activity。比如启动一个应用,那么 Android就创建了一个Task,然后启动这个应用的入口Activity,就是intent-filter中配置为main和launch的那个。这个Activity是根(Root)Activity,可能会在它的界面调用其他Activity,这些Activity如果按照上面那三个模式,也会在这个栈(Task)中,只是实例化的策略不同而已。

把Activity2的启动模式改成singleInstance,修改后AndroidManifest.xml中代码如下:

代码清单4-9. AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="//schemas.android.com/apk/res/android"
            package="feixun.com.jiang"
            android:versionCode="1"
            android:versionName="1.0">
          <application  android:icon="@drawable/icon"  android:label="@string/app_name">
              <activity android:name=".Fx_Main"
                          android:label="@string/app_name" >
                  <intent-filter>
                      <action android:name="android.intent.action.MAIN" />
                      <category android:name="android.intent.category.LAUNCHER" />
                  </intent-filter>
              </activity>
            <activity android:name=".Activity2" android:label="@string/Ac2"
                          android:launchMode="singleInstance"/ >
            <activity android:name=".Activity3" android:label="@string/Ac3/>

          </application>
          <uses-sdk android:minSdkVersion="4" />

    </manifest>

然后进行测试,启动Fx_Main---->Actvity2---->Actvity3然后看一下Log信息。


图4-13     singleInstance启动模式下log信息

可以看到Fx_Main以及Activity3的TaskID为9,而Actvity2的TaskID为10,此时在Actvity3点返回会发现先退到Fx_Main,继续返回会到Actvity2后退出。我们从该过程可以看出:如果从其它应用程序调用singleInstance模式的Activity(Fx_Main),从该Activity开启其他Activity(Activity2)时,会创建一个新的Task(task id为10的那个),实际上,如果包含该Activity(Activity2)的Task已经运行的话,他会在该运行的Task中重新创建。

经过上述的介绍,我们用下面的表格来进行一个简单的总结:

区别 是否允许多个实例 如何决定所属task 是否每次都生成新实例 是否允许其它activity存在于本task内
standard 可被多次实例化,同一个task的不同的实例可位于不同的task中,每task也可包含多个实例 存放于StartActivity()的Task。除非设置FLAG_ACTIVITY_NEW_TASK标记 允许
singleTop 同standard 同standard 如果寄存Activity的栈顶为该Activity,则直接用该Activity处理;否则,创建新实例 允许
singleTask 不能有多个实例。由于该模式下activity总是位于栈顶,所以actvity在同一个设备里至多只有一个实例。 放入新的task内,并且位于该task的根 只有在第一次才创建新的实例,其他情况复用该Activity。 允许。如果存放singleTask的寄存Task内。相应一个Intent时,如果singleTask位于栈顶,则处理Intent,否则会丢失Intent,但该Task会处于前台。
singleInstance 同singleTask 同singleTask 同singleTask 不允许

表4-2. Activity四种启动模式对比

4.5. 程序调试

Android系统提供了两种调试工具LogCat和DevTools,用于定位、分析及修复程序中出现的错误。

4.5.1 LogCat命令行工具

LogCat是可以显示在Eclipse集成开发环境中的用来获取系统日志信息的工具。它的主要功能就是能够捕获包括Dalvik虚拟机产生的信息、进程信息、ActivityManager信息、PackagerManager信息、Homeloader 信息、WindowsManager信息、Android运行时信息和应用程序信息等可被捕获的信息。

1. LogCat的使用方法:

打开方式:Window → Show View → Other,打开Show View的选择菜单,然后在Andoird → LogCat中选择LogCat。打开LogCat后,它便显示在Eclipse的下方区域,其界面如图4-5所示。


图4-14     LogCat界面

从图中我们可以看到LogCat的右上方有五个不同的字母,这五个字母分别表示五种不同类型的日志信息,他们的级别依次增高,表示含义如下:

  • [V]:详细(Verbose)信息
  • [D]:调试(Debug)信息
  • [ I ]:通告(Info)信息
  • [W]:警告(Warn)信息
  • [E]:错误(Error)信息

在LogCat中,用户可以通过五个字母图标选择显示的信息类型,级别高于所选类型的信息也会在LogCat中显示,但级别低于所选类型的信息则不会被显示。

同时,LogCat提供了"过滤"功能,在右上角的"+"号和"-"号,分别是添加和删除过滤器。用户可以根据日志信息的标签(Tag)、产生日志的进程编号(Pid)或信息等级(Level),对显示的日志内容进行过滤。

2. 程序调试原理:

  • 首先,引入android.util.Log包
  • 使用Log.v()、 Log.d()、 Log.i() 、Log.w() 和 Log.e()五个方法在程序中设置"日志点"。
    • √    Log.v()用来记录详细信息;
    • √    Log.d()用来记录调试信息;
    • √    Log.i()用来记录通告信息;
    • √    Log.w()用来记录警告信息;
    • √    Log.e()用来记录通错误信息。
  • 当程序运行到"日志点"时,应用程序的日志信息便被发送到LogCat中
  • 判断"日志点"信息与预期的内容是否一致
  • 进而判断程序是否存在错误。

下面的例子演示了Log类的具体使用方法。

代码清单4-10. LogCat.java

package com.example.LogCat;

    import android.app.Activity;
    import android.os.Bundle;
    import android.util.Log;

    public class LogCat extends Activity {
      final static String TAG = "LOGCAT";
      @Override
      public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        Log.v(TAG,"Verbose");
        Log.d(TAG,"Debug");
        Log.i(TAG,"Info");
        Log.w(TAG,"Warn");
        Log.e(TAG,"Error");
      }
    }

在本段代码中,程序第5行"import android.util.Log;"引入android.util.Log包;第8行定义标签,标签帮助用户在LogCat中找到目标程序生成的日志信息,同时也能够利用标签对日志进行过滤;第14行记录一个详细信息,Log.v()方法的第一个参数是日志的标签,第二个参数是实际的信息内容;第15行到第18行分别产生了调试信息、通告信息、警告信息和错误信息。

终运行结果如图4-6所示,从图中还可以看出LogCat对不同类型的信息使用了不同的颜色加以区别。


图4-15     LogCat工程的运行结果

3. 添加过滤器:

上文中提到LogCat提供了"过滤"功能,下面我们就来介绍一下LogCat是如何添加过滤器的。

首先,单击右上角的"+",在弹出的对话框中填入过滤器的名称:LogcatFilter,设置过滤条件为"标签=LOGCAT"即可,操作方法如图4-7所示。


图4-16     添加过滤器

经过上述过滤器过滤后,无论什么类型的日志信息,属于哪一个进程,只要标签为LOGCAT,都将显示在LogcatFilter区域内。LogCat过滤后的输入结果如图4-8所示:


图4-17     LogCat过滤后的输入结果

4.5.2 DevTools开发调试工具

DevTools是用于调试和测试的工具,它包括了如下所示一系列用户各种用途的小工具:Development Settings、Exception Browser、Google Login Service、Instrumentation、Media Scanner、Package Browser、Pointer Location、Raw Image Viewer、Running processes和Terminal Emulator。

如图4-9所示,为DevTools使用时界面。由使用时的界面也可以看出其各个小工具。

< align="center">
图4-18     DevTools的使用界面

以下我们着重讲解DevTools的一些小工具。

1. Development Settings

Development Settings中包含了程序调试的相关选项,单击功能前面选择框,出现绿色的"对号"表示功能启用,模拟器会自动保存设置。

图4-10显示了Development Settings的运行界面。


图4-19     Development Settings运行界面

下面我们就来详细介绍一下Development Settings中各个选项的含义。

选项 说明
Debug App 为Wait for debugger选项指定应用程序,如果不指定(选择none),Wait for debugger选项将适用于所有应用程序。Debug App可以有效的防止Android程序长时间停留在断点而产生异常。
Wait for debugger 阻塞加载应用程序,直到关联到调试器(Debugger)。用于在Activity的onCreate()方法的进行断点调试。
Show running processs 在屏幕右上角显示运行中的进程。
Show screen updates 选中该选项时,界面上任何被重绘的矩形区域会闪现粉红色,有利于发现界面中不必要的重绘区域。
No App Process limit 允许同时运行进程的数量上限。
Immediately destroy activites Activity进入停止状态后立即销毁,用于测试在方法 onSaveInstanceState()、onRestoreInstanceState()和onCreate()中的代码。
Show CPU usage 在屏幕顶端显示CPU使用率,上层红线显示总的CPU使用率,下层绿线显示当前进程的CPU使用率。
Show background 应用程序没有Activity显示时,直接显示背景面板,一般这种情况仅在调试时出现。
Show Sleep state on LED 在休眠状态下开启LED。
Windows Animation Scale 窗口动画规模
Transition Animation 转换动画
Light Hinting 轻显示
Show GTalk service connection status 显示GTalk服务连接状态。

表4-3. Development Settings选项

2. Pointer Location

Pointer Location是屏幕点位置查看工具,能够显示触摸点的X轴坐标和Y轴坐标。


图4-20     Pointer Location的使用画面

3. Running processes

Running processes能够查看在Android系统中正在运行的进程,并能查看进程的详细信息,包括进程名称和进程所调用的程序包。


图4-21     Andoird模拟器缺省情况下运行的进程和com.android.phone进程的详细信息

4. Terminal Emulator

Terminal Emulator可以打开一个连接底层Linux系统的虚拟终端,但具有的权限较低,且不支持提升权限的su命令。如果需要使用root权限的命令,可以使用ADB工具。

图4-13是Terminal Emulator运行时的画面,输入ls命令,显示出根目录下的所有文件夹。


图4-22 Terminal Emulator运行时的画面

本章小结

本章主要介绍了Android系统的进程优先级排序、不同优先级进程之间的变化方式,Android系统的四大基本组件及其用途,Activity的生命周期中各个状态以及状态间的变化关系、Android应用程序的调试方法和工具。

发表评论
评论列表(网友评论仅供网友表达个人看法,并不表明本站同意其观点或证实其描述)