Android官方文档之App Components(Activities)

来源:互联网 发布:重装ubuntu系统分区 编辑:程序博客网 时间:2024/05/16 11:50

Activity是Android四大组件之首,本文将介绍Activity的含义、创建、启动、销毁、生命周期 等。


如需访问官方原文,您可以点击这个链接:《Activities》


Activities

Activity是一个类,它是Android呈现界面的载体,用于与用户操作交互,如拨号、照相、发送邮件、展示地图 等。每个Activity都承载了一个Window,这个Window用来绘制UI(User Interface)。一般情况下,该Window铺满(fill)整个屏幕;有时候,它也可以悬浮于其它Window之上(float on top of other windows)。


通常,一个Android应用程序可包含多个activity,每个activity之间耦合较松(loosely bound to each other)。一般都有一个主activity,用于在启动程序时启动,每个activity都可以启动其他activity,每当一个新的activity被启动时,原来的activity就会处于stop状态,并把该activity实例保存在后退栈中(back stack),新启动的activity会被装入后退栈中并获得焦点。后退栈的存储机制是后进先出(last in, first out),所以当用户点击返回键时,顶端的activity将从栈顶弹出,并被destroyed,处于stop状态的activity进入resume状态。有关任务栈和返回栈的官方文档,您可以参考官方文档:《Tasks and Back Stack》,我将在后续翻译该文档。


当一个新的activity启动时,原来的activity将处于stop状态,在这个过程中,activity将回调其相应的生命周期方法来改变状态,activity有若干个生命周期回调方法,在每个方法中,可以做一些相应的工作,比如当activity进入stop状态时,系统会回调onStop()方法,在这个方法中,可以释放一些大的对象(release any large objects),如网络或数据库连接,当activity进入resume状态时,系统会回调onResume()方法,在这个方法中,可以重新获得一些重要的资源(eacquire the necessary resources)、重新开始一些被终止的action(resume actions that were interrupted)等。


创建Activity(Creating an Activity)


为了创建一个Activity,必须继承Activity类,并重写生命周期方法。如onCreate(), onStop(), onResume(), onDestroy() 等。其中最重要的回调方法为:

  • onCreate():必须重写该方法,系统会在创建Activity时回调。在该方法中,可以进行一些必要的组件初始化,另外,必须调用setContentView()方法绑定视图。

  • onPause():当Activity由可见变成不可见状态时,系统会回调该方法。在该方法中,应对用户的改变操作做一些持久化保存。


更多有关Activity生命周期的官方原文,您可以点击这个链接:《Managing the Activity Lifecycle》。


绑定UI(Implementing a user interface)


Activity承载的UI由一系列嵌套的View组成(a hierarchy of views),这些View都是继承于View类,每个View都控制着一个长方形的区域,并与用户交互。


Android内置了大量的View,您可以定制View的布局,如button, text field, checkbox, 或仅仅是一张image。ViewGroup类也继承于View类,它负责View的布局(layout),linear layout, a grid layout, relative layout都是不同的布局方式。您也可以继承View或ViewGroup来定制自己的布局。


最普遍的定义布局方式是在资源文件夹中创建XML布局文件。通过这种方式,您可以降低布局与Activity实现逻辑之间的耦合,您可以通过setContentView()方法将一个Activity与一个layout布局绑定。
更多有关布局的内容,您可以参考官方文档《User Interface》。


在manifest文件中注册Activity(Declaring the activity in the manifest)


为了让系统识别代码中定义的Activity,您需要在manifest清单文件中注册该Activity,格式如下:

<manifest ... >  <application ... >      <activity android:name=".ExampleActivity" />      ...  </application ... >  ...</manifest >

在activity标签中,只有android:name属性是不可缺省的。使用theme、icon等属性可以为Activity设置主题、图标等。一旦确定了Activity的名字,就不可再修改,否则会破坏一些功能,有关这方面的内容,您可以参考这篇博客:《Things That Cannot Change》


在activity标签中使用intent-filters(Using intent filters)


在activity标签中,可以指定多个<intent-filter>标签,该标签用于定义本activity具有何种性质以及其他应用程序如何启动本activity。


如需将一个activity定义为一个应用程序的主入口,可按如下方式定义:

<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只能由自己的应用程序启动(not allow other applications to activate its activities),那么不要为这个activity设置intent-filter。一个应用程序中只允许一个activity的intent-filter是 “main” action 和”launcher” category。Android不鼓励使用显式intent启动跨应用activity。

更多有关intent-filter的内容,您可以参考官方文档:《Intents and Intent Filters》,或者我翻译的博文:《Android官方文档之App Components(Intents and Intent Filters)》。


启动Activity(Starting an Activity)


为了启动一个activity,您可以调用startActivity()方法并传入Intent对象参数。Intent对象可以包含了action参数,action用于指定目标activity具备的性质,另外,Intent还能携带少量数据。
若启动同一应用程序的activity,可以使用显示Intent,如:

Intent intent = new Intent(this, SignInActivity.class);startActivity(intent);

