Android应用程序组件之Activity

来源:互联网 发布:网络造谣大v 编辑:程序博客网 时间:2024/05/21 09:21

虽然以前在cnblogs上写过不少技术文章,但这回在CSDN上重新开辟了一个新的博客,我想很大程度上是一种心态上的转变。希望在CSDN能够度过一段美好的时光,也希望大家能够在博客中学习到一些东西,谢谢!

Android系列的技术文章在各大技术博客网站上已经流传很久了,那么写这个系列的文章很大程度上是借助于博客来重新梳理一下自己的知识脉络,另外一方面也磨练一下码字的水平,博文中出现的任何问题望大家积极拍砖。

Android有四大应用程序组件:Activity、Service、Content Provider和Broadcast Receiver,这篇博文围绕Activity展开。

所谓Activity,简单说来就是提供用户交互的界面。不论是发邮件、写短信还是玩游戏,屏幕上显示的内容正是Activity所呈现的元素。从MVC设计思想的角度而言,Activity所承担的角色正是View显示层。那么,对于Activity我们需要了解的知识有哪些呢?下面分别对这些知识点进行总结和说明。

创建Activity

(1)创建Activity子类,实现与Activity生命周期相关的回调函数,后面会谈到与Activity生命周期相关的内容。我们从用户角度上来看,Activity至少有两种状态:显示和不显示。当然,从程序员的角度而言,这种说法并不能让我们满意,通常我们将其视为对象,那么它至少可以创建和销毁,尽管实际的生命周期会比这个稍微复杂的多。

(2)编写界面。通常我们会在res/layout目录下创建XML文件来定义Layout,再调用setContentView方法传入资源ID来设置UI。当然,你也可以自行创建View,并将View插入到ViewGroup中,将ViewGroup传入setContentView方法。ViewGroup充当View的容器,为View提供一致的布局。常见的布局有LinearLayout、AbsoluteLayout、FrameLayout、TableLayout和RelativeLayout,在API LEVEL 14(Android 4.0)里又加入了GridLayout,那么这些布局均是ViewGroup的子类。

(3)在AndroidManifest.xml文件中声明Activity。在对应的<application>标签中添加<activity>子标签,后面会有相关的文章介绍标签属性的作用,如下:

<manifest ... >  <application ... >      <activity android:name=".ExampleActivity" />      ...  </application ... >  ...</manifest >
(4)使用<intent-filter>,用来声明其他应用程序组件如何启动该Activity,例如在使用Android SDK tools创建新的应用程序时,会为你的主Activity自动添加如下<intent-filter>。

<activity android:name=".ExampleActivity" android:icon="@drawable/app_icon">    <intent-filter>        <action android:name="android.intent.action.MAIN" />        <category android:name="android.intent.category.LAUNCHER" />    </intent-filter></activity>

启动Activity

通过调用startActivity并传入Intent(指定了启动的Activity或Action类型),同时还可以向Intent传入数据。启动Activity分为显式和隐式两种方式。

显式的方式需要指定Activity类名,如启动SignActivity:

Intent intent = new Intent(this, SignInActivity.class);startActivity(intent);
隐式的方式需要利用其它应用程序提供的Activity完成相应的动作,如发短信、拍照等,这时你需要指定Action(动作)类型。当多个Activity能够处理该Intent时,系统会让你选择其中一个来处理。如发邮件:

Intent intent = new Intent(Intent.ACTION_SEND);intent.putExtra(Intent.EXTRA_EMAIL, recipientArray);startActivity(intent);
如果启动Activity用来获取结果(for a result),在启动时要调用startActivityForResult。为了接受启动的Activity的结果要在原Activity中实现onActivityResult回调函数。当启动的Activity结束时,会返回Intent到onActivityResult回调函数中。如选择联系人获取联系人信息的例子:

private void pickContact() {    // Create an intent to "pick" a contact, as defined by the content provider URI    Intent intent = new Intent(Intent.ACTION_PICK, Contacts.CONTENT_URI);    startActivityForResult(intent, PICK_CONTACT_REQUEST);}@Overrideprotected void onActivityResult(int requestCode, int resultCode, Intent data) {    // If the request went well (OK) and the request was PICK_CONTACT_REQUEST    if (resultCode == Activity.RESULT_OK && requestCode == PICK_CONTACT_REQUEST) {        // Perform a query to the contact's content provider for the contact's name        Cursor cursor = getContentResolver().query(data.getData(),        new String[] {Contacts.DISPLAY_NAME}, null, null, null);        if (cursor.moveToFirst()) { // True if the cursor is not empty            int columnIndex = cursor.getColumnIndex(Contacts.DISPLAY_NAME);            String name = cursor.getString(columnIndex);            // Do something with the selected contact's name...        }    }}

关闭Activity

调用finish()关闭Activity,或调用finishActivity()来关闭先前启动的Activity。

Activity生命周期


Activity的生命周期的核心内容都在上面这张图上。Activity实际上存在三种状态:resumed(Activity显示在最前面,获取了用户焦点)、paused(另一个Activity显示在前面并获取了焦点,当这个Activity仍然可见)和stopped(完全被另一个Activity覆盖)。当Activity处于paused或stopped状态时,可以通过finish结束或系统直接杀死该进程,此时系统会回收该Activity的内存资源。

那么,我们在上图中可以发现存在很多生命周期的回调函数,如onCreate()、onStart()等。通常来说,我们必须实现onCreate()回调函数,完成初始化的工作,如创建View等。

需要我们注意的是,在图中onPause()、onStop()和onDestroy()三个回调函数在返回后进程是可能被kill掉的,也就是说如果系统要杀死该进程,那么可能在这三个函数返回后任何一处下手。那么,给我们的启示就是,当我们在编写程序时,比如一个写博客的程序,我们在编辑界面需要考虑的一个情况是系统杀死该进程的处理,这时候我们的逻辑需要放在onPause()内来完成。当然,在某些极端的情况下Activity会在任何一个阶段都被系统kill,这就不是本节所要讨论的内容了。

我们知道,当activity处于paused或stopped状态时,activity的状态是保存在内存中的,因此当activity重新获取焦点时,与之前的状态保持一致。然而,当系统为了回收内存而销毁该activity时,以前的状态信息都会被销毁,再次启动该activity时不会恢复先前的状态。在这种情况下,一种解决方法是实现onSaveInstanceState()回调函数,保存系统状态信息。需要指出的是,保存的信息都维护在Bundle中,可以将Bundle视为一个Map。那么在系统杀死进程时,用户重新回到该activity时,在onCreate()或onRestoreInstanceState()中能够恢复先前的系统状态。如下图所示:


注意:当activity销毁时,并不能保证onSaveInstanceState()会被调用。因为在某些情况下没有必要保存系统状态,比如用户点击返回键离开activity。如果系统调用onStoreInstanceState(),可能发生在onStop()前或onPause()前。

另外,即使你并没有实现onSaveInstanceState(),系统的某些状态也得到了保存,它是由Activity类的onSaveInstanceState()实现来完成的。比如EditText控件,它会保存用户编辑的内容。但并不能保证父类的onSaveInstanceState()能够保存所有的状态信息,有时我们需要实现该方法来完成对一些额外信息的保存。那么,有人可能会问了,你前面说在onPause()里保存信息,这里又说在onSaveInstanceState()里保存信息,那究竟应该选择哪个?它们之间有什么区别?

前面说过,onSaveInstanceState()不一定会被调用,那么你应该用它来保存一些临时的数据,比如UI的状态等,而onPause()通常用来保存一些持久的数据。

测试应用程序保存状态的能力有一种很简单的方法,就是旋转设备。屏幕朝向在发生变化时,系统会销毁并重建activity,即调用onDestroy再快速调用onCreate()。处理这种变化的最好的方式是使用onSaveInstanceState和onRestoreInstanceState()(或onCreate())。

最后一点需要说明的是,当Activity A启动Activity B时,下面是回调函数执行的顺序:

(1)Activity A执行onPause();

(2)Activity B顺序执行onCreate()、onStart()和onResume(),此时Activity B并没有获得用户焦点;

(3)当Activity A不可见时,调用它的onStop()。

了解这一点可以维护Activity之间信息的过渡,比如Activity A在onPause()时向数据库写入数据,这样Activity B可以读取Activity A写入的数据,设想如果写数据操作是在onStop()中实现,那么数据一致性会受到破坏。

参考资料:Google ADT doc



Except as noted, this content is licensed under Creative Commons Attribution 2.5