第1节 Activity的使用
Activity
几乎是每个应用必有的组件,所以任何安卓应用的开发几乎都是从Activity
开始的。
比如,你希望设计一个计算器应用,要呈现这个计算器的功能(显示出计算器的样子,实现计算的能力)一定会在这个应用中创建一个Activity
,让这个Activity
展示的界面就是计算器。
使用Android Studio创建工程后,会默认为我们创建一个Activity
组件,它继承自Android SDK的Activity
或其派生类。
class CalulatorActivity extends Activity {
......
}
新创建的Activity–CalulatorActivity作为安卓的四大组件之一,一定要在这个应用的配置文件manifest.xml中声明自己的存在,否则系统会不认识这个组件,当你启动它的时候就会报错。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.anddle.calculator">
<application
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name">
<!--为应用声明一个Activity-->
<activity android:name=".CalulatorActivity">
......
</activity>
</application>
</manifest>
/*******************************************************************/
* 版权声明
* 本教程只在CSDN和安豆网发布,其他网站出现本教程均属侵权。
/*******************************************************************/
第2节 Activity的生命周期
每一个Activity都有它的生命周期,体现了它从诞生到消亡的各个阶段。
下图为我们展示了Activity整个生命的过程,这是安卓官方提供的Activity生命周期图。
2.1 生命各个时期的回调
Activity每进入到一个生命的阶段,就会调用这些状态对应的函数。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
protected void onDestroy() {
super.onDestroy();
}
@Override
protected void onResume() {
super.onResume();
}
@Override
protected void onPause() {
super.onPause();
}
@Override
protected void onStart() {
super.onStart();
}
@Override
protected void onStop() {
super.onStop();
}
@Override
protected void onRestart() {
super.onRestart();
}
如果我们在代码中继承了Activity类,并覆盖了这些函数,我们就能感知到Actvity生命状态的转换,并在这个转换的时刻让Activity做对应的处理。
比如在Activity创建的时候,让它调用我们的代码,完成界面的布局;在Activity销毁的时候,让它调用我们的代码,完成各种资源的释放。
//继承Activity类
class MyActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//进行界面的布局
setContentView(R.layout.activity_main);
......
}
......
@Override
protected void onDestroy() {
super.onDestroy();
//释放各种占用的资源
......
}
}
2.2 Activity的状态
接下来,我们详细介绍下Activity的各个阶段。这些阶段都是成对出现的。根据不同的标准,我们将它们分成三类:
全生命周期:这是Activity的从创建到销毁的整个阶段,从
onCreate()
开始到onDestroy()
结束。可见生命周期:这是Activity能被用户看到的整个阶段,这里说的能被看到包括了“虽然被显示但是不能与用户交互”的情况,例如你正在微信聊天,突然系统弹出来一个对话框类型的Activity,提示你电量不足,这时微信界面被挡在后面,不能和用户交互了,但是你还能看到。从
onStart()
开始到onStop()
结束。前台生命周期:这是Activity可以和用户进行交互的阶段。从
onResume()
开始到onPause()
结束。
2.3 Acivity常见的切换周期
下面我们看看几个经常见到的Activity的周期切换。
2.3.1 完全周期
从一个Activity创建出来,到显示,再到用户按返回键主动退出销毁这个Activity,它将经历:
onCreate()
->
onStart()
->
onResume()
->
用户可以与Activity交互,用户按返回键主动退出->
onPause()
->
onStop()
->
onDestroy()
;
2.3.2 Activity启动另一个Activity
Activity A创建出来,并显示;然后Activity A启动另一个Activity B,B创建出来,并显示,它们将经历:
Activity A onCreate()
->
Activity A onStart()
->
Activity A onResume()
->
用户可以与Activity A交互,然后Activity A启动另一个Activity B,
Activity A onPause()
->
Activity B onCreate()
->
Activity B onStart()
->
Activity B onResume()
->
Activity A onSaveInstanceState()
->
Activity A onStop()
->
Activity A被隐藏,用户可以与Activity B交互;
2.3.3 Activity的自动重新创建
在某些情况下,即使用户没有点击“返回”按钮,Activity也会被系统主动回收销毁,例如,
当Activity被放在后台(OnPause或OnStop状态)没有显示出来的时候,内存紧张,系统就会主动回收这个Activity;
横竖屏切换时,Activity的界面布局发生变化需要重新载入横屏的界面布局时,就要将当前的Activity销毁,然后再重新创建一个它横屏时的Activity。
因为这是系统“悄悄”销毁的Activity,并没有得到用户的“同意”,所以当用户再次切换到这种Actiity的时候,系统需要制造出一种这个Activity从来没有被回收过的假象,它就需要“悄悄的”再把这个Activity再次创建出来。
这个过程就是Activity的重建。
不论是是因为资源不足触发的重建,还是因为屏幕旋转触发的重建,Activity的生命周期过程都一样。
这里用屏幕旋转,举个例子。如果一个Activity从创建出来,到显示,然后旋转,那么它将经历:
onCreate()
->
onStart()
->
onResume()
->
用户可以与Activity交互,此时屏幕进行旋转,从竖屏变横屏->
onPause()
->
onSaveInstanceState()
->
onStop()
->
onDestroy()
->
onCreate()
->
onStart()
->
onRestoreInstanceState()
->
onResume()
->
用户可以与Activity交互;
在周期变化的过程当中,加入了onSaveInstanceState()
和onRestoreInstanceState()
。虽然它们不是Activity
周期的一部分,但是对它们对回调在Activity
的各种切换扮演了非常重要的角色--提供了保存Activity
数据的时机。
关于Activity的系统回收,我们将在下一节详细介绍。
/*******************************************************************/
* 版权声明
* 本教程只在CSDN和安豆网发布,其他网站出现本教程均属侵权。
/*******************************************************************/
第3节 Activity的系统回收
在Activity生命的周期中,安卓系统可能直接回收Activity。系统回收Activity有两种常见的情况,
- 系统资源紧张;
- 屏幕的旋转;
3.1 资源紧张
当系统资源紧张时,例如你打开了很多应用,系统中的可用内存很少了。这时安卓系统会采用一定的策略来回收系统中的资源,这些资源包括前台可见的Activity、后台不可见的Activity,后台中运行的Service,在状态栏中给出了“正在运行”的提示的Service等等。
在回收资源的时候,系统会根据这些资源所在的进程优先级来判断。进程优先级高的,最后回收;进程优先级低的,最先回收。 这些进程优先级从高到低分别是,
前台优先级-IMPORTANCE_FOREGROUND,它可能是,
一个正在与用户进行交互的Activity;
一个被Activity绑定的Service,而这个Activity正在与用户进行交互;
一个被调用了
startForground()
方法的Service,此时它的优先级被设置成了前台优先级;一个正在经历周期变化的Service(也就是说这个Service的
onCreate()
onDestroy()
等周期变化的方法正在被触发调用);一个正在执行
onReceive()
的BroadcastReceiver
;
可见优先级-IMPORTANCE_VISIBLE,它可能是,
一个虽然用户不能交互、但是用户仍然能看到的Activity;
一个绑定了可见Activity的Service;
服务优先级-IMPORTANCE_SERVICE,它可能是,
- 一个普通的Service(没有处于前台优先级和可见优先级中那些Service的状态);
后台优先级-IMPORTANCE_BACKGROUND,它可能是,
- 不可见的Activity;
空优先级-IMPORTANCE_EMPTY,
- 它当中没有任何应用组件,它只是为了提高应用的启动速度而预留的空进程,可以随时被系统回收。
这里举个例子,资源极度匮乏的时候,
- 系统会优先回收那些没有显示的Activity(例如在onStop()状态下的Activity);
如果回收那些资源以后,发现资源还是不够用,就会回收虽然显示了、但却没有和用户做交互的Activity(例如在onPause()状态下的)。
这里会遇到一个问题:假如Activity A启动了Activity B之后,系统发现内存不足,在回收了其它所有资源后,它不得不继续回收Activity A。
3.1.1 产生的问题
此时与Acitity B正在交互的用户,点击了“返回”按钮,按照道理,应该显示Activity A才对,但是系统已经回收了Activity A。这时该怎么办呢?
系统遇到这种情况就会重新创建Activity A,重新调用它的onCreate()。
不过这里又会遇到另一个很现实的问题:如果之前的Activity A上面用户正输入了一些东西,例如他的名字和邮箱地址,或者是已经编辑了很长一大段的文字,如果系统onCreate的话,这些输入的东西就会被清除掉了。这可怎么办呢?
3.1.2 解决办法
安卓系统为我们提供了一个这种情况下数据的重建机制:在Activity被放到后台运行的时候使用onSaveInstanceState()
回调函数来保存这些数据,
@Override
protected void onSaveInstanceState(Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
//对于含有唯一id的UI控件和TextView控件来说,
//super.onSaveInstanceState()将触发它们自己的onSaveInstanceState()方法,
//实现对内容的自动保存,例如TextView控件上已有的文字,
//activity的onSaveInstanceState()主要用来保存需要恢复的其他信息
}
在Activity被重建的时候,使用onRestoreInstanceState()
回调函数来恢复这些数据
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
//将需要恢复的数据,从savedInstanceState变量中取出,设置到界面上
}
需要注意的是,onSaveInstanceState()
与onRestoreInstanceState()
在Activity周期切换过程中并不一定会被调用,只是在这种Activity异常流程处理时(Activity被系统回收、config信息变化等情况),才会被调用到。
3.2 屏幕旋转
屏幕旋转时,Activity的生命周期也将发生变化。
如果一个Activity从创建出来,到显示,然后旋转,那么它将经历:
onCreate()
->
onStart()
->
onResume()
->
用户可以与Activity交互,此时屏幕进行旋转,从竖屏变横屏->
onPause()
->
onSaveInstanceState()
->
onStop()
->
onDestroy()
->
onCreate()
->
onStart()
->
onRestoreInstanceState()
->
onResume()
->
用户可以与Activity交互;
可以看到,旋转的时候onCreate()
函数会被再次调用。在这里,如果拥有横屏布局文件,onCreate()
中的setContentView()
将会使用横屏的布局,如果没有,依然使用默认的布局文件。
3.2.1 产生的问题
如果Activity从竖屏变成横屏,那么会先执行onDestroy()
,再进行一次onCreate()
创建的过程。这意味着之前界面上显示的数据需要重新刷新一次。
假如之前刷新这些数据需要花费很长的时间,那就有必要认真的思考如何避免数据的再次刷新。
3.2.2 解决方法
解决屏幕旋转时Activity被create两次的方法有两种。
用
onSaveInstanceState()
和onRestoreInstanceState()
可用用来保存和还原这些数据;在
AndroidManifest.xml
文件中,给这个Activity组件加上android:screenOrientation="orientation|screenSize"
的属性就可以了;<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.anddle.anddleplayer"> <application ......> <activity android:name=".VideoListActivity" android:configChanges="orientation|screenSize"> </activity> ...... </application> </manifest>
这种Activity从创建出来,到显示,然后旋转,那么它将经历:
onCreate()
->
onStart()
->
onResume()
->
用户可以与Activity交互,此时屏幕进行旋转,从竖屏变横屏->
onConfigurationChanged()
;在被触发的
onConfigurationChanged()
函数中,可以感知到屏幕的变化,@Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); // 当新设置中,屏幕布局模式为横屏时 if(newConfig.orientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) { //TODO 某些操作 } // 当新设置中,屏幕布局模式为竖屏时 else if(newConfig.orientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT){ //TODO 某些操作 } }
如此一来,这个Activity在旋转时就不会重走销毁、创建的过程了,而只是在旋转后经历一个
onConfigurationChanged()
。这种情况下,即使它存在横屏布局文件,这个横屏布局也不会被使用到,因为onCreate()
并没有被调用到,也就不会调用setContentView)
了。
/*******************************************************************/
* 版权声明
* 本教程只在CSDN和安豆网发布,其他网站出现本教程均属侵权。
*另外,我们还推出了Arduino智能硬件相关的教程,您可以在我们的网店跟我学Arduino编程中购买相关硬件。同时也感谢大家对我们这些码农的支持。
*最后再次感谢各位读者对安豆
的支持,谢谢:)
/*******************************************************************/