Activity

众所周知,Activity是用来为用户提供交互界面的app组件,每个Activity都会获得了一个窗口window,一般来说,window会覆盖整个屏幕,但有时也会小于屏幕大小或者悬浮于其他屏幕之上。

一个app中的各个Activity之间通常都会有一定的关联。例如其他Activity都可能是从main Activity直接或者间接的启动一样。在每次启动一个新的Activity时,之前的Activity都会处于stopped状态,系统会把它继续保存在back stack上,而最新的Activity一直处于back stack的顶端,并且获得用户的focus。当用户点击back按钮时,该Activity就会从back stack中弹出,并且进入destroyed状态,与此同时,back stack中的前一个Activity将会进入resume状态。

Activity基本状态的改变都会通过其生命周期相关的回调函数通知给该Activity,以让我们可以在对应的生命周期中做一些处理工作。例如,当Activity处于stopped状态,我们可以释放一些大的对象,例如网络请求或者数据库连接等。当Activity进入resume状态时,我们可以重新获取必要的资源,或者重新开始之前被打断的action。

接下来,我们将讨论如何创建和使用Activity以及Activity生命周期的详细讨论,这将有助于我们在各种生命周期状态之间做好切换和准备的工作。


创建一个Activity

首先需要创建一个Activity的子类,并实现与生命周期相关的回调方法。最重要的两个回调方法是onCreate()和onPause()。其中,在onCreate()方法中务必要调用setContentView()来定义用户交互界面的布局。


1.实现UI

Activity的UI主要都是由一系列View的子类所提供的。每个view都控制着Activity窗口中特定的一个长方形区域,并且可以同用户交互。

Android已经提供了很多view来帮助设计和组织布局。Widget可以提供视觉效果和交互功能,例如button,text field,checkbox或者一个image。Layout是ViewGroup的派生类,它提供了一个特定的布局模型,它的派生类主要有linear layout,grid layout和relative layout。我们也可以直接自定义View或者ViewGroup的子类来自定义widget和layout。

定义布局最常见的方式是在app的资源文件中定义一个XML布局文件。这样就可以将UI的设计同Activity具体行为相关的代码分离。可以将资源ID传递给setContentView来为Activity设置布局。但是我们也同样可以创建一系列View实例,并将它们添加至一个ViewGroup中,最后将根ViewGroup传递给setContentView方法来设置布局。

2.在manifest文件中声明Activity

为了使得系统可以获得该Activity,必须在manifest文件中声明Activity。将<activity>标签嵌套在<application>标签内。例如:

<manifest ... >
  <application ... >
      <activity android:name=".ExampleActivity" />
      ...
  </application ... >
  ...
</manifest >
此外还可以为Activity指定一些其他的属性,如label,icon,或者theme style。android:name属性是唯一一个必须要指定的,它的值是该Activity的类名。一旦发布了app之后,就不应该再修改Activity的名字,否则你会破坏一些功能性,比如app的缩写(详见Things That Cannot Change)。


使用Intent filter

添加<intent-filter>标签,声明其他app组件激活该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>
详解可以参考intent和intent filters文章。


如果你不希望其他app来激活某个Activity,则不需要制定intent filter。只有主Activity需要指定main的action和launcher的category。

否则你就需要添加intent filter标签,并且必须指定一个action标签,而category或者data是可选的。这些属性共同决定了该Activity能够响应的intent类型。


启动一个Activity:

Intent用来指定所要启动的Activity类型,然后将它传递给startActivity方法,就用启动一个Activity。关于显式和隐式的启动方法略。

启动一个有返回值的Activity:

从名字就可以知道,这个时候应该使用startActivityForResult方法,然后实现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...
        }
    }
}
第一个条件表达式用来检查请求是否成功,第二个条件表达式检查返回码从而确定是哪个返回类型,返回码在调用startActivityForResult方法时即指定。

结束一个Activity:

可以使用finish()方法。还可以通过finishActivity()方法来结束一个之前启动的Activity。

注意:

在大多数情况下不要使用这些方法。Android系统本身就是管理Activity的生命周期,自己结束Activity可能会带来较差的用户体验,因此只有在确定需要结束Activity时才使用上述方法。


管理Activity生命周期:

通过实现回调函数来管理Activity的生命周期对于开发一个健壮且灵活app是相当重要的。一个Activity的生命周期直接受到其他与之相关的Activity,它的task以及back stack的影响。

Activity主要有三个状态:

1.Resumed

Activity在屏幕前端,且获得了用户的focus(有时也叫作running)。

2.Paused

其他Activity处于屏幕前端且持有focus,但是该Activity仍然可以被看见。也就是说,有个Activity在该Activity上方,但是那个Activity是部分透明的或者并不占据整个屏幕,那么该Activity就处于Paused状态。此时,Activity也是完全具有活性的(即Activity对象仍然在内存中,它保持着自己所有的状态和成员信息,并且仍然依附在window manager上),但是在系统内存极低的情况下也可能会被杀死。

3.Stopped

完全被其他Activity遮住(该Activity进入后台),处于stopped状态的Activity也是具有活性的,它的对象仍然在内存中,它以及保持着自己所有的状态和成员信息,但是不再依附于window manger。此时用户并看不见该Activity,因此在其他地方需要内存的时候就会被杀死(比Paused条件宽松)。

