2.2 Activities
Activities 一个activity就是一个应用程序的组件,它可以提供一个用户交互的屏幕让用户进行某些操作,例如打电话,照相,发送邮件或者查看地图。每一个activity都会给用户展示一个窗口。这些窗口一般都会充满整个屏幕,但是也有可能比屏幕小一点,悬浮在别的
Activities
一个activity就是一个应用程序的组件,它可以提供一个用户交互的屏幕让用户进行某些操作,例如打电话,照相,发送邮件或者查看地图。每一个activity都会给用户展示一个窗口。这些窗口一般都会充满整个屏幕,但是也有可能比屏幕小一点,悬浮在别的窗口之上。
一个应用程序通常包括多个activity,并且这些activity都有松散的相互联系。典型的是可以在应用程序中特别定义一个activity让它作为“Main”activity,在首次打开该应用程序的时候都展现这个activity给用户。每个activity都可以启动另一个activity来执行不同的操作。每次新的activity启动时,前一个activity就会停止,但是系统会将前一个activity保护在栈(the “back stack”)中。当一个新的activity启动的时候,它会被推到back stack中同时引起用户的注意。这个backstack遵循基本的“后进先出”的栈机制,所以当用户完成当前的activity后,按“返回”键,当前activity会从栈中被抛出(且销毁),然后前一个activity恢复。(back stack会在Tasks and Back Stack文档中讲述更多内容)
一个activity停止是因为新的activity启动,该activity的状态变化是通过activity生命周期回调方法得知的。一个activity可能会因为其状态改变获得几个回调方法——当系统创建,停止,恢复,或者是销毁它的时候,且每一个回调方法在状态改变的适当时候提供一个执行特殊动作的机会。例如,当停止的时候,你的activity应该释放任何一个大型对象,像是网络连接或者数据库连接。当activity恢复的时候,你可以请求必需的资源和恢复被打断的action。这些状态的改变都是activity生命周期的一部分。
下面讨论的是如何建立和使用一个activity,包括完整的activity生命周期是如何工作的,这样你可以管理不同的activity的状态改变。
Creating an Activity
为了建立一个activity,你必须创建一个Activity的子类(或者一个已经存在的Activity的子类)。在你的子类中,你必须实现当activity在其生命周期中各种状态间切换时系统调用的回调方法,像是activity创建,停止,恢复或者是销毁时。最重要的两个回调方法是:
onCreate ()
你必须实现这个方法。系统会在创建这个activity的时候调用该方法。在这个方法中,你必须初始化你的activity需要的基本元件。更重要的是,你必须在这个方法中调用setContentView()方法声明这个activity用户界面的布局。
onPause ()
当用户离开你的activity的时候(尽管它并不总意味着这个activity被摧毁),系统会调用这个方法作为第一指令。这通常是你应该提交当离开当前页面时,应该被保持修改的方法。
所有生命周期的方法请看Managingthe Activity Lifecycle。
Implementing a user interface
一个Activity用户界面是由有层次的许多view提供的——由View类派生的许多对象。在activity窗口中每个view控制着独特的方形空间,且对用户的交互产生反应。例如,一个view可能是一个按钮,当用户点击它的时候会初始化一个action。
Android提供许多已经准备好的view供你使用。“Widget”是许多views的集合,它为屏幕提供许多可视化(同时是可交互的)元素,例如按钮,输入框,单选框,或仅仅是一张图片。“Layout”是从ViewGroup衍生出的view,为它的子类提供独特的布局模型,例如linear layout, grid layout或者relative layout。你也可以创建View或ViewGroup的子类来实现你自己的widget和layout并应用到你的activity布局上。
最通常定义布局的方式是在你的应用程序资源中使用xml文件。通过这种方式,你可以将代码和布局设计分离开,更利于维护。你可以通过在方法setContentView()设置该布局的资源ID初始化activity布局。当然也可以用代码实现布局,通过向一个ViewGroup中插入新的view,然后再将这个ViewGroup传递到方法setContentView()中。
更多关于创建用户界面的信息,参照User Interface文档。
Declaring the activity in the manifest
为了使activity可被系统方法,你必须在manifest文件中描述你的activity,如下:
…
ThingsThan cannot Change)
Using intent filters
一个
如果你使用AndroidSDK工具来创建一个新的应用程序,它会自动在创建一个activity作为Main Activity并且在manifest文件中添加intent filter,例如:
Starting an Activity
你通过调用方法startActivity()启动另一个activity,但是要传递一个Intent(描述了你想要启动的activity)作为参数。这个intent或者声明一个你想要启动的显式的activity或者描述了你想要执行的action(然后系统为你会选择相应的activity,这个activity可能是来自别的应用程序)。
例如下面的例子,使用显式方法打开一个叫做SignInActivity:
Intentintent=new Intent (this, SignInActivity.class);
startActivity(intent);
另外,当你的activity想要执行一些行为的时候,例如发送邮件,短信,可以调用设备上的其他具有该功能的activity。这时候就凸显intent的重要性了——你可以创建intent并在其中描述你希望执行的动作,然后系统会帮你选择合适的activity来启动。如果有多个activity都可以处理这个intent,则用户可以选择其中之一来启动它。例如,你希望允许用户发送一封邮件,你可以在activity中如下创建intent:
Intentintent=new Intent (Intent.ACTION_SEND);
intent.putExtra(Intent.EXTRA_EMAIL, recipientArray);
startActivity(intent);
EXTRA_EMAIL向intent添加了需要发送的email地址的stringarray。当email应用程序对这个intent相应时,它会读取extra中的string array并将他们放在“收件人”一栏。在这个情况下,email应用程序的activity被启动,而当用户完成操作后,你的activity会恢复。
Starting an activity for a result
有时候,你希望从你启动的activity中获取结果。在这种情况下,需要调用方法startActivityForResult()来启动activity(而不是startActivity())。对于从接下来的这个activity获取结果,需要实施回调方法onActivityResult()。当接下来的这个activity完成后,它会在Intent中返回一个结果传递给onActivityResult()。
例如,你希望用户选择联系人中的一个,这样你的activity就可以获得并处理该联系人的信息。下面是你如何创建intent并处理结果的:
privatevoid 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
protectedvoid onActivityResult (int requestCode, int resultCode, Intent data){
//If the request went well (OK) and therequest was PICK_CONTACT_REQUEST
If (resultCode == Activity.RESULT_OK&&
requestCode ==PICK_CONTACT_REQUEST) {
//Perform a query to the contact’scontent provider for the contact’s name
Cursor cursor=getContentResolver ().query(data.getData (),
new String []{Contacts.DISPLAY_NAME},null, null,null);
if (cursor.moveToFirst ()){//Trueif the cursor is not empty
intcolumnIndex=cursor.getColumnIndex (Contacts.DISPLAY_NAME);
Stringname=cursor.getString (columnIndex);
//Do something with theselected contact’s name
}
}
}
这个例子展示了你如何使用onActivityResult()方法的逻辑,这个方法可以处理一个activity的返回结果。第一个条件是检查请求是否成功——如果成功,则resultCode的值会是RESULT_OK——以及请求得到的结果是否为已知结果——在这个例子中,requestCode匹配的第二个参数会发送给startActivityForResult()。代码通过查询返回在intent中的数据处理activity的结果。(data参数)
ContentResolver对content provider执行了查询,结果返回了一个Cursor。该cursor允许该被查询的数据是可读的。更多信息参考Content Providers文档。
Shutting Down an Activity
可以通过调用activity本身的方法finish()来关闭activity。也可以关闭别的activity通过调用finishActivity()。
Note:在大多数情况下不需要调用这个方法,因为之前提到的android系统会为你处理一个activity的生命周期。这个方法仅仅在你不希望用户再返回这个activity时调用。
Managing theActivity Lifecycle
通过回调方法来管理你的activity的生命周期对于创建一个强壮而坚韧的应用程序来说非常重要。一个activity的生命周期直接影响到其相关联的activity,以及它的任务和它的back stack。
一个activity在本质上以三种状态存在:
Resumed
该activity在屏幕的前台(理解为“运行中“即可)
Paused
另一个activity在前台运行,获得用户的焦点,但是该activity依然可见。就是说,另一个activity在该activity的顶部且可见,即运行中的activity是部分透明的或者并没有覆盖整个屏幕。一个暂停的activity是完全alive的(Activity对象依然保留在内存中,它保持所有的声明和成员的信息,依然依附在windowmanager上/theActivity objects retained in memory, it maintains all state and memberinformation, and remains attached to the window manager),但是当内存极低的时候系统会关掉这个activity。
Stopped
该activity被其他的activity完全遮盖(此时该activity在后台)。一个停止的activity依然是alive的(Activity对象依然保留在内存中,它保持所有的声明和成员的信息,但是不依附在windowmanager上/the Activityobjects retained in memory, it maintains all state and member information, butis not attached to the window manager)。然而,它不再对用户可见同时在其他地方需要内存的时候会被系统关掉。
当一个activity处于暂停或者停止的状态时,系统可以通过两种方法把它从内存中抛出。一种是要求它自己结束(调用它的finish()方法),或者简单的关闭它的线程即可。当这个activity再次打开时(在被结束或关闭之后),它必须被重新创建。
Implementing thelifecycle callbacks
当一个activity在上述的状态之间切换时,系统会通过几种回调方法通知它。所有的回调方法都是hooks(what??),你可以重写它们当你的activity状态改变的时候可以执行相应的功能。下面的框架就是在activity基本生命周期中可能会调用到的方法
publicclass ExampleActivity extends Activity {
@Override
public void onCreate (BundlesavedInstanceState) {
super.onCreate(savedInstanceState);
//The activity is being created.
}
@Override
protected void onStart () {
super.onStart ();
//The activity is about to becomevisible.
}
@Override
protected void onResume () {
super.onResume ();
//The activity has become visible(it is “resume”).
}
@Override
protected void onPause () {
super.onPause ();
//Another activity is taking focus(this activity is about to be “pause”)
}
@Override
protected void onStop () {
super.onStop ();
//The activity is no longervisible (it is now “stopped”).
}
@Override
protected void onDestroy () {
super.onDestroy ();
//The activity is about to bedestroyed.
}
}
Note:在实施这些回调方法的时,在执行任何动作之前要调用其父类方法。正如上面所示。
总的来说,这些方法定义了一个activity的整个生命周期。通过实施这些方法,你可以监控activity声明周期中的三个嵌套循环(nest loop):
Entire lifetime
完整的生命时间发生在调用onCreate()方法和调用onDestroy()方法之间。你的activity应该在onCreate()中设置 ”global” 状态,同时在onDestroy()中释放所有持有的资源。例如,如果你的activity在后台有一个线程从网络上下载数据,则该线程应该在onCreate()中被创建,同时在onDestroy()方法中被停止。
Visible lifetime
可见生命时间发生在调用onStart()方法和调用onStop()方法之间。在此期间,用户可以在屏幕上看见该activity,同时可以与其交互。例如,调用onStop()方法后另一个activity启动,而这个activity则不再可见。在这两个方法之间,你可以保持该activity中需要展示给用户的资源。例如,你可以在onStart()方法中注册BroadcastReceiver监控可能影响你的UI的改变,在onStop()方法中注销它(因为用户对当前activity不再可见,也不需要监控UI的变化了)。在一个activity的entire lifetime中,onStart()和onStop()可能会被多次调用。(当activity在可视/隐藏状态间切换)
Foreground lifetime
这个生命时间发生在调用方法onResume()和onPause()之间。在这段期间,该activity处于所有activity的前端并且获得用户的输入焦点。一个activity可以在频繁的过渡进入或退出前台——例如,onPause()方法在设备准备休眠或对话框出现的时候调用。因为这个状态(foreground)可能会被频繁的切换,这两个方法的代码都应该是轻量级(fairlylightweight)的,为了避免切换速度慢导致用户等待。
下面就是activity的在不同状态之间切换调用的回调方法。
下表总结了activity生命周期的回调方法
Method |
Description |
Killable after? |
Next |
||
onCreate() |
当activity第一次被创建的时候调用,在这个方法中你可以做所有基本常规步骤——创建views,绑定数据到list等。这个方法会传递一个Bundle对象,包含了这个activity之前的状态。(参照Saving Activity State),随后大多是调用onStart() |
No |
onStart() |
||
|
onRestart() |
在activity被停止后调用,只是优先让该activity重新启动,随后总是调用onStart() |
No |
onStart() |
|
|
onStart() |
在activity准备对用户可视化前调用。如果activity进入前端,随后调用onResume(),当它变成隐藏状态时随后调用onStop() |
No |
onResume() or onStop() |
|
|
|
onResume() |
在activity开始与用户交互的时候调用,在这时activity处于activity栈的顶端。后面总跟着onPause() |
No |
onPause() |
|
|
onPause() |
当系统准备启动别的activity的时候调用。这个方法典型用于提交未保存的持久性数据的变化,停止动画以及其他可能消耗内存的事务。它应该尽可能的快速执行,因为下一个activity会被运行。后面或者调用onResume()使当前activity重启,也可能调用onStop()当它对用户不可见时。 |
Yes |
onResume() or onStop() |
|
onStop() |
当activity对用户不再可见时调用。当它正在被销毁或者另一个activity(已经存在的或者新建的)正在运行且覆盖它时调用。接下来或者调用onRestart()使activity返回与用户交互或者调用onDestroy()销毁 |
Yes |
onRestart() or onDestroy() |
|
onDestroy() |
当activity被销毁的时候调用。这是activity接收到的最后一个调用。当activity结束时(调用finish())或者系统临时摧毁了这个activity实例的保存空间时调用。你可以用方法isFinishing()判断这两种情况。 |
Yes |
nothing |
Note:Killable after这个状态只会发生在没有其他资源可用的极端情况。我们会在文档Processes and Threading中讨论更多activity被杀掉的情况。
Saving activitystate
当activity处于暂停或者停止状态的时候,activity对象依然被保留在内存中——它所有成员的信息和当前状态都是alive的。这样,用户操作对activity造成的改变就会保留,所以当activity返回前端的时候,这些改变依然存在。
当系统为了回收内存销毁一个activity时,该activity对象也会被销毁。所以系统无法简单的在保证其原封不动的情况下恢复这个activity。取而代之的是,当用户通过导航回这个activity时,系统必须重新创建这个activity对象。用户并不清楚系统摧毁了这个activity然后又重建了它,但用户可能期望该activity就是原来的那个。(插播:就是说用户希望不惯自己怎么折腾,activity就是原来的那个,希望它并没有被新建)在这个情况下,你可以通过添加一个回调方法来确保activity状态的重要信息被保存下来。这个方法是onSaveInstanceState(),它允许你保存你的activity的状态。
系统在使activity容易受到破坏之前调用onSaveInstanceState()。系统给这个方法传递了一个Bundle,使用方法类似putString()和putInt()你可以往Bundle中存放activity的状态信息,类似name-value pairs。然后,如果系统杀掉了你的应用程序线程而用户又通过导航返回这个activity,系统会重新创建这个activity同时将原先的Bundle值传递给onCreate()和onRestoreInstanceState()。使用前面描述的两个方法之一,你可以从Bundle中取出你保存的状态,然后还原activity的状态。如果没有需要恢复的状态信息,则Bundle会传递null给你。(这就是你第一次创建activity时的情况)
图2:一个activity以两种方式给用户展现一个原封不动的状态:一种情况是activity被摧毁,然后重建,此时activity必须恢复之前保存的状态,另一种情况是activity被停止,然后再次运行,此时activity的状态依然完整。
Note:并不保证在你的activity被摧毁之前onSaveInstanceState()一定会被调用,因为在很多情况下并不需要保存状态(例如当用户使用“返回“键离开你的activity,这时用户已经明确的想要关闭这个activity,此时不需要再保存状态了)。如果系统调用了onSaveInstanceState(),那么这一定发生在onStop()之前,也有可能发生在onPause()之前。
然而,即使你什么也没有做,也没有实现onSaveInstanceState(),一些activity状态依然会被Activity类默认执行的onSaveInstanceState()恢复。Specifically, thedefault implementation calls the correspondingonSaveInstanceState() method for every View in the layout,which allows each view to provide information about itself that should besaved. 几乎每个android框架中的widget都会酌情执行这个方法,如此任何UI可视化的改变可以被自动保存,在你的activity被重新创建的时候可以被恢复。例如,EditTextwidget保存用户输入的任何文本,CheckBox widget保存是否选择某些选择框。唯一需要你做的工作是给每个你希望保存状态的widget提供一个独特的ID(通过android:id属性)。如果一个widget没有ID,则系统不能保存它的状态。
尽管默认执行onSaveInstanceState()能保存你的activity UI的有用信息,你可能仍然需要重写这个方法来添加一些新的信息。例如,你可能需要保存在activity生命期间某些成员值的改变(这些成员可能与UI中被恢复的值关联,但是这些持有UI值的成员在默认情况下不会被恢复)
因为默认执行onSaveInstanceState()可以帮助保存UI的状态,如果你为了保存添加的状态信息而重写这个方法,你应该在做任何事之前调用其父类的该方法。同样的,重写方法onRestoreInstanceState()同样要调用其父类的该方法。
Note:因为onSaveInstanceState()并不保证一定被调用,所以该方法应该只用作记录activity的状态改变(UI的状态)——它永远不应该用来保存持久性数据。取而代之,当用户离开页面的时候,你可以在onPause()方法中保存持久性数据(例如应该保存入数据库的数据)。
测试你的应用程序保存其状态的能力的方法很简单,旋转你的设备,屏幕方向就会改变。当屏幕方向改变的时候,系统会为了使可选择资源能应用在新的屏幕配置上摧毁并重建activity。因为这个原因,你的activity在被重建的时候可以完全恢复其状态非常重要,因为用户在使用应用程序的时候可能频繁的旋转屏幕。
Handlingconfiguration changes
一些设备的配置可以在运行期间被改变(例如屏幕方向,键盘有效性和语言功能)。当改变发生的时候,Android会重建一个运行中的activity(系统调用onDestroy(),然后立即调用onCreate())。这个行为通过自动重新下载你的app中可选择的资源,可以帮助你的应用程序调整以适应新的配置。(例如针对不同的屏幕方向和大小可以有不同的页面布局)
如果合理的设计你的activity,让它根据屏幕方向的改变来处理一个重启事件,如上述一样恢复activity状态,你的app可以更具弹性的处理在activity生命周期中发生的其他意外事件。(即使用onSaveInstanceState()和onRestoreInstanceState()/onCreate())
更多关于运行期配置改变以及如何处理它们的信息,请阅读Handling Runtime Changes文档。
Coordinatingactivities
当一个activity启动另一个activity的时候,二者的生命周期都改变了。第一个暂停和停止(如果它在背景中依然可见,则不停止),而另一个则被创建。在这种情况下这两个activity共享数据且保存到硬盘或者别处,要注意此时第一个activity并不会在第二个activity被创建好之前完全停止。开启第二个activity的线程会与停止第一个activity的线程重叠。
到目前为止,生命周期回调的命令已经很清楚了,特别是当两个activity处在同一个线程中且一个activity打开另一个的时候。下面就是ActivityA启动Activity B的操作指令:
1. ActivityA调用onPause()
2. ActivityB按顺序调用onCreate(),onStart()和onResume()(此时Activity B获得用户焦点)
3. 然后,如果Activity A不再可见,则调用onStop()
这种可预见的生命周期回调顺序使得你可以控制从一个activity到另一个activity的状态改变信息。例如,如果你必须在第一个activity停止的时候向数据库中写入数据并且让接下来的这个activity能从数据库中读到数据,那么你应该在onPause()中向数据库中写入数据而非在方法onStop()中。
上一篇: 长期使用中型Access数据库的一点经验_MySQL
下一篇: 追踪DBCA建库log
推荐阅读
-
浅谈从ASP.NET Core2.2到3.0你可能会遇到这些问题
-
windows下PHP_intl.dll正确配置方法(apache2.2+php5.3.5)
-
.Net Core2.2 WebApi上传文件
-
Android 1.5 1.6 2.0 2.1 2.2 的区别详解
-
windows下PHP_intl.dll正确配置方法(apache2.2+php5.3.5)
-
ASP.NET Core 2.2中的Endpoint路由详解
-
浅谈从ASP.NET Core2.2到3.0你可能会遇到这些问题
-
Apache2.2以上版本与Tomcat整合配置及负载均衡实现
-
.NET Core 2.2 新增部分功能使用尝鲜
-
从.NET CORE2.2升级到3.0过程及遇到的一些问题