Activity

Activity 是一个应用组件,提供与用户交互的界面,用户可以通过activity进行一些操作,比如打电话,发邮件,查看地图等。每个activity都有一个对应的窗口显示它的UI。一般这个窗口会填充屏幕,但也有可能比屏幕小或者浮动在其它窗口上。

每个app通常由多个activity组成,这些activity有可能会互相绑定。在一个app的activity中,有一个main activity,当用户启动这个app是,这个activity会第一个被加载。每个activity有可能会启动其它activity来执行不同的功能。当新的activity启动后,旧的activity就stop (停止),但是系统在stack中保留旧的activity。当新的activity启动后,它将会被放到back stack (后台栈)中,并得到focus (关注)。在back stack中执行的是后进先出的机制,所以当用户与当前的activity交互完后按下back(返回)键,这个activity就会从stack中抛出,之前的那个activity就resume (复原)。关于back stack (后台栈)更多信息详见Tasks and Back Stack。

当一个activity因为一个新的activity 的start (启动)而停止时,系统会通过这个activity的生命周期的回调函数告知这个变化。当activity的状态变化时,比如当系统create(创建)它,停止(stop)它,复原(resume)它,或者销毁(destory)它,activity的几个生命周期的回调函数会被调用,在每个对应的回调函数中你可以执行相应的动作。比如当被停止时,你的activity应该释放大的对象,如网络或者数据库连接。当activity复原时,可以重新获得资源并复原中断的动作。这些状态的转换都是activity生命周期的一部分。

下面部分讲述了怎么创建并使用一个activity,包括activity的生命周期是怎么工作的,以便你能够正确的管理多个activity的状态转换。

Creating an Activity (创建一个Activity)

你可以通过创建一个activity的继承类来创建activity。在这个子类中,你需要实现相应的生命周期状态变化的回调函数,比如当activity被create(创建),stop(停止),resume(复原),销毁(destory)等。最重要的两个回调函数:

onCreate()

你必须在你的activity中实现这个函数,当系统创建你的activity时,会调用这个函数。在这个函数中,你必须初始化你activity中重要的组件,更重要的是,在这个函数中,必须调用setContentView()来定义这个activity的UI对应的layout。

onPause()

当用户离开当前的activity时 (仅仅是离开,不是销毁这个activity,这个activity还在后台栈中保存着),会调用当前activity的这个函数。在这个函数中,你应该保存相应的变化,因为用户可能不再返回了。

还有其它生命周期的回调函数,你可以使用这些回调函数提供给用户一个流畅的用户体验,并用这些回调函数来处理意外中断,因为这些这些中断可能导致你的activity被stop或者destory。这些函数在Managing the Activity Lifecycle中会讲到。

Implementing a user interface (实现一个UI)

UI是由一系列继承view类的类对象组成。每个view控制着activity窗口的一个特定的长方形区域并响应用户的动作,比如view可以是一个按钮,当用户单击这个按钮可以产生一系列其它动作。

Andoird 提供一些现成的view,你可以使用这些view来设计和组织你的layout。Widgets(小工具)里面的view提供了一些可视化的元素,比如按钮,文本区域,复选框或者图片。Layout里面的view从ViewGroup继承而来,这些view提供了独特的layout风格,比如linear (线性) layout, grid (网格)layout, relative (相对位置) layout。你也可以从这些类中派生出你自己的view或者viewgroup类。

最普遍的在layout中定义view的方法是通过编写在你的app中的resource的XML  layout文件。这种方法可以让你的UI设计与你的源代码隔开。你可以通过将你的UI layout的ID传入函数setContentView()来设置你activity对应的UI layout。另外,你也可以在你的activity 的代码中创建view,viewgroup,然后将这layout引进到原先传给setContentView()的layout中。

更过关于如何创建UI请参照User Interface 文档。

Declaring the activity in the manifest (在manifest中声明activity)

为了能让系统访问你的activity,你必须在manifest文件中声明你的activity。在mainfiest的<application>中添加<activity>元素。例如:

<manifest ... >
  <application ... >
      <activity android:name=".ExampleActivity" />
      ...
  </application ... >
  ...
</manifest >
在<activity>中你可以包含其他属性,比如activity的标题,图标等。在<activity>中,android:name这个属性是必须有的,它声明了这个activity的类名。一旦你发布你的app,这个名字是不能改的,一旦改了,你将损坏一些功能(详见Things That Cannot Change)。

