android中Activity的生命周期及应用

来源:互联网 发布:java 请求webservice 编辑:程序博客网 时间:2024/05/16 19:16

本文详细介绍了 Android 应用编程中Activity 的生命周期、通信方式和Intent Filter 等内容,并提供了一些日常开发中经常用到的关于Activity 的技巧和方法。通过本文,你可以进一步了接Android Activity 的运作方式。

详解 Android Activity 组件

Activity 的生命周期

J2ME MIDlet 一样,在android 中,Activity的生命周期交给系统统一管理。与 MIDlet不同的是安装在 android中的所有的 Activity都是平等的。

Activity 的状态及状态间的转换

android 中,Activity拥有四种基本状态:

1. Active/Runing一个新 Activity 启动入栈后,它在屏幕最前端,处于栈的最顶端,此时它处于可见并可和用户交互的激活状态。

2. Paused 当 Activity 被另一个透明或者Dialog 样式的Activity 覆盖时的状态。此时它依然与窗口管理器保持连接,系统继续维护其内部状态,所以它仍然可见,但它已经失去了焦点故不可与用户交互。

3. Stoped 当 Activity 被另外一个Activity 覆盖、失去焦点并不可见时处于Stoped状态。

4. Killed Activity 被系统杀死回收或者没有被启动时处于 Killed状态。

当一个 Activity 实例被创建、销毁或者启动另外一个Activity 时,它在这四种状态之间进行转换,这种转换的发生依赖于用户程序的动作。下图说明了Activity 在不同状态间转换的时机和条件:

图 1. Activity 的状态转换

如上所示,Android 程序员可以决定一个Activity ,但不能决定它的,也就时说程序员可以启动一个Activity,但是却不能手动的结束一个Activity。当你调用Activity.finish()方 法时,结果和用户按下 BACK键一样:告诉 Activity ManagerActivity实例完成了相应的工作,可以被回收。随后Activity Manager 激活处于栈第二层的Activity 并重新入栈,同时原Activity 被压入到栈的第二层,从Active 状态转到Paused 状态。例如:从Activity1 中启动了Activity2,则当前处于栈顶端的是Activity2,第二层是Activity1,当我们调用Activity2.finish()方法时,Activity Manager重新激活 Activity1并入栈,Activity2Active状态转换 Stoped状态,Activity1. onActivityResult(int requestCode, int resultCode, Intent data)方法被执行,Activity2返回的数据通过 data参数返回给 Activity1

Activity 栈

Android 是通过一种 Activity栈的方式来管理 Activity的,一个 Activity的实例的状态决定它在栈中的位置。处于前台的 Activity总是在栈的顶端,当前台的 Activity因为异常或其它原因被销毁时,处于栈第二层的 Activity将被激活,上浮到栈顶。当新的 Activity启动入栈时,原 Activity会被压入到栈的第二层。一个 Activity在栈中的位置变化反映了它在不同状态间的转换。Activity的状态与它在栈中的位置关系如下图所示:

图 2. Activity 的状态与它在栈中的位置关系

如 上所示,除了最顶层即处在 Active 状态的Activity 外,其它的Activity 都有可能在系统内存不足时被回收,一个Activity 的实例越是处在栈的底层,它被系统回收的可能性越大。系统负责管理栈中Activity 的实例,它根据Activity 所处的状态来改变其在栈中的位置。

Activity 生命周期

android.app.Activity类中,Android 定义了一系列与生命周期相关的方法,在我们自己的 Activity中,只是根据需要复写需要的方法,Java的多态性会保证我们自己的方法被虚拟机调用,这一点与 J2ME中的 MIDlet类似。

 public class OurActivity extends Activity {

    protected void onCreate(Bundle savedInstanceState);

    protected void onStart();

    protected void onResume();

    protected void onPause();

    protected void onStop();

    protected void onDestroy();

 }

这些方法的说明如下:

1. protected void onCreate(Bundle savedInstanceState)一个 Activity 的实例被启动时调用的第一个方法。一般情况下,我们都覆盖该方法作为应用程序的一个入口点,在这里做一些初始化数据、设置用户界面等工作。大多数情况下,我们都要在这里从xml 中加载设计好的用户界面。例如:

 setContentView(R.layout.main);

当然,也可从 savedInstanceState中读我们保存到存储设备中的数据,但是需要判断 savedInstanceState是否为null,因为 Activity 第一次启动时并没有数据被存贮在设备中:

 if(savedInstanceState!=null){

 savedInstanceState.get("Key");

 }

1. protected void onStart()该方法在 onCreate()方法之后被调用,或者在 ActivityStop状态转换为 Active状态时被调用。

2. protected void onResume()ActivityPause状态转换到 Active状态时被调用。

