购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

3.1 生命周期

和计算机的操作系统类似,Android系统是一个多任务(Multi-Task)的操作系统,用户可以在听音乐的同时,也可以进行其他操作,如上网、玩游戏、聊微信等,但是在同一个时间只能有一个活动的应用程序对用户可见。由于手机内存有限,每次多执行一个应用程序,就会多消耗一部分系统可用的内存,运行的程序越多内存消耗就越大,系统运行就越慢,甚至不稳定,导致用户体验越来越糟糕。为了解决这个问题,提高手机内存的利用率,Android系统主动管理资源,完成对系统资源的调度工作。

3.1.1 概述

在Android系统中,每个应用程序可以理解为一个独立的进程。默认情况下,每个程序都是通过它们自己的进程运行的,每一个进程都是Dalvik(Dalvik是Google公司自己设计的用于Android平台的Java虚拟机)的一个单独的实例。每个应用程序的内存和进程管理都是由运行时专门进行处理的。应用程序进程从创建到结束的过程就是应用程序的生命周期。Android应用程序的生命周期有一个非常重要、但是又很少见的特性:应用程序进程的生命周期不是由进程自身控制的,而是由Android系统决定的。影响应用程序生命周期的主要因素包括:该进程对于用户的重要性,以及当前系统中剩余内存的多少。对于开发者而言,不正确地使用系统组件可能会导致应用程序在运行过程中被意外地终止。

Android中的生命周期,其实是体现一个应用程序运行起来以后的状态。因此,需要了解进程和线程的概念。

1.进程

在Android系统中,进程可以理解成应用程序的具体运行实现。当用户同时开启多个应用程序,势必会造成大量资源的占用,从而造成了“系统慢”的用户反馈。为了避免发生这种问题,Android操作系统需要考虑到,当系统资源紧张的情况出现,操作系统应该做些什么来避免不良的用户体验?实际上,其目标就是要让用户感觉不到这种资源紧张。因此,Android系统需要关闭那些不常用或者对用户体验没有直接影响的进程。至于到底要关闭哪个进程,需要根据系统中的进程运行状态而定。

根据运行于进程内的组件和组件的状态,把进程划分为5种不同重要程度的等级。等级低的进程会被优先淘汰,从而释放系统资源。在Android系统中,进程的优先级取决于所有组件中优先级最高的部分。下面根据优先级由高到低进行介绍。

(1)前台进程

顾名思义,前台进程就是目前显示在屏幕上正和用户交互的进程。通常,在系统中前台进程数量很少,而这种进程对用户体验的影响最大,只有系统的内存稀少到不足以维持和用户的基本交互时才会销毁前台进程。因此,这种进程的重要性是最高的。

(2)可见进程

可见进程是指可以被用户看见,只是目前不是最上层界面(最上层界面在前台进程里面)。可见进程比前台进程重要性低,但是在交互方面影响还是很大的,因为用户随时可能将它切换为前台进程,所以系统不会轻易销毁它。

(3)服务进程

一个包含已启动服务的进程就是服务进程。它对用户不可见,但是保证了一些重要的事件被监听或者维持着某些状态,比如网络数据传输、后台音乐播放。因此,除非不能保证前台进程或可见进程所必需的资源,否则不能强行清除服务进程。

(4)后台进程

这里的后台进程不是一般意义上的后台进程,而是指不包含任何已经启动的服务,而且没有任何用户可见的Activity的进程。例如,某个被完全遮挡的应用程序便属于后台进程。通常,用户暂时没有和这个进程交互的愿望,所以这里后台进程有点“待销毁”的意思。在Android系统中存在较多的后台进程,系统资源紧张时,优先考虑销毁这些后台进程。

(5)空进程

空进程是指不包含任何活跃组件的进程。这类进程的作用就是高速缓存,当有新进程创建的时候,这个空进程可以加快进程创建速度,当系统内存不足的时候,首先销毁空进程。

2.线程

一个进程中,可以有一个或多个线程。默认情况下,进程中所有组件都在UI线程中进行初始化,保证整个程序是单线程的。但是如果对于特殊操作,如网络连接、下载等占用时间的操作,开发人员需要创建独立的线程,以免阻塞UI线程操作。

3.1.2 Activity生命周期的方法

所有Android组件都具有自己的生命周期,本节以Activity组件为例,对其生命周期进行介绍。

Android操作系统跟踪所有运行的Activity对象,将这些对象统一放进一个Activity栈中,如图3-2所示。当一个新的Activity启动时,处于栈顶的Activity将暂停,而这个新的Activity将被放入栈顶;当用户按下返回键时,当前Activity出栈,而前一个恢复为当前运行的Activity。栈中保存的其实是对象,其中的Activity永远不会重排,只会进行压入或弹出操作。

图3-2 Activity栈

通过上面的描述不难看出,一个Activity从启动到销毁,会经历多种状态,而且状态之间会进行转化。这些状态主要包括:①活动状态;②非活动状态;③暂停状态;④停止状态。随着Activity状态的不断变化,Android系统会调用不同的事件回调函数,开发人员在回调函数中添加代码,就可以在Activity状态变化时完成适当的工作。例如,开发人员通常在onCreate()函数中,完成相关的初始化工作。这些回调方法的状态变化如图3-3所示。

图3-3 一个Activity的生命周期

从图3-3中可以看出,Activity生命周期包含了3层循环。

(1)全生命周期

从 onCreate()开始到 onDestroy()结束。一般情况下,开发人员会在 onCreate()中初始化Activity所能使用的全局资源和状态,并在 onDestroy()中释放这些资源。例如,Activity中使用后台线程,则可以在onCreate()中创建线程,在onDestroy()中停止并销毁线程。

(2)可视生命周期

从onStart()开始到onStop()结束。onStart()一般用来初始化或启动与更新界面相关的资源,onStop()一般用来暂停或停止一切与更新界面相关的线程、计时器或Service等,因为在onStop()执行时,界面已经处于不可见状态,所以此处不适合做更新界面等操作。另外,onStart()和onStop()也经常被用来注册和销毁BroadcastReceiver组件。由于Activity经常在可见和不可见的状态进行转换,所以onStart()和onStop()经常会被反复调用。

(3)前台生命周期

从onResume()开始到onPause()结束。当前阶段,界面是处于屏幕最顶层,并且与用户进行交互。由于Activity会频繁地在前台和非前台的状态进行切换,因此onResume()和onPause()也会被反复调用,例如设备休眠或弹出对话框,均会导致状态的切换,因此在 onResume()和onPause()中应该尽量完成一些轻量级的工作,避免每次切换时都需要用户等待。

表3-1将图3-3中的各回调函数进行详细说明和介绍。

表3-1 生命周期的方法

除了上述的Activity生命周期事件回调函数外,还有onRestoreInstantceState()和onSaveInstance State()两个函数经常会被使用,用于保存和恢复Activity的界面临时信息,如用户在界面中输入的数据或选择的内容等,而onPause()一般被用来保存界面的持久信息。虽然这两个函数不属于Activity生命周期的回调函数,但onSaveInstanceState()在Activity被暂时停止时会被调用,而暂停的Activity被恢复时,也会调用onRestoreInstantceState()。

3.1.3 LogCat

在Android程序开发中,出现错误(Bug)是每个开发人员必有的经历。通常,开发环境可以检测到语法错误,根据提示,比较容易修改。但运行时的错误,分析和定位通常就没有那么简单了,尤其是代码量较大、结构比较复杂的应用程序,仅凭直觉很难达到理想的效果。因此,Android提供了LogCat工具,帮助开发人员进行错误分析。

LogCat是用来获取系统日志信息的工具,集成于Android开发环境中。在Android Studio中,通过执行“Tools”→“Android”→“Android Device Monitor ”命令打开图3-4所示窗口。该窗口下方为LogCat和Console控制台,开发人员可以选择需要追踪的工程,查看详细的调试信息。

下面通过案例演示LogCat工具的使用,以及借助于Log类如何进行程序调试。

【例3-1】 为了监测生命周期中各回调函数的调用情况,在LogCat 中进行打印输出。

实现步骤如下:

(1)新建项目:项目名称为ActivityLifeDemo。

图3-4 Android Device Monitor窗口

(2)找到Activity类(默认为MainActivity):查看代码,内容如下(已有onCreate回调函数):

@Override

