Android开发入门教程--3.Activity入门指南
Activity是Android程序的4大组件之一,是Android程序的表示层。程序的每一个显示屏幕就是一个Activity,它上面可以显示一些控件也可以监听并处理用户的事件做出响应,Activity之间通过Intent进行通信。
一、Activity 的生命周期:
为了理解生命周期,先说一下任务:任务其实就是“应用程序”,它是由一个或多个Activity组成的共同完成一个完整的用户体验。
比如我们打开QQ首先进入联系人列表界面,这个界面就是一个Activity:
点击“添加好友”便进入到搜索的Activity:
点击一条搜索结果便进入到了名片的Activity:
一个任务的Activity是由栈的形式存储的,栈底的是启动整个任务的Activity,栈顶的是当前运行的用户可以交互的Activity,当一个activity 启动另外一个的时候,新的activity 就被压入栈,并成为当前运行的activity。而前一个activity 仍保持在栈之中。当用户按下BACK 键的时候,当前activity 出栈,而前一个恢复为当前运行的activity。栈中的Activity 永远不会重排,只会压入或弹出。
任务中的所有Activity 是作为一个整体进行移动的。整个的任务(即Activity 栈)可以移到前台,或退至后台。举个例子说,比如当前任务在栈中存有四个Activity──三个在当前Activity 之下。当用户按下HOME 键的时候,回到了系统界面,然后选择了一个新的应用程序(也就是一个新任务)。则当前任务转入后台,而新任务的根Activity 显示出来。然后,用户再次回到了系统界面选择了前一个应用程序(上一个任务)。于是那个任务,带着它栈中所有的四个Activity,再一次的到了前台。当用户按下BACK 键的时候,屏幕不会回到用户刚才离开的任务。取而代之,当前任务的栈中最上面的Activity 被弹出,倒数第二个Activity 显示了出来。
Android系统是一个多任务(Multi-Task)的操作系统,可以在用手机听音乐的同时执行其他多个程序。每多执行一个应用程序,就会多耗费一些系统内存,当同时执行的程序过多,或是关闭的程序没有正确释放掉内存,系统就会觉得越来越慢,甚至不稳定。为了解决这个问题, Android 引入了一个新的机制-- 生命周期(Life Cycle)。Android 应用程序的生命周期是由Android 框架进行管理,而不是由应用程序控制。通常,每一个应用程序(入口一般会是一个Activity 的onCreate 方法),都会产生一个进程(Process)。当系统内存即将不足的时候,会依照优先级自动进行进程(process)的回收。不管是使用者或开发者, 都无法确定的应用程序何时会被回收。所以为了防止数据丢失和其他问题,了解生命周期很重要。
1、四种状态:
1.活动(Active/Running)状态:
当Activity运行在屏幕前台(处于当前任务活动栈的最上面),此时它获取了焦点能响应用户的操作,属于运行状态,同一个时刻只会有一个Activity 处于活动(Active)或运行(Running)状态。
2.暂停(Paused)状态:
当Activity失去焦点但仍对用户可见(如在它之上有另一个透明的Activity或Toast、AlertDialog等弹出窗口时)它处于暂停状态。暂停的Activity仍然是存活状态(它保留着所有的状态和成员信息并保持和窗口管理器的连接),但是当系统内存极小时可以被系统杀掉。
3.停止(Stopped)状态:
完全被另一个Activity遮挡时处于停止状态,它仍然保留着所有的状态和成员信息。只是对用户不可见,当其他地方需要内存时它往往被系统杀掉。
4.非活动(Dead)状态:
Activity 尚未被启动、已经被手动终止,或已经被系统回收时处于非活动的状态,要手动终止Activity,可以在程序中调用"finish"方法。如果是(按根据内存不足时的回收规则)被系统回收,可能是因为内存不足了。
内存不足时,Dalvak 虚拟机会根据其内存回收规则来回收内存:
1.先回收与其他Activity 或Service/Intent Receiver 无关的进程(即优先回收独立的Activity)因此建议我们的一些(耗时)后台操作,最好是作成Service的形式。
2.不可见(处于Stopped状态的)Activity。
3.Service进程(除非真的没有内存可用时会被销毁)。
4.非活动的可见的(Paused状态的)Activity。
5.当前正在运行(Active/Running状态的)Activity。
2、七个重要方法,当Activity从一种状态进入另一状态时系统会自动调用下面相应的方法来通知用户这种变化:
1.onCreate(Bundle savedInstanceState):
当Activity第一次被实例化的时候系统会调用,整个生命周期只调用1次这个方法通常用于初始化设置,为Activity设置所要使用的布局文件,为按钮绑定监听器等静态的设置操作。
2.onStart():
当Activity可见未获得用户焦点不能交互时系统会调用。
3.onRestart():
当Activity已经停止然后重新被启动时系统会调用。
4.onResume():
当Activity可见且获得用户焦点能交互时系统会调用。
5.onPause():
当系统启动另外一个新的Activity时,在新Activity启动之前被系统调用保存现有的Activity中的持久数据、停止动画等,这个实现方法必须非常快。当由系统而不是用户自己出于回收内存时,关闭了Activity 之后。用户会期望当他再次回到这个Activity 的时候,它仍保持着上次离开时的样子。此时用到了onSaveInstanceState(),方法onSaveInstanceState()用来保存Activity被杀之前的状态,在onPause()之前被触发,当系统为了节省内存销毁了Activity(用户本不想销毁)时就需要重写这个方法了,当此Activity再次被实例化时会通过onCreate(Bundle savedInstanceState)将已经保存的临时状态数据传入。因为onSaveInstanceState()方法不总是被调用,因为有些场景是不需要保存状态数据的。 比如用户按下BACK键退出activity时, 用户显然想要关闭这个activity,此时是没有必要保存数据以供下次恢复的,也就是onSaveInstanceState()方法不会被调用。onSaveInstanceState()方法触发条件为(按下HOME键,按下电源按键关闭屏幕,横竖屏切换情况下),应该仅重写onSaveInstanceState()来记录Activity的临时状态,而不是持久的数据。应该使用onPause()来存储持久数据。
6.onStop():
当Activity被新的Activity完全覆盖不可见时被系统调用。
7.onDestroy():
当Activity(用户调用finish()或系统由于内存不足)被系统销毁杀掉时系统调用,(整个生命周期只调用1次)用来释放onCreate ()方法中创建的资源,如结束线程等。
3、三个嵌套循环
1.Activity完整的生命周期:从第一次调用onCreate()开始直到调用onDestroy()结束
2.Activity的可视生命周期:从调用onStart()到相应的调用onStop()在这两个方法之间,可以保持显示Activity所需要的资源。如在onStart()中注册一个广播接收者监听影响你的UI的改变,在onStop() 中注销。
3.Activity的前台生命周期:从调用onResume()到相应的调用onPause()。
举例说明:
有3个Acitivity,分别用One,Two(透明的),Three表示,One是应用启动时的主Activity;
启动第一个界面Activity One时,它的次序是:
onCreate (ONE) - onStart (ONE) - onResume(ONE)
点"打开透明Activity"按钮时,这时走的次序是:
onPause(ONE) - onCreate(TWO) - onStart(TWO) - onResume(TWO)
再点back回到第一个界面,Two会被杀这时走的次序是:
onPause(TWO) - onActivityResult(ONE) - onResume(ONE) - onStop(TWO) - onDestroy(TWO)
点"打开全屏Activity"按钮时,这时走的次序是:
onPause(ONE) - onCreate(Three) - onStart(Three) - onResume(Three) - onStop(ONE)
再点back回到第一个界面,Three会被杀这时走的次序是:
onPause(Three) - onActivityResult(ONE) - onRestart(ONE) - onStart(ONE)- onResume(ONE) - onStop(Three) - onDestroy(Three)
再点back退出应用时,它的次序是:
onPause(ONE) - onStop(ONE) - onDestroy(ONE)
二、代码示例:
新建一个测试项目,并在MainActivity写以下代码:
1 /** Called when the activity is first created. */ 2 @Override 3 public void onCreate(Bundle savedInstanceState) { 4 super.onCreate(savedInstanceState); 5 setContentView(R.layout.main); 6 Toast.makeText(this, "onCreate(Bundle savedInstanceState)方法被调用了。", Toast.LENGTH_SHORT).show(); 7 } 8 9 @Override 10 public void onStart(){ 11 super.onStart(); 12 Toast.makeText(this, "onStart()方法被调用了。", Toast.LENGTH_SHORT).show(); 13 } 14 15 @Override 16 public void onRestoreInstanceState(Bundle savedInstanceState){ 17 super.onRestoreInstanceState(savedInstanceState); 18 Toast.makeText(this, "onRestoreInstanceState(Bundle savedInstanceState)方法被调用了。", Toast.LENGTH_SHORT).show(); 19 } 20 21 @Override 22 public void onRestart(){ 23 super.onRestart(); 24 Toast.makeText(this, "onRestart()方法被调用了。", Toast.LENGTH_SHORT).show(); 25 } 26 27 @Override 28 public void onSaveInstanceState(Bundle savedInstanceState){ 29 super.onSaveInstanceState(savedInstanceState); 30 Toast.makeText(this, "onSaveInstanceState(Bundle savedInstanceState)方法被调用了。", Toast.LENGTH_SHORT).show(); 31 } 32 33 @Override 34 public void onPause(){ 35 super.onPause(); 36 Toast.makeText(this, "onPause()方法被调用了。", Toast.LENGTH_SHORT).show(); 37 } 38 39 @Override 40 public void onResume(){ 41 super.onResume(); 42 Toast.makeText(this, "onResume方法被调用了。", Toast.LENGTH_SHORT).show(); 43 } 44 45 @Override 46 public void onStop(){ 47 super.onStop(); 48 Toast.makeText(this, "onStop()方法被调用了。", Toast.LENGTH_SHORT).show(); 49 } 50 51 @Override 52 public void onDestroy(){ 53 super.onDestroy(); 54 Toast.makeText(this, "onDestroy()方法被调用了。", Toast.LENGTH_SHORT).show(); 55 } 56 57 @Override 58 public void onConfigurationChanged(Configuration newConfig){ 59 super.onConfigurationChanged(newConfig); 60 Toast.makeText(this, "onConfigurationChanged()方法被调用了。", Toast.LENGTH_SHORT).show(); 61 }
运行后在模拟器上观察方法的调用情况。
三、横竖屏切换时候Activity的生命周期:
1.默认设置切换时的生命周期:
运行刚才新建的测试项目,Toast消息的提示调用顺序是:
“onCreate”->"onStart"->"onResume"
按crtl+f12切换模拟器为横屏时,Toast消息的提示调用顺序是:
“onSaveInstanceState”->"onPause"->"onStop"->"onDestroy"->"onCreate"->"onStart"->"onRestoreInstanceState"->"onResume"
再次按按crtl+f12切换模拟器为竖屏时,发现Toast消息显示了2次相同的调用顺序:
“onSaveInstanceState”->"onPause"->"onStop"->"onDestroy"->"onCreate"->"onStart"->"onRestoreInstanceState"->"onResume"->
"onSaveInstanceState"->"onPause"->"onStop"->"onDestroy"->"onCreate"->"onStart"->"onRestoreInstanceState"->"onResume"
2.修改AndroidManifest.xml,在该Activity添加android:configChanges="orientation":
1 <activity android:name=".MainActivityLife" 2 android:label="@string/app_name" android:configChanges="orientation"> 3 <intent-filter> 4 <action android:name="android.intent.action.MAIN" /> 5 <category android:name="android.intent.category.LAUNCHER" /> 6 </intent-filter> 7 </activity>
重新生成并运行项目,然后按crtl+f12切换模拟器为横屏时,Toast消息的提示调用顺序是:
“onSaveInstanceState”->"onPause"->"onStop"->"onDestroy"->"onCreate"->"onStart"->"onRestoreInstanceState"->"onResume"
再次按按crtl+f12切换模拟器为竖屏时,Toast消息的提示多了一个"onConfigurationChanged":
“onSaveInstanceState”->"onPause"->"onStop"->"onDestroy"->"onCreate"->"onStart"->"onRestoreInstanceState"->"onResume"->"onConfigurationChanged"
3.修改AndroidManifest.xml,把刚才添加的android:configChanges="orientation"改为android:configChanges="keyboardHidden|orientation":
1 <activity android:name=".MainActivityLife" 2 android:label="@string/app_name" android:configChanges="keyboardHidden|orientation"> 3 <intent-filter> 4 <action android:name="android.intent.action.MAIN" /> 5 <category android:name="android.intent.category.LAUNCHER" /> 6 </intent-filter> 7 </activity>
重新生成并运行项目,然后按crtl+f12切换模拟器为横屏时,Toast消息只提示了"onConfigurationChanged":
"onConfigurationChanged"
再次按按crtl+f12切换模拟器为竖屏时,Toast消息提示了2次"onConfigurationChanged":
"onConfigurationChanged"->"onConfigurationChanged"
4.总结:
不设置Activity的android:configChanges时,切屏会重新调用各个生命周期,切横屏时会执行一次,切竖屏时会执行两次。
设置Activity的android:configChanges="orientation"时,切屏还是会重新调用各个生命周期,切横、竖屏时只会执行一次,且在竖屏时执行了一次"onConfigurationChanged"。
设置Activity的android:configChanges="keyboardHidden|orientation"时,切屏不会重新调用各个生命周期,只会执行"onConfigurationChanged"。