当一个Activity处于paused或者stopped状态时,系统可以通过调用finish()方法或者直接杀死该进程来清理它的内存(直接杀死进程,是否会导致Activity的回调方法不被调用?)。当Activity被重新打开时,必须重新开始创建create。


实现生命周期的回调方法:

在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三个嵌套的生命周期循环:

1.entire lifetime

处于onCreate和onDestroy之间。应该在onCreate中做一些全局的准备工作(如设置布局文件),然后在onDestroy中释放所有的资源。

2.visible lifetime

处于onStart和onStop之间。此时,用户可以在屏幕上看见Activity并且可以与之交互。因此,可以在这个阶段保持一些与显示相关的资源。比如在onStart方法中注册一个BroadcastReceiver来监听可能会改变UI的事件,而在onStop中解除注册。当Activity在可见和不可见之间切换时,系统将会频繁的调用onStart和onStop方法。

3.foreground lifetime

处于onResume和onPause之间。此时,在屏幕中,该Activity处于其他所有Activity之前,并且持有用户的focus。一个Activity可以频繁的切进前台和切出前台(比如设备进入休眠状态或者弹出一个对话时),因此这两个回调方法中的代码应该处理轻量级的工作。

下面这张生命周期图比较重要的:


每个生命周期的详细描述不做讨论,这里只说一下onDestroy:它被调用既可能是因为有人调用了finish也可能是系统暂时性的回收空间,此时可以通过isFinishing()方法来判断,而该方法经常在onPaused()中使用,来确定Activity是简单的pausing还是被结束。


保存Activity的状态:

当Activity被pause或者stop时,Activity的状态仍然会被保持,这是因为它的实例仍然在内存中。但是当系统杀死该Activity来回收内存时,系统就不能如此简单的恢复Activity的状态了。此时,系统需要重新创建Activity。因此就需要提供一个回调函数来做保存实例状态的工作,这也就是onSaveInstanceState()。

在Activity处于极易被回收的状态之前,系统会调用onSaveInstanceState()方法。此时可以操作一个Bundle对象,比如通过putString或者putInt等方法。当用户重新切换至该Activity而该Activity又被回收时,就会将这个Bundle对象传递给onCreate和onRestoreInstanceState()方法,无论使用哪个方法都可以从Bundle中恢复Activity的状态。如果没有状态信息需要保持,Bundle参数就会是一个null值(即当Activity是第一次被创建时)。

注意:

并不保证在Activity被销毁之前一定会执行onSaveInstanceState方法,这是因为不是在所有的情况下都需要保持Activity的状态(比如用户点击back按钮主动离开该Activity)。如果系统调用了onSaveInstanceState方法,那么它必定在onStop方法之前,也有可能在onPause方法之前。


但是,如果什么都不做且不实现onSaveInstanceState回调方法,Activity的一些状态也会通过Activity类中onSaveInstanceState的默认实现来保存和恢复。特别的,默认的实现会为布局中的每一个view调用onSaveInstanceState方法,从而使得每个view可以提供需要保存的信息。几乎Android架构中的所有widget都实现了这个方法,从而UI的任意可见的变化都会自动的保存和恢复。例如,EditText保存用户输入的任意文本,CheckBox保存它是否被checked的状态。但是,只有通过Android:id属性指定了唯一的ID时,该widget才会自动保存和恢复。如果一个widget没有ID,则系统不会保存它的状态。

尽管onSaveInstanceState的默认实现会保存很多UI相关的有用信息,我们有时还是需要复写onSaveInstanceState方法来存储更多的信息。比如一些成员变量。

因为onSaveInstanceState有默认实现,所有我们在复写该方法时也需要先调用超类中的方法。同样的,在复写onRestoreInstanceState方法时,也需要调用超类的实现。

注意:

因为onSaveInstanceState方法并不一定被调用,因此只能用它来保存一些瞬态的数据(如UI的状态),而不能保存持久的数据。此时,应该在onPause方法中来保存持久的数据。


测试app重新获取状态能力的一个好方法是旋转屏幕。当屏幕旋转时,系统会销毁Activity然后再重新创建Activity来重新加载可能对新的屏幕更加合适的布局。同样的,因为用户可能会经常旋转屏幕,所以需要完全的存储Activity的状态以保证用户体验。


处理configuration的变化:

当设备的一些设置发生变化时(如屏幕旋转,键盘变化或者语言变化),Android就会重新创建Activity(先调用onDestroy,再立马调用onCreate),这个行为可以重新加载app,从而更加适应新的设置。


Activity之间的相互切换:

当一个Activity启动另一个Activity时,它们都会经历生命周期的变化。第二个Activity的启动过程与第一个Activity的停止过程存在重叠。

当Activity A启动Activity B时,情况如下:

1.Activity A的onPause方法执行;

2.Activity B的onCreate,onStart和onResume方法顺序执行(Activity B从此持有用户的focus);

3.然后,如果Activity A不在出现在屏幕上,它的onStop方法执行。

这种完全可预料的生命周期回调函数的执行的顺序,使得我们可以管理Activity之间的转换。例如,如果第一个Activity必须向数据库写入数据从而第二个Activity才能够读取它,那么这段操作应该写在onPause方法中而非onStop中。

注:

从onStop方法调用的时机也可以看出来,它在该Activity不再对用户可见之后才会调用。那么上述过程可以表述为:Activity A退出foreground,Activity B开始创建并接管foreground,同时使Activity A不再可见(比较只能可见一个),Activity A发现自己不再被用户可见,从而调用自己的onStop方法。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值