更多的时候,是通过隐式Intent来启动activity,如启动一个发送邮件的activity:

Intent intent = new Intent(Intent.ACTION_SEND);intent.putExtra(Intent.EXTRA_EMAIL, recipientArray);startActivity(intent);

系统提供了大量的内置action,以便隐式启动系统自带的应用程序的activity,有关这部分的内容,您可以参考官方原文:《Common Intents》或我的翻译的博文:《Android官方文档之App Components(Common Intents)》。


启动activity并返回结果(Starting an activity for a result)


为了得到启动activity的返回结果,您可以调用startActivityForResult()方法代替startActivity()方法,并在onActivityResult()回调方法中接收结果(从该方法的Intent参数中获取)。
下面举一个获取通讯录联系人的例子:

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(Shutting Down an Activity)


调用finish()方法可以手动终止activity,也可以调用finishActivity()方法手动终止之前启动而不在前台的activity。


!请注意:大多数情况下,请不要手动终止activity,activity会根据其生命周期的状态自动终止。手动终止activity会影响用户体验,除非一些极端情况,请不要手动终止activity。


管理Activity的生命周期(Managing the Activity Lifecycle)


通过回调的Activity生命周期方法,可以管理Activity的生命周期。Activity会至少处于这三个状态:

  • Resumed(或running):这时Activity处于前台并获得焦点。

  • Paused:当另一个Activity处于前台并获得焦点时,原Activity失去焦点但仍有部分可见,此时原Activity处于pause状态。处于pause状态的Activity在内存中仍有其实例,且依附于window manager,它拥有暂停之前的所有状态,但系统可以在内存及其少的时候销毁它(can be killed by the system in extremely low memory situations)。

  • Stopped:当另一个Activity将原Activity完全遮挡时(completely obscured by another activity ),原Activity处于stopped状态。处于stopped状态的Activity在内存中仍有其实例,但不再依附于window manager,系统可以在需要内存的时候销毁它(can be killed by the system when memory is needed elsewhere)。


当Activity处于pause或stopped状态时,系统可以在其内存紧张时调用finish()方法销毁该Activity实例、或仅仅是杀死应用所在进程(simply killing its process)。当需要再次启动该Activity时,该实例必须重新创建。


重写生命周期回调方法(Implementing the lifecycle callbacks)


当Activity在上述不同状态之间切换时,系统会回调Activity中的相应方法,如下所示:

public class ExampleActivity extends Activity {    @Override    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        // The activity is being created.    }    @Override    protected void onStart() {        super.onStart();        // The activity is about to become visible.    }    @Override    protected void onResume() {        super.onResume();        // The activity has become visible (it is now "resumed").    }    @Override    protected void onPause() {        super.onPause();        // Another activity is taking focus (this activity is about to be "paused").    }    @Override    protected void onStop() {        super.onStop();        // The activity is no longer visible (it is now "stopped")    }    @Override    protected void onDestroy() {        super.onDestroy();        // The activity is about to be destroyed.    }}

!请注意:在重写这些回调方法之前,请务必先调用它们的父类方法。就像示例中的那样。


在这些回调方法中,包含了三个嵌套的内循环生命周期(three nested loops ):

  • 完整生命周期(entire lifetime):从onCreate()到onDestroy()。应在两个方法中处理一些全局的内容,如在onCreate()中初始化layout资源,并在onDestroy()方法中释放。如果在Activity中需要开启一个线程,访问网络下载文件,那么应在onCreate()中创建线程并在onDestroy()中停止线程。

  • 可见生命周期(visible lifetime):从onStart()到onStop(),在这个生命周期中,用户能看到该Activity承载的UI界面并与之交互(the user can see the activity on-screen and interact with it),如当onStop()方法被回调时,该Activity将不再可见,而新的Activity正处于启动状态。在这两个方法之间,您可以向用户展示一些资源,如您可以在onStart()方法中注册BroadcastReceiver,并在onStop()方法中解除注册。