更多关系<activity>详见<activity>部分。

Using intent filter (使用intent filter)

在<activity>中,可以通过<intent-filter>声明多个intent filter,intent filter是为了让其他app的组件可以调用当前的activity。

当你用android的sdk生成一个新的app时,系统会自动帮你生成一个如下的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>

其中的<action>指明这个activity是app启动的第一个activity,<category>表明这个activity应该在系统的app launcher的列表中。

如果你希望你的activity不被其他app的组件调用,那么可以不用<intent-filter>。在一个app中,只有一个activity的action为main和category为launcher。对于没有intent filter的activity,你可以通过显式的intent来启动这个activity。

当然,如果你想让其他app的组件能够启动你的activity,那你必须在你的activity中声明<intent-filter>。对于你想反应的intent,你必须声明对应的<action>,还可以声明相应的<category>, <data>元素。通过这些筛选出你的activity想接收的intent。

关于更多activity响应intent的信息请参照Intents and Intent Filters。

Starting an Activity (启动activity)

你可以通过往startActivity()函数传入一个Intent来启动其它的activity,在这个intent中,你可以指定你想要启动的确切的activity,也可以声明想要执行的动作(系统会根据这个动作来寻找符合的activity,哪怕这个activity在其他app中),在intent中可以携带一些数据用于其它activity。

在你的app中,你可以需要启动其它的activity,你可以通过显示的intent或者隐式的intent来启动相应的activity。下面代码启动了一个类名是SignInActivity的activity:

Intent intent = new Intent(this, SignInActivity.class);
startActivity(intent);
另一方面是你的app可能需要其它app的组件能完成的功能,比如发送邮件,短信等,这种情况下你的app可以借助其他app的组件来完成这些功能。通过隐式的intent可以实现这个功能(这也是为什么引进intent),你可以创建一个intent,在里面声明相应的动作,系统会根据这个intent寻找符合的activity来启动。若有多个符合条件的activity,系统会显示一个界面让用户选择,下面启动可以发送邮件的app组件:

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

在EXTRA_EMAIL中添加的是邮件的接收人地址数组。当某个邮件app响应这个intent,它会从这个数组中提取相应的接收人地址。在这种情况下,email应用被启动,当发完email后,原先的activity会复原。

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

有些时候你可能希望启动其它的activity并从启动的activity返回结果到原先的activity中,在这种情况下,你可以通过调用startActivityForResult()来启动activity,返回的结果在回调函数onActivityResult()的intent中。

比如在你的app中想让用户从联系人列表中选择一个联系人信息,在对这个联系人信息进行处理,可以通过下面代码实现:

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...
        }
    }
}
上面例子展示如何在onActivityResult()函数处理返回的结果,首先检查请求是否成功,即resultCode == RESULT_OK,然后检查返回的结果是否是想要的结果,即检查requestCode == PICK_CONTACT_REQUEST,如果都符合,那么就可以从返回的Intent中提取想要的结果。

在这里是通过ContentResolver来将返回的结果放到Cursor,可以通过这个Cursor读取想要的结果,关于这个的详细解答请参照Content Providers。

关于这部分的更多信息请参照Intent and Intent Filters文档。

Shutting Down an Activity (关闭activity)

可以通过调用函数finish()关闭当前activity,可以调用finishActivity()关闭之前打开的activity。

注意:在大多数的情况下,你最好不要直接调用这些函数来关闭activity。因为android系统会自动管理activity的生命周期。如果你直接调用这些函数,可能会相反的降低用户体验。只有你确定不想用户返回这些activity时,你才调用这些函数关闭相应的activity。

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

通过在相应的生命周期状态的回调函数实现相关代码是很重要的。activity的生命周期直接与其他activity,它本身的task(任务)和后台stack相关。

一个activity存在三种重要的状态:

Resumed (复原, running)

activity 在屏幕的前端并获得用户的focus。即运行中。

Paused (暂停)

其它的activity在前端并获得focus, 但还可以看到这个activity,即其它的activity在这个activity的前面,但是其它的activity是部分透明或者说没有占有整个屏幕。一个暂停的activity是完全存活的(activity对象扔存在内存中,而且它所有的状态和成员消息都被保存着,一直和window manager关联着),但当内存剩余很少时,会被系统销毁。

