一、Activity创建、启动和关闭
1.创建 Activity
2.在清单文件中声明
3.启动
4.结束
二、Activity生命周期
三、Activity处理配置变更等问题
Activity创建、启动和关闭
Activity是一个应用组件,用户可与其提供的屏幕进行交互,以执行拨打电话、拍摄照片、发送电子邮件或查看地图等操作。 每个Activity都会获得一个用于绘制其用户界面的窗口。窗口通常会充满屏幕,但也可小于屏幕并浮动在其他窗口之上。
1.创建Activity
要创建Activity,必须先创建Activity的子类(或使用其现有子类)。需要在子类中实现Activity在其生命周期的各种状态之间转变时(例如创建Activity、停止Activity、恢复Activity或销毁Activity时)系统调用的回调方法。 两个最重要的回调方法是:
- onCreate(),必须实现此方法,系统会在创建Activity时调用此方法。在实现该方法时,应该初始化Activity的必需组件。 最重要的是,必须在此方法内调用setContentView(),以定义Activity用户界面的布局。
- onPause(),系统将此方法作为用户离开Activity的第一个信号(但并不总是意味着 Activity 会被销毁)进行调用。 在此方法内,通常应该确认当前用户会话结束后仍然有效的任何更改(因为用户可能不会返回)。
2.在清单文件中声明Activity
(1)在清单文件中声明了Activity,系统才能访问它。 要声明Activity,则需打开清单文件,并将 <activity/> 元素添加为<application/> 元素的子项。例如:
<manifest ... >
<application ... >
<activity android:name=".ExampleActivity" />
...
</application ... >
...
</manifest >
您还可以在此元素中加入几个其他特性,以定义Activity标签、Activity图标或风格主题等用于设置Activity UI风格的属性。android:name特性是唯一的必需特性—它指定。
(2)使用 Intent 过滤器
元素 <activity/> 还可指定各种Intent过滤器—使用<Intent-filter/>元素—以声明其他应用组件激活它的方法。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具有“main”和“launcher”
类别。 如果不想提供给其他应用的Activity不应有任何Intent过滤器,可以利用显式 Intent 自行启动它们。不过,如果想让 Activity对衍生自其他应用(以及您的自有应用)的隐式Intent作出响应,则必须为Activity定义其他 Intent 过滤器。 对于您想要作出响应的每一个 Intent类型,都必须加入相应的<Intent-filter/>,其中包括一个<action/>元素,还可选择性地包括一个<category/>元素和/或一个<data/>元素。这些元素指定您的Activity可以响应的Intent类型。
3.启动 Activity
(1)调用startActivity(),并将其传给想启动Activity的Intent来启动另一个Activity
Intent对象会指定您想启动的具体Activity或描述您想执行的操作类型(系统会为您选择合适的Activity,甚至是来自其他应用的Activity)。 Intent 对象还可能携带少量供所启动Activity使用的数据。
例如,可以通过以下代码让一个Activity启动另一个已知为SignInActivity:
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会恢复执行。
(2)调用 [startActivityForResult()](https://developer.android.com/reference/android/app/Activity.html#startActivityForResult(android.content.Intent, int)),启动 Activity 以获得结果,重写 [onActivityResult()](https://developer.android.com/reference/android/app/Activity.html#onActivityResult(int, int, android.content.Intent))方法,就可以得到被启动Activity的返回结果。
干货:彻底搞懂 startActivityForResult 在 FragmentActivity和 Fragment中的异同
例:您可能希望用户选取其中一位联系人,以便您的Activity对该联系人中的信息执行某项操作。 代码实例如下:
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,让查询的数据能够被读取。如需了解详细信息,参阅内容提供程序。
(3)由以上两种方式可看到启动Activity时都会传入相应的Itent,根据Intent的不同又可以将Activity的启动方式分为显示启动和隐式启动
- 显示启动:按名称(完全限定类名)指定要启动的组件。通常在自己的应用中使用显式Intent来启动组件,这是因为此时知道要启动的 Activity 或服务的类名。例如,启动新Activity以响应用户操作,或者启动服务以在后台下载文件。如果您没有为Activity声明任何Intent过滤器,则Activity只能通过显式Intent启动。
- 隐式启动:不会指定特定的组件,而是声明要执行的常规操作,配置Intent的action、data、category等,从而允许其他应用中的组件来处理它。例如,如需在地图上向用户显示位置,则可以使用隐式Intent,请求另一具有此功能的应用在地图上显示指定的位置。
隐式Intent相对于显示Intent具有以下优势: - 可以通过url传web数据(存在数据泄漏)
- 可以实现解耦(如Activity之前的依赖)
- 可以在一个Application启动另一个Application
- 可以通过配置Intent过滤器,使多个应用响应,然后根据用户选择启动具体的应用
4.结束 Activity
调用Activity的finish()方法来结束该Activity,还可以通过调用finishActivity()结束在之前启动的另一个Activity。
注意:在大多数情况下,不应使用这些方法显式结束 Activity,Android系统会为您管理Activity的生命周期,因此您无需完成自己的 Activity。 调用这些方法可能对预期的用户体验产生不良影响,因此只应在您确实不想让用户返回此Activity实例时使用。
Activity生命周期
1.Activity的三种存在状态
- 已继续:此Activity位于屏幕前台并具有用户焦点。(有时也将此状态称作“运行中”)
- 已暂停:此Activity失去焦点,另一个Activity位于屏幕前台并具有用户焦点,但此Activity仍可见。也就是说,另一个Activity显示在此Activity上方,该Activity部分透明或未覆盖整个屏幕(例如跳转到一个Dialog Activity,非Dialog)。已暂停的Activity处于完全Activity 状态(Activity 对象保留在内存中,它保留了所有状态和成员信息,并与窗口管理器保持连接),但在内存极度不足的情况下,可能会被系统终止。
-
已停止:该 Activity被另一个Activity完全遮盖(该Activity目前位于“后台”)。 已停止的Activity同样仍处于Activity状态(Activity对象保留在内存中,它保留了所有状态和成员信息,但未与窗口管理器连接)。 不过,它对用户不再可见,在他处需要内存时可能会被系统终止。
Activity 的存在状态
如果Activity处于暂停或停止状态,系统可调用其 finish() 方法或直接终止其进程,将其从内存中删除。(将其结束或终止后)再次打开Activity时,必须重建。
2.Activity生命周期图
Activity的生命周期对 Activity生命周期图中的主要方法介绍如下:
(1)onCreate()
- 初始化,准备数据,创建view setContentView、findViewById
- 一生一次
- 不可见
- 不适合动画,不适合耗时,影响用户看到界面的时间
(2)onStart()
- 初始化或恢复中,onCreate或onRestart之后,和onStop是一对儿
- 多次调用,Activity即将可见
(3)onResume()
- 初始化或恢复中,恢复activity时一定会调用,和onPause是一对儿
- 多次调用,可以交互,Activitty 进入active 状态
- 执行动画、唤起相机、刷新数据
(4)onPause()
- 失去焦点、不可触摸,所以和onResume是一对儿
- 可见
- 下一个Activity可以进行初始化工作
(5)onStop()
- 完全不可见,进入后台
(6)onDestory()
- go die
(7)onPause() vs onStop()
- 不可操作 vs 不可见
其中,onCreate()、onStart()、onResume()可用于初始化,onPause()、onStop()、onDestory()不可用于初始化。
(8)Activity存在状态与生命周期各个方法对应关系如图3所示:
图3 Activity存在状态与生命周期各方法对应关系
例:当一个 Activity A启动另一个 ActivityB时,各自的生命周期转变情况
- Activity A的onPause()方法执行
- Activity B的onCreate()、onStart() 和 onResume()方法依次执行(Activity B现在具有用户焦点)
- 然后,如果Activity A在屏幕上不再可见,则其onStop()方法执行Activity A暂停并停止(但如果它在后台仍然可见,则不会停止)时,系统会创建 ActivityB。 如果这些Activity共用保存到磁盘或其他地方的数据,必须了解的是,在创建第Activity B前,Activity A不会完全停止。更确切地说,启动Activity B的过程与停止Activity A的过程存在重叠。
Activity A暂停并停止(但如果它在后台仍然可见,则不会停止)时,系统会创建Activity B。 如果这些Activity共用保存到磁盘或其他地方的数据,必须了解的是,在创建第Activity B前,Activity A不会完全停止。更确切地说,启动 Activity B的过程与停止 Activity A的过程存在重叠。
Activity状态保存与数据保存
1.Activity状态保存
当Activity暂停或停止时,Activity的状态会得到保留。 因为当Activity暂停或停止时,Activity对象仍保留在内存中 — 有关其成员和当前状态的所有信息仍处于Activity状态。 因此,用户在Activity内所做的任何更改都会得到保留,这样一来,当Activity返回前台(当它“继续”)时,这些更改仍然存在。不过,当系统为了恢复内存而销毁某项Activity时,Activity对象也会被销毁,因此系统在继续Activity时根本无法让其状态保持完好,而是必须在用户返回Activity时重建Activity对象。但用户并不知道系统销毁 Activity 后又对其进行了重建,因此他们很可能认为Activity状态毫无变化。
在这种情况下,您可以使用onSaveInstanceState()回调方法对有关 Activity 状态的信息进行保存,以确保有关 Activity 状态的重要信息得到保留。系统会先调用 onSaveInstanceState(),然后再使 Activity 变得易于销毁,系统会向该方法传递一个 Bundle,您可以在其中使用 putString() 和putInt() 等方法以名称-值对形式保存有关 Activity 状态的信息。然后,如果系统终止您的应用进程,并且用户返回您的 Activity,则系统会重建该 Activity,并将 Bundle 同时传递给 onCreate() 和 onRestoreInstanceState()。您可以使用上述任一方法从 Bundle 提取您保存的状态并恢复该 Activity 状态。如果没有状态信息需要恢复,则传递给您的 Bundle 是空值(如果是首次创建该 Activity,就会出现这种情况)。
由于onSaveInstanceState 的默认实现有助于保存 UI 的状态, 因此如果您为了保存更多状态信息而重写该方法,应始终先调用 onSaveInstanceState 的超类实现,然后再执行任何操作。同样,如果您替代onRestoreInstanceState 方法,也应调用它的超类实现,以便默认实现能够恢复视图状态。
例:模拟调用onSaveInstanceState()、onRestoreInstanceState()场景
(1)运行你的程序,当程序打开时,按HOME键,这时系统会调用onSaveInstanceState方法,注意:这个方法的调用是系统决定的,不是软件或其他什么因素,系统觉得有可能在某个时间因内存不足等因素而Kill掉你,所以给你个机会让你现在先利用这个方法保存下数据,所以调用onSaveInstanceState()。
(2)一般情况下,即使你在onSaveInstanceState保存了数据,在系统没Kill掉程序的情况下,你再回到刚关闭的界面,你也会感觉刚才调用onSaveInstanceState方法保存的数据没什么作用,只有在系统kill掉程序的情况下,再回到刚关闭的界面,回调了onRestoreInstanceState方法,这时onSaveInstanceState方法保存的数据,才发挥真正的作用,如何重现这种场景呢,利用DDMS替系统干这件坏事,kill掉你的程序:在按下HOME键后,系统已经调用你的onSaveInstanceState方法,打开DDMS找到你的程序进程,stop你的进程,再打开程序!
注意:无法保证系统会在销毁您的Activity前调用 onSaveInstanceState(),因为存在不需要保存状态的情况(例如用户使用“返回” 按钮离开您的Activity时,因为用户的行为是在显式关闭 Activity)。 如果系统调用 onSaveInstanceState(),它会在调用 onStop() 之前,并且可能会在调用onPause() 之前进行调用。
2.Activity数据保存问题
(1)由于无法保证系统会调用 onSaveInstanceState(),因此您只应利用它来记录Activity的瞬态(UI 的状态)—切勿使用它来存储持久性数据,而应使用onPause()在用户离开Activity后保存持久性数据(如应保存到数据库的数据)。如果您必须在第一个Activity停止时向数据库写入数据,以便下一个 Activity 能够读取该数据,则应在onPause()而不是onStop() 执行期间向数据库写入数据。
(2)如果从Activity A通过Itent携带数据打开Activity B,Activity B界面被系统kill后,重新创建Activity B之后,之前携带的数据能被还原,但该界面被kill之前对传递过来的数据作任何修改都作废;
(3)B界面→C界面,C界面 finish后,系统重建B界面,依然能得到C界面回传的数据。例如Activity B打开Activity C,用 startActivityForResult()方法要求Activity C finish时回传数据到Activity B,跳转到Activity C后Activity B被kill了,当系统在Activity C finish后重建Activity B,onActivityResult()方法依然能收到Activity C传回的数据;
(4)从A界面-->B界面-->C界面,用A界面通过Intent传过来的数据,即使B界面到C界面后B界面被杀了,再回到B界面,B界面还是拿得到A界面传过来的数据,但还是原始Intent的数据,如果B界面在被杀之前对Intent数据加工,通过onSaveInstanceState存储,通过onRestoreInstanceState统一处理了,那么就要绕过之前A界面通过Intent传过来的原始数据的干扰。
Activity处理配置变更等问题
程序在运行时,一些设备的配置可能会改变,如:横竖屏的切换、键盘的可用性或语言的切换等,这样的事情已发生,Activity会重新启动。其中的过程是:在销毁之前会先调用onSaveInstancestate()去保存应用中的一些数据,然后调用 onDestory(),最后才会去调用onCreate()或者onRestoreInstanceState方法重新启动Activiy。当在Android的Manifest.xml定义了android:configchange属性之后就不会去重新启动Activity,而是通知程序去调用onConfigurationChange()函数。例如,在切换语言之后会重新启动Activity,定义这个属性之后就不会重新启动Activity。其可以设置多个属性,中间用“|”隔开。
对android:configChanges属性,一般认为有以下几点(横竖屏切换):
- 不设置Activity的android:configChanges时,切屏会重新调用各个生命周期,切横屏时会执行一次,切竖屏时会执行两次;
- 设置Activity的android:configChanges="orientation"时,切屏还是会重新调用各个生命周期,切横、竖屏时只会执行一次;
- 设置Activity的android:configChanges="orientation|keyboardHidden"时,切屏不会重新调用各个生命周期,只会执行onConfigurationChanged方法,在其中可获取切换后的横竖屏参数或者进行一些其他操作。
参考资料:
http://blog.csdn.net/liuhe688/article/details/6754323
http://droidyue.com/blog/2015/08/16/dive-into-android-activity-launchmode/index.html
网友评论