3. protected void onResume()ActivityActive状态转换到 Pause状态时被调用。

4. protected void onStop()ActivityActive状态转换到 Stop状态时被调用。一般我们在这里保存 Activity的状态信息。

5. protected void onDestroy()Active被结束时调用,它是被结束时调用的最后一个方法,在这里一般做些释放资源,清理内存等工作。

图 3. 这些方法的调用时机

此外,Android 还定义了一些不常用的与生命周期相关的方法可用:

 protected void onPostCreate(Bundle savedInstanceState);

 protected void onRestart();

 protected void onPostResume();

Android 提供的文档详细的说明了它们的调用规则。

回页首

创建一个 Activity

android 中创建一个Activity 是很简单的事情,编写一个继承自android.app.ActivityJava 类并在 AndroidManifest.xml声明即可。下面是一个为了研究Activity 生命周期的一个Activity 实例(工程源码见下载):

Activity 文件:

 public class EX01 extends Activity {

    private static final String LOG_TAG = EX01.class.getSimpleName();

    @Override

    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);      

        setContentView(R.layout.main);

        Log.e(LOG_TAG, "onCreate");

    }

   @Override

    protected void onStart() {

        Log.e(LOG_TAG, "onStart");

        super.onStart();

    }

    @Override

    protected void onResume() {

        Log.e(LOG_TAG, "onResume");

        super.onResume();

    }

    @Override

    protected void onPause() {

        Log.e(LOG_TAG, "onPause");

        super.onPause();

    }

    @Override

    protected void onStop() {

        Log.e(LOG_TAG, "onStop");

        super.onStop();

    }

    @Override

    protected void onDestroy() {

        Log.e(LOG_TAG, "onDestroy ");

        super.onDestroy();

    }

 }

AndroidManifest.xml 中通过 <activity>节点说明 Activity,将apk 文件安装后,系统根据这里的说明来查找读取Activity,本例中的说明如下:

 <activity android:name=".EX01" 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

Activity.startActivity()方法可以根据传入的参数启动另外一个 Activity

 Intent intent =new Intent(CurrentActivity.this,OtherActivity.class);

 startActivity(intent);

当然,OtherActivity同样需要在 AndroidManifest.xml中定义。

回页首

Activity 之间通信

使用 Intent 通信

Android 中,不同的Activity 实例可能运行在一个进程中,也可能运行在不同的进程中。因此我们需要一种特别的机制帮助我们在Activity 之间传递消息。Android中通过 Intent对象来表示一条消息,一个 Intent对象不仅包含有这个消息的目的地,还可以包含消息的内容,这好比一封 Email,其中不仅应该包含收件地址,还可以包含具体的内容。对于一个Intent 对象,消息目的地是必须的,而内容则是可选项。

在上面的实例中通过 Activity. startActivity(intent)启动另外一个 Activity 的时候,我们在 Intent类的构造器中指定了收件人地址

如果我们想要给收件人”Activity说点什么的话,那么可以通过下面这封“e-mail”来将我们消息传递出去:

 Intent intent =new Intent(CurrentActivity.this,OtherActivity.class);

  // 创建一个带“收件人地址”的 email

 Bundle bundle =new Bundle();// 创建 email 内容

 bundle.putBoolean("boolean_key", true);// 编写内容

 bundle.putString("string_key", "string_value");

 intent.putExtra("key", bundle);// 封装 email

 startActivity(intent);// 启动新的 Activity

那么收件人该如何收信呢?在OtherActivity类的 onCreate()或者其它任何地方使用下面的代码就可以打开这封“e-mail”阅读其中的信息:

 Intent intent =getIntent();// 收取 email

 Bundle bundle =intent.getBundleExtra("key");// 打开 email

 bundle.getBoolean("boolean_key");// 读取内容

 bundle.getString("string_key");

上面我们通过 bundle对象来传递信息,bundle维护了一个 HashMap<String, Object>对象,将我们的数据存贮在这个HashMap 中来进行传递。但是像上面这样的代码稍显复杂,因为Intent 内部为我们准备好了一个bundle,所以我们也可以使用这种更为简便的方法:

 Intent intent =new Intent(EX06.this,OtherActivity.class);

 intent.putExtra("boolean_key", true);

 intent.putExtra("string_key", "string_value");

 startActivity(intent);

接收:

 Intent intent=getIntent();

 intent.getBooleanExtra("boolean_key",false);

 intent.getStringExtra("string_key");

使用 SharedPreferences