  • 前台生命周期(foreground lifetime):从onResume()到onPause(),在这期间,Activity处于所有其他Activity的最上层并获得用户输入焦点,系统可以在前台生命周期中快速切换,如当设备休眠或者对话框弹出时,onPause()被回调。正因为可以快速切换,所以在这两个方法中尽量不要做较重的工作。

下图展示Activity生命周期的整个过程:
这里写图片描述


下表对activity生命周期做一总结:


方法 描述 系统是否可以在该方法执行后杀死activity? 接下来回调的方法 onCreate() 当activity第一次启动时回调,可为activity做静态初始化,如初始化View、为ListView绑定数据等;该方法回传一个Bundle 参数,该参数保存了上次在activity中存储的信息。 否 onStart() onRestart() activity处于stop状态而准备从新启动时 否 onStart() onStart() 当activity处于可见状态时,activity实例刚被压入后退栈 否 onResume() 或 onStop() onResume() 在用户处于可交互状态之前回调 否 onPause() onPause() 在其他activity准备启动之前调用,一般会在该方法中对一些数据进行持久化保存,停止动画等。在该方法中执行的操作不能过于繁琐,否则将影响新的activity的创建。 是 onResume() 或 onStop() onStop() UI界面不再可见时回调 是 onRestart() 或 onDestroy() onDestroy() activity示例销毁时调用。可能是开发者主动调用了finish()方法到时onDestroy()回调,也可能是系统的内存紧张而回调,您可以调用isFinishing()方法判断是上述那种情况到时onDestroy()被回调。 是 无

在“系统是否可以在该方法执行后杀死activity?”这一列中,有三个方法的结果为“是”(onPause(), onStop(), 和 onDestroy())。当内容及其紧张时,onPause()方法将被回调,而onStop() 和 onDestroy()方法不被回调,所以在onPause()方法中应对一些关键的、轻量的数据做持久化保存;在其他为“否”的回调方法中,也并不是说activity不能在这期间被杀死,只是这种情况基本不存在(内存中无可用空间)。


保存activity状态(Saving activity state)


当系统回调onPause()或onStop()方法时,activity实例在内存中仍然可见,所以当activity重新可见时,activity中的数据仍会原封不动地显示出来。
然而,有些时候activity实例会因为系统的内存不足而被回收,当activity再被重新创建时,系统将无法保证之前在界面中输入的数据可被完整地恢复,这时需要重写onSaveInstanceState()方法对数据做保存。
onSaveInstanceState()方法一般在activity实例即将被系统回收时(the activity vulnerable to destruction)回调,您可以在该方法回传的Bundle参数中保存键值对格式的数据,接着当activity因为内存紧张、屏幕转屏等原因被销毁时,该Bundle参数会同时传递至onCreate() 和 onRestoreInstanceState()方法中,当再次启动该activity时,回调onCreate() 或 onRestoreInstanceState(),可以从Bundle参数从获取之前保存的数据。若Bundle中没有数据,将为null。
onCreate() 和 onRestoreInstanceState()的区别在于:前者在每次初始化activity时必被回调,所以回传的Bundle参数可能为null,这需要在获取Bundle参数前做一判断;而后者只有在向Bundle中保存了数据后才会回调,所以不需要对Bundle判断是否为空,直接获取Bundle就可以。


activity的数据保存如下所示:
这里写图片描述


onSaveInstanceState()方法并非一定在destroy之前调用,当用户点击后退键主动退出activity时,onSaveInstanceState()可能在onStop()甚至是onPause()之前就被回调。


即便您不主动复写onSaveInstanceState()方法,系统也会回调该方法,并保存一些与View对应的layout布局信息(如CheckBox是否勾选、EditText中键入的内容等),当设备由竖屏切换至横屏时,横屏的Layout会被加载,并恢复onSaveInstanceState()中保存的布局信息,而整个的保存过程是通过同一View的不同布局的同一ID而恢复的,也就是说,若需要保存某个控件的键入信息,只需要在为其设置不同布局时(如横竖屏对应两个布局)为该控件指定同一ID即可。
当然,您也可以在activity标签中将android:saveEnabled属性设为false、或调用setSaveEnabled(false),来阻止onSaveInstanceState()方法自动保存layout信息,但一般不推荐这么做。


在实际开发中,推荐重写onSaveInstanceState()方法以保存额外的重要的信息,并在保存信息之前先调用父类的onSaveInstanceState()方法,这样可以保证layout布局、控件等信息被保存。


!请注意:由于onSaveInstanceState()方法不保证一定会被回调,所以一般使用onSaveInstanceState()方法保存一些UI界面上的即时信息,而不应用它保存持久化的内容;而应在onPause()方法中保存需要持久化的数据(如将数据存入Database中)


您可以通过旋转屏幕来测试信息是否已经有效保存,因为屏幕的旋转在设备使用过程中经常发生,若不能很好的保存数据,将破坏用户体验;另外,屏幕旋转就是一个activity销毁并重建的过程,这是一个很好的测试。


处理配置改变所带来的问题(Handling configuration changes)


设备的配置可能在运行时发生改变(如旋转屏幕,键盘可见于不可见,语言环境发生改变等),此时onDestroy()方法会被回调,并接着立刻回调onCreate()方法,这时之前在界面上保存的信息可能会消失。
最好的解决方式就是回调onSaveInstanceState() 和 onRestoreInstanceState()方法(或onCreate())。更多有关配置改变的内容,您可以参考这个官方文档《Handling Runtime Changes》


协调启动activity(Coordinating activities)


当Activity A 启动Activity B 时,下列回调将会发生:

  1. Activity A回调onPause();
  2. Activity B依次回调onCreate(), onStart(), 和 onResume();(此时Activity B 处于前台)
  3. Activity A 回调onStop();(Activity A不再可见)

上述步骤的一个应用场景是:若您希望将Activity A中的数据保存至Database中,并在Activity B中获取时,应在Activity A中的onPause()方法中保存,而不是在onStop()方法中。

2 0
原创粉丝点击