protected void onCreate(Bundle savedInstanceState){

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

}

(3)重构回调函数:模仿上述代码,完成其他回调函数。借助于Android Studio工具,执行“Code”→“Generate”→“Override Methods…”命令,选取需要重载的函数(见图3-5)。最后单击“OK”按钮,则在上述的类中自动生成相关代码:

图3-5 选择重载函数

@Override

protected void onDestroy(){

super.onDestroy();

}

@Override

protected void onStart(){

super.onStart();

}

@Override

protected void onStop(){

super.onStop();

}

@Override

protected void onRestart(){

super.onRestart();}

@Override

protected void onResume(){

super.onResume();

}

@Override

protected void onPause(){

super.onPause();

}

@Override

protected void onRestoreInstanceState(Bundle savedInstanceState){

super.onRestoreInstanceState(savedInstanceState);

}

@Override

protected void onSaveInstanceState(Bundle outState){

super.onSaveInstanceState(outState);

}

(4)运行项目:此时程序运行,不会有任何特别的效果,因为以上的回调函数里没有任何实质的内容。

(5)代码中增加日志输出信息:为了验证每个回调函数是否被调用,开发人员可以在回调函数里增加打印信息。在上述的各个回调函数中,依次加入下面的方法,其含义和Java的System.out.println()类似,只是该打印效果在LogCat中可以通过更丰富的形式进行输出。

Log.i(TAG,"--(1)OnCreate");

Log.i(TAG,"--(2)onStart");

Log.i(TAG,"--(3)onRestoreInstanceState");

Log.i(TAG,"--(4)onResume");

Log.i(TAG,"--(5)onSaveInstanceState");

Log.i(TAG,"--(6)onRestart");

Log.i(TAG,"--(7)onPause");

Log.i(TAG,"--(8)onStop");

Log.i(TAG,"--(9)onDestroy");

其中,TAG是该类中的一个常量,用于以后的日志信息过滤。定义如下:

private static String TAG="ACTIVITYLIFE ";

(6)运行项目,查看运行效果,如图3-6所示。需要注意,此次项目的运行结果不是在模拟器上,而是在LogCat的输出信息。而LogCat下的信息,是否有些混乱呢?

图3-6 LogCat输出信息

(7)信息过滤:LogCat监测的是当前模拟器的所有日志输出,由于其中的信息错综复杂,而开发人员通常只关注其开发的应用程序的日志输出,也就是图3-6的运行结果,因此需要进行信息过滤和筛选。在左上角的“+”号和“-”号,分别是添加和删除过滤器。用户可以根据日志信息的标签(Tag)、产生日志的进程编号(Pid)或信息等级(Level),对显示的日志内容进行过滤。单击图3-6左侧的绿色“+”号,出现如图3-7所示的对话框,也就是通过本例中的TAG值即可。

图3-7 LogCat过滤器设置

(8)查看结果:通过分析输出结果,进行生命周期的认识,如图3-8所示。

图3-8 LogCat运行效果

【说明】 也可以通过传统的System.out.println()进行信息打印,过滤日志的时候,需要将

LogTag的内容换成System.out即可。

LogCat的右上方的单词表示几种不同类型的日志信息,单词含义如下:Verbose ——详细信息;Debug ——调试信息;Info——通知信息;Warn ——警告信息;Error——错误信息。不同类型日志信息的级别是不同的,级别最高的是错误信息,其次是警告信息,然后是通知和调试信息,最低的是详细信息。在LogCat中,开发人员可以通过字母图标选择显示的信息类型,级别高于选择类型的信息会在LogCat中显示,但级别低于选定的信息则会被忽略。因此,当程序运行出现错误时,可以尝试选择错误信息,检查其中的日志输出,看看是否对开发人员有所帮助。

LogCat的功能是由Android的类android.util.Log决定的,在Java程序中,日志输出的使用方法包括以下几种:Log.v(),Log.d(),Log.i(),Log.w(),Log.e(),分别对应上述的5种不同类型的日志,其中字母代表日志类别单词的首字母。 8gp5S/Hn4tNMYHm0f2WOx/TTkWuXNnnjbdyG8vTPTOqgpydxT0+6x/NGDqXRaQL7

点击中间区域
呼出菜单
上一章
目录
下一章
×