SharedPreferences 使用 xml格式为 Android应用提供一种永久的数据存贮方式。对于一个 Android应用,它存贮在文件系统的 /data/ data/your_app_package_name/shared_prefs/目录下,可以被处在同一个应用中的所有Activity 访问。Android提供了相关的 API来处理这些数据而不需要程序员直接操作这些文件或者考虑数据同步问题。

 // 写入 SharedPreferences

 SharedPreferences preferences = getSharedPreferences("name", MODE_PRIVATE);

 Editor editor = preferences.edit();

 editor.putBoolean("boolean_key", true);

 editor.putString("string_key", "string_value");

 editor.commit();

        

 // 读取 SharedPreferences

 SharedPreferences preferences = getSharedPreferences("name", MODE_PRIVATE);

 preferences.getBoolean("boolean_key", false);

 preferences.getString("string_key", "default_value");

其它方式

Android 提供了包括 SharedPreferences在内的很多种数据存贮方式,比如 SQLite,文件等,程序员可以通过这些API 实现Activity 之间的数据交换。如果必要,我们还可以使用IPC 方式。

回页首

Activity 的 Intent Filter

Intent Filter 描述了一个组件愿意接收什么样的 Intent对象,Android将其抽象为 android.content.IntentFilter类。在 AndroidAndroidManifest.xml配置文件中可以通过 <intent-filter >节点为一个Activity 指定其Intent Filter,以便告诉系统该Activity 可以响应什么类型的Intent

当 程序员使用 startActivity(intent) 来启动另外一个Activity 时,如果直接指定intent 了对象的Component 属性,那么Activity Manager 将试图启动其Component 属性指定的Activity。否则Android 将通过Intent 的其它属性从安装在系统中的所有Activity 中查找与之最匹配的一个启动,如果没有找到合适的Activity,应用程序会得到一个系统抛出的异常。这个匹配的过程如下:

图 4. Activity 种 Intent Filter 的匹配过程

Action 匹配

Action 是一个用户定义的字符串,用于描述一个 Android应用程序组件,一个 Intent Filter可以包含多个 Action。在AndroidManifest.xml Activity 定义时可以在其<intent-filter >节点指定一个 Action 列表用于标示 Activity所能接受的动作,例如:

 <intent-filter >

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

 <action android:name="com.zy.myaction" />

……

 </intent-filter>

如果我们在启动一个 Activity 时使用这样的Intent 对象:

 Intent intent =new Intent();

 intent.setAction("com.zy.myaction");

那么所有的 Action 列表中包含了com.zy.myactionActivity 都将会匹配成功。

Android 预定义了一系列的 Action分别表示特定的系统动作。这些 Action通过常量的方式定义在 android.content. Intent中,以ACTION_开头。我们可以在Android 提供的文档中找到它们的详细说明。

URI 数据匹配

一个 Intent 可以通过URI 携带外部数据给目标组件。在<intent-filter >节点中,通过 <data/>节点匹配外部数据。

mimeType 属性指定携带外部数据的数据类型,scheme指定协议,hostportpath指定数据的位置、端口、和路径。如下:

 <data android:mimeType="mimeType" android:scheme="scheme"

 android:host="host" android:port="port" android:path="path"/>

如果在 Intent Filter 中指定了这些属性,那么只有所有的属性都匹配成功时URI 数据匹配才会成功。

Category 类别匹配

<intent-filter >节点中可以为组件定义一个 Category类别列表,当 Intent中包含这个列表的所有项目时 Category类别匹配才会成功。

回页首

一些关于 Activity 的技巧

锁定 Activity 运行时的屏幕方向

Android 内置了方向感应器的支持。在 G1中,Android会根据 G1所处的方向自动在竖屏和横屏间切换。但是有时我们的应用程序仅能在横屏 /竖屏时运行,比如某些游戏,此时我们需要锁定该 Activity运行时的屏幕方向,<activity >节点的 android:screenOrientation属性可以完成该项任务,示例代码如下:

 <activity android:name=".EX01"

 android:label="@string/app_name"

 android:screenOrientation="portrait">// 竖屏 , 值为 landscape 时为横屏

…………

 </activity>

全屏的 Activity

要使一个 Activity 全屏运行,可以在其onCreate()方法中添加如下代码实现:

 // 设置全屏模式

 getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,

    WindowManager.LayoutParams.FLAG_FULLSCREEN);

 // 去除标题栏

 requestWindowFeature(Window.FEATURE_NO_TITLE);

在 Activity 的 Title 中加入进度条

为了更友好的用户体验,在处理一些需要花费较长时间的任务时可以使用一个进度条来提示用户不要着急,我们正在努力的完成你交给的任务。如下图:

Activity 的标题栏中显示进度条不失为一个好办法,下面是实现代码:

 // 不明确进度条

 requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);

 setContentView(R.layout.main);

 setProgressBarIndeterminateVisibility(true);

 

 // 明确进度条

 requestWindowFeature(Window.FEATURE_PROGRESS);

 setContentView(R.layout.main);

 setProgress(5000);

 

0 0