stopped (停止)

这个activity完全被其它的activity挡住(即这个activity在后台了)。一个被stopped的activity还是存活的(activity对象扔存在内存中,而且它所有的状态和成员消息都被保存着,但是没有跟window manager关联着)。此时这个activity对用户不可见,当系统需要内存时,这个activity可能会被系统销毁。


如果一个activity被paused或者stopped,系统会通过调用其finish()函数或者销毁它的进程来释放它占有的内存。当这个activity被重新打开时(即已经被finished或销毁时),只能重新创建这个activity(就当以前没发生过)。

Implementing the lifecycle callbacks (实现生命周期的回调函数)

当一个activity从一个状态转换到另外一个状态时,通过回调函数通知相应的转换。所有的回调函数在最初的activity类都有定义,所以在你的activity类中可以override(覆盖)这些函数。下面包含了所有的生命周期回调函数:

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

注意:在实现这些函数时,你都必须在函数开始时调用superclass(超类)中对应的函数。

这些函数定义了整个activity的生命周期,通过这些函数,你可以监视在activity生命周期存在的三种内置循环活动。

Entire lfietime (整个activity的生命过程): 从onCreate()到onDestory()。你的activity必须在onCreate()函数中设置好一些全局的状态(比如定义对应的layout),在onDestory()中释放所有剩下的资源。比如,你的activity有一个进程是在后台从网络上下载数据,那么你应该在onCreate()中创建这个进程,在onDestory()中停止这个进程。

Visible lifetime (可见的生命过程):从onStart()到onStop()。在这段时间内,用户可以在屏幕上看到这个activity并和它交互。比如,当一个新的activity被start(启动)后,这个activity的onStop()函数就会被调用。这onStart()被开始调用和onStop被调用之间,你可以维持需要的资源以便向用户展现这个activity,例如,你可以在onStart()中注册一个BroadcastReceiver来监视那些影响你UI的事件, 在onStop()中解除这个注册。系统可能在整个activity生命中会多次调用onStart()和onStop()函数,这是因为activity有可能会在对用户可见和不可见之间却换。

Foreground lifetime (位于屏幕前端生命过程):从onResume()到onPause()。在这段时间内,这个activity处于屏幕前端,并获得用户的focus(焦点),一个activity可以频繁的在前端和不是前端却换,比如,但设备待机时或弹出一个对话框时,系统会调用activity的onPause()函数。由于经常在这个过程中却换,也就是这两个函数会被经常调用,所以位于这两个函数的代码要尽可能少,不然影响却换速度和用户体验。

下图显示了一个activity可能的状态转换过程,长方形表示回调函数,你可以在这些回调函数执行想要的动作。


下表详细描述了每个回调函数在activity的整个生命周期如何工作,包括当回调函数被调用后,系统是否可以销毁当前的activity。


列“是否可销毁当前activity"表示当这个函数被调用后,系统是否会销毁这个activity所在的进程。其中有三个函数时Yes的(onPause(),onStop()和onDestory()),因为onPause()是这三个最先被调用的,一旦activity被创建,onPause()是能保证activity所在进程最销毁的最后一个调用的函数(即进程被销毁有可能不会调用onStop()和onDestory())。因此你应该在onPause()函数进行保存重要信息。当前应该保存那些比较重要的信息,这是为了不影响转到下一个activity的速度。

那些是No的函数保证了函数被调用之后进程不会被销毁。这里可以知道,一个activity从onPause()到onResume()被调用前是可能被销毁的,而activity只有在onPause()被调用之后才能被销毁。

注意:前面的No对应的函数表明调用这些函数,activity不会被销毁,但是特殊情况还是有可能会被系统销毁,比如当系统没有其他可用资源时。更多关系activity是否被销毁详见Processes and Threading 文档。

Saving activity state (保存activity状态)

Managing the Activity Lifecycle部分简单介绍了神马时候activity会被暂停或者停止,activity的状态是可以获得的,因为即便activity被暂停或者停止时,activity对象仍存在内存中,所有关于这个activity的成员和状态都存在。所以当activity回到前端时,所有用户直线做的改动都保持在activity中。

但是当系统为了回收内存而销毁一个activity时,这个activity对象就被销毁了,所以系统不能恢复原来的activity状态,当用户返回销毁的activity时,系统只能重新创建这个activity对象,而且此时的用户是不知道系统已经销毁了之前的activity并重新创建它(物是人非啊)。此时如果用户希望之前的activity跟以前的一样(比如我在发短信,突然来个电话,我当然希望我之前写的短信还在),肿么办?可以通过在回调函数onSaveInstaceState()中保存重要的信息。

系统在销毁activity的进程前会调用onSaveInstanceState()函数。在这个函数中传入一个Bundle对象,通过函数putString()或者putInt(),你可以在这个对象中保存一些名字-值的信息,当系统销毁这个activity后,要想重新创建它,系统会在创建activity的onCreate()函数及onRestoreInstanceState()传入你之前保存的信息。通过其中的一个函数,你就可以提前之前保存的信息并复原activity之前的状态。当没有信息在bundle时,bundle为null(当activity被第一次创建时,bundle为null).


注意:不能保证在activity被销毁前都会调用onSaveInstanceState(),因为有些时候不需要保存信息(比如当用户按下返回按钮时,这时候用户表明想关闭这个activity),系统会在onStop()钱或者可能在onPause()前调用onSaveInstaceState()。

即便你没有在onSaveInstanceState()实现相关的保存信息,系统也会在这个函数中自动地保存一些activity状态。特别的,默认地调用每个view的onSaveInstanceState()函数以判断这个view是否需要保存状态。几乎每个widget都实现这个函数,例如此时view的任何变化都会自动保存,并当activity被重新创建时都会恢复之前的内容。例如EditText都会保存输入在里面的内容,CheckBox会保存是否被选上。你唯一需要做的事提供一个需要保存状态的widget的ID。如果widget没有ID,那么系统将不能保存它的状态。

虽然onSaveInstanceState()默认会帮你保存你acitivy UI有用的信息,但你还是需要override(覆盖)它来添加额外的信息,在添加额外信息之前,你必须先调用超类的onSaveInstanceState()函数,类似的,你必须在onRestoreInstanceState()调用超类的onRestoreInstanceState()函数。

注意:因为onSaveInstanceState()不能保证被调用,你必须只用它来保存activity的状态转换信息,而不能用它来保存永久的数据。你必须在函数onPause()中保存那些永久的数据(比如必须保存到数据库中的数据)。

一个测试你app是否能够恢复到原先状态的办法是将你的设备旋转,当屏幕旋转是,系统会销毁并重新创建当前的activity,以便使用那些适合屏幕尺寸的资源。从这一点可以看出,你的activity保存它的状态时非常有必要的,因为用户有可能会不时的翻转屏幕。

Handling configuration changes(处理配置变化)

一些设备的配置有可能会在运行时变化(比如屏幕的方向,键盘是否可用,语言等)。当发生这些变化时,android会重新创建当前运行的activity(系统会调用onDestory(),然后马上调用onCreate())。这是为了加载那些更适合当前配置的资源。

如果你合理的设计你的activity来处理因为屏幕方向变化而restart (重新启动),恢复之前的activity状态,那么你的app将会对其他不突发事件更鲁棒。

最好的方式是通过onSaveInstanceState()和onRestoreInstanceState()来恢复因重新启动的activity 状态。

关于更多在运行时配置变化和怎么处理这些变化请参照Handling Runtime Changes.

Coordinating activities (协调activities)

当一个activity启动其他activity时,这两个activity都发生了状态变化。第一个activity暂停并停止(如果还能在屏幕看到,是不会被停止的),第二个activity被创建。当这两个activity共享保存在磁盘的数据时,重要的一点是知道在第二个activity被创建之前,第一个activity不是完全的被停止。所以启动第二个activity的进程回个关闭第一个activity的进程重叠。

回调函数被调用的顺序是已经定义好的,特别当在同一个进程内两个线程之间互相启动。下面是Activity A 启动activity的过程:

1. Activity A的onPause()函数被调用。

2. Activity B的onCreate(), onStart(), onResume()函数按顺序执行(activity B拥有用户focus(焦点))。

3. 如果activity A在屏幕上不可见,activity A的onStop()被调用。

这些可以预见的回调函数的执行顺序可以帮助你更好的管理不同activity的信息床底。例如,你必须在第一个activity停止前而写入信息到数据库中,这样第二个activity就可以读取这些信息,那么你必须在第一个activity的onPause()函数中写入数据库,而不是在onStop()。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值