很多人都关注手机性能的提升,认为相比与桌面计算系统,手机的计算性能有了很大的进步,不过实际上移动设备的性能或者说资源依然被认为很有限(当然了,功率才几瓦,算力能有多高),尤其是内存的使用方面.所以安卓操作系统的一个关键任务,就是如何将有限的资源分配给操作系统和应用程序,让这些内容都能够及时对用户的操作作出反应.为了达到这个目的,不管是整个应用,还是应用中的组件,都有着生命周期管理.
安卓开发的关键之一,就是要理解应用和Activity
的生命周期模型,以及在应用运行过程中,如何对状态的改变产生响应.
安卓进程的状态
在一个安卓进程里,这个进程的状态由这个进程里最高级别的活动组件的优先级来决定,一个进程可以有五种状态之一:
Foreground Process
, 最高优先级VisibleProcess
Service Process
Background Process
Empty Process
, 最低优先级
来一个一个看一下
Foreground Process
我翻译成前台进程,是最高优先级的进程,一般同时只有一到二个前台进程,也是最不可能被系统杀掉的进程.一般来说前台进程的条件是:
- 拥有正在和用户交互的内容
- 拥有与用户交互内容相关联的服务
- 有一个调用过
startForeground()
方法的服务,如果终止会影响用户交互 - 拥有一个正在执行
onCreate() onResume()
或者onStart()
回调方法的服务 - 拥有一个正在执行
onReceive()
方法的Broadcast Receiver
可以看出,前台进程基本上都是与当下用户交互相关联的内容,肯定会被系统给予最优先的资源,这样才不会让手机看上去比较"卡".
VisibleProcess
拥有可以让用户看到的内容,但是没有和用户发生交互,就是可见进程,比如切换应用的页面,半屏幕的对话框等.与可见进程有关联的服务也被判断为可见进程.
Service Process
拥有已经启动的服务的进程,被分类为此类别.
Background Process
拥有对用户不可见的Acitivity
,但是不拥有任何服务.被分类到这个下边的进程,有很高的可能性被系统关闭,释放出内存给其他更高优先级的进程使用.ART
有一个后台进程的列表,根据使用频度和系统压力,会选择关闭其中的一些进程.
Empty Process
空进程不包含任何内容,只在内存中等着在其中装入新的应用程序,所以是最低等级的进程.
在实际中,由于出现进程间互相依赖的情况,判断方法不是这么简单,操作系统的进程调度一直都是操作系统最核心也最复杂的内容之一.不过安卓文档里有一条规范,就是一个进程的优先度,不能低于它为之服务的另外一个进程(否则会造成被服务对象还没结束,其依赖的提供服务对象就结束了,导致程序无法运行).
Activity
的生命周期
由于一个应用程序的主要组成部分就是各个Activity
,而应用程序的进程优先级又是由其中的Activity
所决定的,所以了解Activity
的生命周期就非常关键了.
在应用程序运行的时候,其中的各个Activity
会反复被用户使用,系统对于每个应用,使用一个Activity Stack
,一个栈来管理这些Acitivity
.
Activity Stack
对于每个运行中的应用,操作系统会为其维护一个栈,当一个应用程序开始运行的时候,它的第一个Activity
就会被放入这个栈中,第二个Activity
打开的时候,会继续被压入栈中,栈顶的始终是当前活动的Activity
,当一个Activity
终止的时候,会被弹出,其下边的一个Activity
就会变成当前的活动栈.就和浏览器维护的历史记录很像,弹出栈的Activity
会被销毁来释放内存空间.
此外,当资源出现问题的时候,操作系统也会把一些最老的栈清除掉.书上的图不错:
Activity
的状态
有四种状态:
Active/Running
,这表示Activity
处在栈顶端,正在和用户交互,当前应用是前台应用状态.这也是最不可能被系统干掉的情况.Paused
,这个对用户可见,但不是处于活动状态,这种状态的Activity
依然处于内存中,并且其维护的数据也会依然可用,方便快速恢复到Active
状态.Stopped
,对用户不可见,和暂停的Activity
一样,也保有对应的状态和数据,然而被系统干掉的风险更高Killed
,被ART
干掉了,如果想再次运行,就要经历完整的重新启动应用的过程.
导致Activity
状态发生变化的原因
有三种:
- 将这个
Activity
在前后台移动,比如切换到另外一个程序或者页面 - 关闭
Activity
- 系统配置发生变更(比如手机从横屏变成竖屏)
一般来说,凡是会导致Activity
的界面发生变化的配置,都会导致系统先销毁原来的Activity
,再重新创建,这是因为相比其他手段,这个手段最高效.当然,也有一些配置改变并不会导致Activity
发生变化,这个时候就不一定需要重新来一遍.
处理状态变化
处理状态变化有两种方式:
- 在系统调用的生命周期方法里编写处理状态的代码.像之前的
onCreate()
方法,在特定的时候会被ART
调用,只要在其中写上处理状态的代码,在Activity
到某个状态的时候,其中的处理状态变化的代码就会被调用.这也是比较传统的做法. Google
引入的新方法, 使用Jetpack Android Architecture
中提供的涉及到生命周期的类.这个我现在还不清楚书上说的是什么意思.
为什么要管理生命周期,其实主要是用来处理状态(state)变化,所谓状态,其实是指的程序依赖的数据.在一个Activity
的状态发生变化的时候,很可能其依赖的数据也发生了变化,最简单比如一个需要登录的app
,在切换Activity
的时候,就要在状态从活动和其他状态切换的时候,检测用户是否还在登录状态,如果不是,则不能将那些负责登录后功能的Activity
展示给用户,这个逻辑一般就要写在声明周期方法中.说白了,就和web
应用中的有状态组件很类似.
处理生命周期状态变化
这里只来看传统的方法,虽然上边提到了新的方法,但其实很多代码还是使用传统的方法,而且传统的方法对于理解状态变化更深刻,所以还是要深刻掌握的.
Activity
和Fragment
的生命周期方法
以Activity
为例,在之前的例子里,代码中可以直接看到MainActivity
继承的是AppCompatActivity
,继续探究下去,整个继承关系如下:
- `MainActivity
AppCompatActivity
FragmentActivity
ComponentActivity
androidx.core.app.ComponentActivity
Activity
最下边的Activity
以及上边的所有类,都实现了若干方法,比如onCreate()
,这些特定的方法,会在生命周期的不同时刻被ART
调用.Fragment
类也是如此.我们编写的类想要针对生命周期进行响应的时候,需要重写对应的方法.现在就来看一下具体的方法.
onCreate(Bundle savedInstanceState)
Activity
第一次被创建,一些对应的初始化工作完成之后,会调用这个方法,调用这个方法时,Activity
在内存中被创建,但还没有显示到屏幕上.这个方法的参数是从调用者传入的一个Bundle
对象,这个对象一般会含有动态的状态信息,一般和用户界面相关.
onRestart()
在之前处于Stopped
状态,要重新启动的时候,调用该方法.
onStart()
总是在onCreate()
和onRestart()
调用完后立刻调用,调用这个说明界面已经准备好对用户可见.
onResume()
在onStart()
调用之后, 如果Activity
进入栈顶要展示给用户,就会调用这个方法.
onStop()
在onStop()
调用之后, 如果Activity
不位于栈顶,则会调用此方法,此时Activity
对用户不可见.这个方法之后要么是onRestart()
,要么是onDestroy()
.
onPause()
调用这个方法意味着Activity
暂停,所以一些耗时长,重型的方法不要写在这里.
这个方法之后可以接onResume()
,表示重新位于前台,也可能是onStop()
,即停止.
onDestroy()
Activity
要被销毁.一般来说一个页面如果执行了finish()
方法,ART
就会打算将其销毁,从而调用此方法.注意并不是一个Activity
工作结束就一定会调用该方法.
以下是fragment
特有的方法
onAttach()
当一个fragment
附加到Activity
上时候调用
onCreateView()
创建并返回一个fragment
的用户界面的时候调用
onActivityCreated()
fragment
附加到的Activity
的onCreate()
方法执行完毕之后会调用这个方法.
onViewStatusRestored()
fragment
保存的视图继承恢复的时候(我没看懂是什么意思)
和状态有关的方法
除了生命周期方法之外,还需要了解两个和应用状态关系密切的方法
onRestoreInstanceState(Bundle savedInstanceState)
这个方法会在onStart()
执行被调用,一般会在Activity
从之前的状态恢复的时候使用,用来读取之前的数据,也常用做初始化Acitivity
数据的手段.
onSaveInstanceState(Bundle outState)
在Activity
被销毁之前调用,常用来保存这个应用所需的数据.所以可以看到,直接销毁一个应用,这个应用也还是有机会做一些事情的.当应用重新启动的时候,这个函数的参数会传递给onCreate()
和onRestoreInstanceState()
.
在重写这两个方法的时候,一定要记住,必须首先调用一下super
的同名方法,否则会报运行时错误.说了半天,还是上一张图最清晰: