Activity活动

  Activity是一个应用组件,用户可与其提供的屏幕进行交互,以执行拨打电话、拍摄照片、发送电子邮件或查看地图等操作。每个Activity都会获得一个用于绘制其用户界面的窗口。窗口通常会充满屏幕,但也可小于屏幕并浮动在其他窗口之上。
  一个应用通常由多个彼此松散联系的Activity组成。一般会指定应用中的某个Activity主Activity,即首次启动应用时呈现给用户的那个Activity。而且每个Activity均可启动另一个Activity,以便执行不同的操作。每次新Activity启动时,前一Activity便会停止,但系统会在堆栈(返回栈)中保留该Activity。当新Activity启动时,系统会将其推送到返回栈上,并取得用户焦点。返回栈遵循基本的后进先出堆栈机制,因此当用户完成当前Activity并按返回按钮时,系统会从堆栈中将其弹出(并销毁),然后恢复前一Activity
  当一个Activity因某个新Activity启动而停止时,系统会通过该Activity的生命周期回调方法通知其这一状态变化。Activity因状态变化:系统是创建Activity、停止Activity、恢复Activity还是销毁Activity,而收到的回调方法可能有若干种,每一种回调都会为您提供执行与该状态变化相应的特定操作的机会。例如停止时,您的Activity应释放任何大型对象,例如网络或数据库连接。当Activity恢复时,您可以重新获取所需资源,并恢复执行中断的操作。这些状态转变都是Activity生命周期的一部分。

创建Activity

  要创建Activity,您必须创建Activity的子类(或使用其现有子类)。您需要在子类中实现Activity在其生命周期的各种状态之间转变时(例如创建Activity、停止Activity、恢复Activity或销毁Activity时)系统调用的回调方法。两个最重要的回调方法是onCreateonPause

onCreate

  您必须实现此方法。系统会在创建您的Activity时调用此方法。您应该在实现内初始化Activity的必需组件。最重要的是,您必须在此方法内调用setContentView,以定义Activity用户界面的布局。

onPause

  系统将此方法作为用户离开Activity的第一个信号(但并不总是意味着Activity会被销毁)进行调用。您通常应该在此方法内确认在当前用户会话结束后仍然有效的任何更改(因为用户可能不会返回)。
  您还应使用几种其他生命周期回调方法,以便提供流畅的Activity间用户体验,以及处理导致您的Activity停止甚至被销毁的意外中断。后文的管理Activity生命周期部分对所有生命周期回调方法进行了阐述。

实现用户界面

  Activity的用户界面是由层级式视图(衍生自View类的对象)提供的。每个视图都控制Activity窗口内的特定矩形空间,可对用户交互作出响应。例如,视图可以是在用户触摸时启动某项操作的按钮。
  您可以利用Android提供的许多现成视图设计和组织您的布局。小部件是提供按钮、文本字段、复选框或仅仅是一幅图像等屏幕视觉(交互式)元素的视图。布局是衍生自ViewGroup的视图,为其子视图提供唯一布局模型,例如线性布局、网格布局或相对布局。您还可以为View类和ViewGroup类创建子类(或使用其现有子类)来自行创建小部件和布局,然后将它们应用于您的Activity布局。
  利用视图定义布局的最常见方法是借助保存在您的应用资源内的XML布局文件。这样一来,您就可以将用户界面的设计与定义Activity行为的源代码分开维护。您可以通过setContentView将布局设置为ActivityUI,从而传递布局的资源ID。不过,您也可以在Activity代码中创建新View,并通过将新View插入ViewGroup来创建视图层次,然后通过将根ViewGroup传递到setContentView来使用该布局。

在清单文件中声明Activity

  您必须在清单文件中声明您的Activity,这样系统才能访问它。要声明您的Activity,请打开您的清单文件,并将<activity>元素添加为<application>元素的子项:

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

您还可以在此元素中加入几个其他特性,以定义Activity标签、Activity图标或风格主题等用于设置Activity UI风格的属性。android:name属性是唯一必需的属性,它指定Activity的类名。应用一旦发布,即不应更改此类名,否则可能会破坏诸如应用快捷方式等一些功能。

使用Intent过滤器

  <activity>元素还可指定各种Intent过滤器,使用<intent-filter>元素以声明其他应用组件激活它的方法。
  当您使用Android SDK工具创建新应用时,系统自动为您创建的根Activity包含一个Intent过滤器,其中声明了该Activity响应操作,且应置于launcher类别内。Intent过滤器的内容如下:

<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>

<action>元素指定这是应用的入口点;<category>元素指定此Activity应列入系统的应用启动器内,以便用户启动该Activity
  如果您打算让应用成为独立应用,不允许其他应用激活其Activity,则您不需要任何其他Intent过滤器。正如前例所示,只应有一个Activity具有操作和launcher类别。如果您想让Activity对衍生自其他应用(以及您的自有应用)的隐式Intent作出响应,则必须为Activity定义其他Intent过滤器。对于您想要作出响应的每一个Intent类型,您都必须加入相应的<intent-filter>,其中包括一个<action>元素,还可选择性地包括一个<category>元素和/或一个<data>元素。这些元素指定您的Activity可以响应的Intent类型。

启动Activity

  您可以通过调用startActivity,并将其传递给描述您想启动的ActivityIntent来启动另一个ActivityIntent对象会指定您想启动的具体Activity或描述您想执行的操作类型(系统会为您选择合适的Activity,甚至是来自其他应用的Activity)。Intent对象还可能携带少量供所启动Activity使用的数据。
  在您的自有应用内工作时,您经常只需要启动某个已知Activity。您可以通过使用类名创建一个显式定义您想启动的ActivityIntent对象来实现此目的。例如,可以通过以下代码让一个Activity启动另一个名为SignInActivityActivity

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

不过,您的应用可能还需要利用您的Activity数据执行某项操作,例如发送电子邮件、短信或状态更新。在这种情况下,您的应用自身可能不具有执行此类操作所需的Activity,因此您可以改为利用设备上其他应用提供的Activity为您执行这些操作。这便是Intent对象的真正价值所在:您可以创建一个Intent对象,对您想执行的操作进行描述,系统会从其他应用启动相应的Activity。如果有多个Activity可以处理Intent,则用户可以选择要使用哪一个。例如,如果您想允许用户发送电子邮件,可以创建以下Intent

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

添加到Intent中的EXTRA_EMAIL extra是一个字符串数组,其中包含应将电子邮件发送到的电子邮件地址。当电子邮件应用响应此Intent时,它会读取extra中提供的字符串数组,并将它们放入电子邮件撰写窗体的收件人字段。在这种情况下,电子邮件应用的Activity启动,并且当用户完成操作时,您的Activity会恢复执行。

启动Activity以获得结果

  有时您可能需要从启动的Activity获得结果。在这种情况下,请通过调用startActivityForResult(而非startActivity)来启动Activity。要想在随后收到后续Activity的结果,请实现onActivityResult回调方法。当后续Activity完成时,它会使用Intent向您的onActivityResult方法返回结果。
  例如您可能希望用户选取其中一位联系人,以便您的Activity对该联系人中的信息执行某项操作。您可以通过以下代码创建此类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);
}

@Override
protected 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结果时应该在onActivityResult方法中使用的基本逻辑。第一个条件检查请求是否成功(如果成功,则resultCode将为RESULT_OK)以及此结果响应的请求是否已知:在此情况下,requestCode与随startActivityForResult发送的第二个参数匹配。代码通过查询Intent中返回的数据(data参数)从该处开始处理Activity结果。实际情况是,ContentResolver对一个内容提供程序执行查询,后者返回一个Cursor,让查询的数据能够被读取。

结束Activity

  您可以通过调用Activityfinish方法来结束该Activity。您还可以通过调用finishActivity结束您之前启动的另一个Activity
  在大多数情况下,您不应使用这些方法显式结束Activity。正如下文有关Activity生命周期的部分所述,Android系统会为您管理Activity的生命周期,因此您无需结束自己的Activity。调用这些方法可能对预期的用户体验产生不良影响,因此只应在您确实不想让用户返回此Activity实例时使用。

管理Activity生命周期

  通过实现回调方法管理Activity的生命周期对开发强大而又灵活的应用至关重要。Activity的生命周期会直接受到Activity与其他Activity、其任务及返回栈的关联性的影响。Activity基本上以三种状态存在:

  • 继续:此Activity位于屏幕前台并具有用户焦点(有时也将此状态称作运行中)。
  • 暂停:另一个Activity位于屏幕前台并具有用户焦点,但此Activity仍可见。也就是说,另一个Activity显示在此Activity上方,并且该Activity部分透明或未覆盖整个屏幕。暂停的Activity处于完全活动状态(Activity对象保留在内存中,它保留了所有状态和成员信息,并与窗口管理器保持连接),但在内存极度不足的情况下,可能会被系统终止。
  • 停止:该Activity被另一个Activity完全遮盖(该Activity目前位于后台)。已停止的Activity同样仍处于活动状态(Activity对象保留在内存中,它保留了所有状态和成员信息,但未与窗口管理器连接)。不过,它对用户不再可见,在他处需要内存时可能会被系统终止。

如果Activity处于暂停或停止状态,系统可通过要求其结束(调用其finish方法)或直接终止其进程,将其从内存中删除。(将其结束或终止后)再次打开Activity时,必须重建。

实现生命周期回调

  当一个Activity转入和转出上述不同状态时,系统会通过各种回调方法向其发出通知。所有回调方法都是挂钩,您可以在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 */
    }
}

正如以上示例所示,您在实现这些生命周期方法时必须始终先调用超类实现,然后再执行任何操作。这些方法共同定义Activity的整个生命周期。您可以通过实现这些方法监控Activity生命周期中的三个嵌套循环:

  • Activity的整个生命周期发生在onCreate调用与onDestroy调用之间。您的Activity应在onCreate中执行全局状态设置(例如定义布局),并释放onDestroy中的所有其余资源。例如,如果您的Activity有一个在后台运行的线程,用于从网络上下载数据,它可能会在onCreate中创建该线程,然后在onDestroy中停止该线程。
  • Activity的可见生命周期发生在onStart调用与onStop调用之间。在这段时间,用户可以在屏幕上看到Activity并与其交互。例如,当一个新Activity启动,并且此Activity不再可见时,系统会调用onStop。您可以在调用这两个方法之间保留向用户显示Activity所需的资源。例如,您可以在onStart中注册一个BroadcastReceiver以监控影响UI的变化,并在用户无法再看到您显示的内容时,在onStop中将其取消注册。在Activity的整个生命周期,当Activity在对用户可见和隐藏两种状态中交替变化时,系统可能会多次调用onStartonStop
  • Activity的前台生命周期发生在onResume调用与onPause调用之间。在这段时间,Activity位于屏幕上的所有其他Activity之前,并具有用户输入焦点。Activity可频繁转入和转出前台:例如,当设备转入休眠状态或出现对话框时,系统会调用onPause。由于此状态可能经常发生转变,因此这两个方法中应采用适度轻量级的代码,以避免因转变速度慢而让用户等待。

  下图说明了这些循环以及Activity在状态转变期间可能经过的路径。矩形表示回调方法,当Activity在不同状态之间转变时,您可以实现这些方法来执行操作:
在这里插入图片描述
  下表列出了相同的生命周期回调方法,其中对每一种回调方法做了更详细的描述,并说明了每一种方法在Activity整个生命周期内的位置,包括在回调方法完成后系统能否终止Activity

方法说明是否能事后终止?后接
onCreate首次创建Activity时调用。您应该在此方法中执行所有正常的静态设置:创建视图、将数据绑定到列表等等。系统向此方法传递一个Bundle对象,其中包含Activity的上一状态,不过前提是捕获了该状态(请参阅后文的保存Activity状态)onStart
onRestartActivity已停止并即将再次启动前调用onStart
onStartActivity即将对用户可见之前调用。如果Activity转入前台,则后接onResume;如果Activity转入隐藏状态,则后接onStoponResumeonStop
onResumeActivity即将开始与用户进行交互之前调用。此时,Activity处于Activity堆栈的顶层,并具有用户输入焦点onPause
onPause当系统即将开始继续另一个Activity时调用。此方法通常用于确认对持久性数据的未保存更改、停止动画以及其他可能消耗CPU的内容,诸如此类。它应该非常迅速地执行所需操作,因为它返回后,下一个Activity才能继续执行。如果Activity返回前台,则后接onResume,如果Activity转入对用户不可见状态,则后接onStoponResumeonStop
onStopActivity对用户不再可见时调用。如果Activity被销毁,或另一个Activity(一个现有Activity或新Activity)继续执行并将其覆盖,就可能发生这种情况。如果Activity恢复与用户的交互,则后接onRestart,如果Activity被销毁,则后接onDestroyonRestartonDestroy
onDestroyActivity被销毁前调用。这是Activity将收到的最后调用。当Activity结束(有人对Activity调用了finish),或系统为节省空间而暂时销毁该Activity实例时,可能会调用它。您可以通过isFinishin方法区分这两种情形

  名为是否能事后终止?的列表示系统是否能在不执行另一行Activity代码的情况下,在方法返回后随时终止承载Activity的进程。有三个方法带有标记:onPauseonStoponDestroy。由于onPause是这三个方法中的第一个,因此Activity创建后,onPause必定成为最后调用的方法,然后才能终止进程:如果系统在紧急情况下必须恢复内存,则可能不会调用onStoponDestroy。因此,您应该使用onPause向存储设备写入至关重要的持久性数据(例如用户编辑)。不过,您应该对onPause调用期间必须保留的信息有所选择,因为该方法中的任何阻止过程都会妨碍向下一个Activity的转变并拖慢用户体验。
  在是否能在事后终止?列中标记为的方法可从系统调用它们的一刻起,防止承载Activity的进程被终止。因此,在从onPause返回的时间到onResume被调用的时间,系统可以终止Activity。在onPause被再次调用并返回前,将无法再次终止Activity

保存Activity状态

  管理Activity生命周期的引言部分简要提及,当Activity暂停或停止时,Activity的状态会得到保留。确实如此,因为当Activity暂停或停止时,Activity对象仍保留在内存中:有关其成员和当前状态的所有信息仍处于活动状态。因此,用户在Activity内所做的任何更改都会得到保留,这样一来,当Activity返回前台(当它继续)时,这些更改仍然存在。
  不过,当系统为了恢复内存而销毁某项Activity时,Activity对象也会被销毁,因此系统在继续Activity时根本无法让其状态保持完好,而是必须在用户返回Activity时重建Activity对象。但用户并不知道系统销毁Activity后又对其进行了重建,因此他们很可能认为Activity状态毫无变化。在这种情况下,您可以实现另一个回调方法对有关Activity状态的信息进行保存,以确保有关Activity状态的重要信息得到保留,即onSaveInstanceState
  系统会先调用onSaveInstanceState,然后再使Activity变得易于销毁。系统会向该方法传递一个Bundle,您可以在其中使用putStringputInt等方法以名称-值对形式保存有关Activity状态的信息。然后,如果系统终止您的应用进程,并且用户返回您的Activity,则系统会重建该Activity,并将Bundle同时传递给onCreateonRestoreInstanceState。您可以使用上述任一方法从Bundle提取您保存的状态并恢复该Activity状态。如果没有状态信息需要恢复,则传递给您的Bundle是空值(如果是首次创建该Activity,就会出现这种情况)。
在这里插入图片描述
  在两种情况下,Activity重获用户焦点时可保持状态完好:系统在销毁Activity后重建ActivityActivity必须恢复之前保存的状态;系统停止Activity后继续执行Activity,并且Activity状态保持完好。
  无法保证系统会在销毁您的Activity前调用onSaveInstanceState,因为存在不需要保存状态的情况(例如用户使用返回按钮离开您的Activity时,因为用户的行为是在显式关闭Activity)。如果系统调用onSaveInstanceState,它会在调用onStop之前,并且可能会在调用onPause之前进行调用。
  不过,即使您什么都不做,也不实现onSaveInstanceStateActivity类的onSaveInstanceState默认实现也会恢复部分Activity状态。具体地讲,默认实现会为布局中的每个View调用相应的onSaveInstanceState方法,让每个视图都能提供有关自身的应保存信息。Android框架中几乎每个小部件都会根据需要实现此方法,以便在重建Activity时自动保存和恢复对UI所做的任何可见更改。例如,EditText小部件保存用户输入的任何文本,CheckBox小部件保存复选框的选中或未选中状态。您只需为想要保存其状态的每个小部件提供一个唯一的ID(通过android:id属性)。如果小部件没有ID,则系统无法保存其状态。
  您还可以通过将android:saveEnabled属性设置为false,或通过调用setSaveEnabled方法显式阻止布局内的视图保存其状态。您通常不应将该属性停用,但如果您想以不同方式恢复Activity UI的状态,就可能需要这样做。
  尽管onSaveInstanceState的默认实现会保存有关您的Activity UI的有用信息,您可能仍需替换它以保存更多信息。例如,您可能需要保存在Activity生命周期内发生了变化的成员值(它们可能与UI中恢复的值有关联,但默认情况下系统不会恢复储存这些UI值的成员)。
  由于onSaveInstanceState的默认实现有助于保存UI的状态,因此如果您为了保存更多状态信息而替换该方法,应始终先调用onSaveInstanceState的超类实现,然后再执行任何操作。同样,如果您替换onRestoreInstanceState方法,也应调用它的超类实现,以便默认实现能够恢复视图状态。
  由于无法保证系统会调用onSaveInstanceState,因此您只应利用它来记录Activity的瞬态(UI的状态):切勿使用它来存储持久性数据,而应使用onPause在用户离开Activity后存储持久性数据(例如应保存到数据库的数据)。
  您只需旋转设备,让屏幕方向发生变化,就能有效地测试您的应用的状态恢复能力。当屏幕方向变化时,系统会销毁并重建Activity,以便应用可供新屏幕配置使用的备用资源。单凭这一理由,您的Activity在重建时能否完全恢复其状态就显得非常重要,因为用户在使用应用时经常需要旋转屏幕。

处理配置变更

  有些设备配置可能会在运行时发生变化(例如屏幕方向、键盘可用性及语言)。发生此类变化时,Android会重建运行中的Activity(系统调用onDestroy,然后立即调用onCreate)。此行为旨在通过利用您提供的备用资源(例如适用于不同屏幕方向和屏幕尺寸的不同布局)自动重新加载您的应用来帮助它适应新配置。
  如果您对Activity进行了适当设计,让它能够按以上所述处理屏幕方向变化带来的重启并恢复Activity状态,那么在遭遇Activity生命周期中的其他意外事件时,您的应用将具有更强的适应性。
  正如上文所述,处理此类重启的最佳方法是利用onSaveInstanceStateonRestoreInstanceState(或onCreate)保存并恢复Activity的状态。

协调Activity

  当一个Activity启动另一个Activity时,它们都会体验到生命周期转变。第一个Activity暂停并停止(但如果它在后台仍然可见,则不会停止)时,同时系统会创建另一个Activity。如果这些Activity共用保存到磁盘或其他地方的数据,必须了解的是,在创建第二个Activity前,第一个Activity不会完全停止。更确切地说,启动第二个Activity的过程与停止第一个Activity的过程存在重叠。
  生命周期回调的顺序经过明确定义,当两个Activity位于同一进程,并且由一个Activity启动另一个Activity时,其定义尤其明确。以下是当Activity A启动Activity B时一系列操作的发生顺序:

  • Activity AonPause方法执行。
  • Activity BonCreateonStartonResume方法依次执行(Activity B现在具有用户焦点)。
  • 然后,如果Activity A在屏幕上不再可见,则其onStop方法执行。

您可以利用这种可预测的生命周期回调顺序管理从一个Activity到另一个Activity的信息转变。例如,如果您必须在第一个Activity停止时向数据库写入数据,以便下一个Activity能够读取该数据,则应在onPause而不是onStop执行期间向数据库写入数据。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值