数据管理
对于只读数据,一种常用的管理模式是在onCreate函数中进行数据的加载,直到组件的onDestory函数被调用时在进行释放。
// 缓存只读的数据
private Object readOnlyData;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 读取数据到内存
readOnlyData = readOnlyData();
}
private Object readOnlyData() {
return null ;
}
@Override
protected void onDestroy() {
super.onDestroy();
// 将数据置空,加速回收
readOnlyData = null ;
}
如果数据支持读写操作,则需要在onResume或者onCreate中进行读取,而在onPause中实现存储。
因为当onPause函数被调用后,该界面组件就处于可回收的状态。当资源紧张时,系统会强行销毁组件对象。
对象中所有未持久化的修改就会丢失。对于读写数据处理的模型示例如下:
// 缓存可读写的数据
private Object data;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 读取数据到内存
data = readData();
}
@Override
protected void onResume() {
super.onResume();
data = readData();
}
private Object readData() {
// TODO 读取数据
return null ;
}
private void writeData(Object data) {
// TODO 写入数据
}
@Override
protected void onPause() {
super.onPause();
writeData( data);
}
@Override
protected void onDestroy() {
super.onDestroy();
// 将数据置空,加速回收
data = null ;
}
状态管理
当系统将界面组件切离前台状态(即onPause函数调用前),会先行调用onSavaInstanceState函数。在该函数中,开发者可以讲组件中的状态数据写入参数的outState对象中。outState的对象类型是Bundle,他是通过键值对的方式进行数据的存储。
onCreate —— 如果含有state数据,则先调用onRestoreInstanceState。
onRestoreInstanceState —— 组件进入前台状态前,先调用恢复数据。
onSaveInstanceState —— 组件离开前台状态,先调用状态保存数据,在调用onPause。如果用户是主动离开前台状态,则不会触发该状态。
boolean needSaveDraft = true;
// 如果是非主动离开时,则会调用onSaveInstanceState
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
// 先保存修改状态,用于恢复启动时恢复该信息
outState.putBoolean( "NEED_SAVE_DRAFT", needSaveDraft );
// 表示在被动退出时无需保存
needSaveDraft = false ;
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
if (savedInstanceState != null) {
needSaveDraft = savedInstanceState.getBoolean("NEED_SAVE_DRAFT" );
}
}
@Override
protected void onPause() {
super.onPause();
// 如果不是被动推动,则询问用户
if (needSaveDraft ) {
showAskSaveDraftDialog();
}
}
private void showAskSaveDraftDialog() {
// TODO Auto-generated method stub
}
当前onSaveInstanceState函数调用完成后,存储状态信息的outState对象中的数据就由系统进程代为保管,不论该应用进程是否被系统回收,这些数据都不会丢失。
如果savedInstanceState为空,说明这是一次全新的构造,反之则说明这是一次恢复性的构造。界面组件可以利用该参数中的信息将界面状态恢复到系统回收前的状态。
区分是恢复性构造还是全新的狗仔,是开发中需要妥善处理的细节。如果是全新的构造,界面组件中需要分析调用发送的Intent对象,控制业务流程;而如果是恢复性构造,则需要将上次缓存的信息一一恢复。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
needSaveDraft = savedInstanceState.getBoolean("NEED_SAVE_DRAFT" );
}
else {
// TODO 解析Intent对象传入的参数或者Action
// Intent intent = getIntent();
// intent.getBundleExtra("");
}
}
为了降低开发者的负担,Android中的大部分的系统控件都实现了状态缓存的逻辑。在onSaveInstanceState函数调用前,界面组件会遍历整个控件树,将各个控件保存下来。等到onRestoreInstanceState函数被调用时在进行恢复。
如果系统内置的控件状态缓存逻辑不符合开发者的需求,开发者可以调用View.setSaveEnabled函数关闭对应控件对象的自动缓存,在onSaveInstanceState函数中自行管理控件的状态。
用于状态管理的onSaveInstanceState 和 onRestoreInstanceState并不属于基本的生命周期函数,但是状态管理的操作还是和组件的生命周期有必然的联系,开发者同样需要妥善利用好这些函数,处理由于生命周期变更引起的变化。
注册管理
界面组件在于用户交互的过程中有时候需要随着系统状态的变化及时的更新信息。比如地址信息。
界面组件可以通过监听相关的事件信息来捕获这些变化。如果所监听的事件的变化,仅当组件在前台状态时才需要生效(比如广播事件的监听,地理位置的变更等),则需要早onResume中注册,在onPause中注销。
LocationManager mLocationManager;
LocationListener mLocationListener;
@Override
protected void onResume() {
super.onResume();
mLocationManager.requestLocationUpdates(provider, minTime, minDistance, listener );
}
@Override
protected void onPause() {
super.onPause();
mLocationManager.removeUpdates(mLocationListener );
}
线程管理
在应用开发中,网络通信、数据操作、复杂计算等都需要耗费大量的时间,因此应用通常需要采用多线程的设计模式,在后台线程中执行此类耗时的操作。
Android的组件生命周期,是一个典型的同步处理逻辑。对于多线程架构没有提供良好的支持模型。这个需要开发者根据自己的需求,充分利用好组件的生命周期,合理的安排线程的构造及销毁。
如果线程的生命周期和该组件的生命周期紧密联系,就需要在界面组件生命周期中管理该线程,一旦线程被界面组件构造出来,就需要在onDestory中明确终止该线程,回收其线程空间。否则,将导致线程资源泄漏。
但是仅在onDestory回收线程依然不够完美,因为在资源紧张的情况下,系统会强行回收组件,此时组件的onDestory函数可能并没有调用,从而导致线程资源泄漏。
一个更好的线程管理方案,是将线程的句柄信息当做界面组件的状态信息缓存下来。如果系统强行回收该对象组件,则需要在组件再次被构造时,根据缓存的线程句柄找到该线程,从而避免线程泄露。
static final String THREAD_WORKER_ID = “thread_id”;
Thread workerThread;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
long threadId = savedInstanceState.getLong(THREAD_WORKER_ID);
workerThread = findThreadById(threadId);
}
else {
// TODO 创建新的线程
}
}
private Thread findThreadById( long id) {
// 完善根据线程id,找到该线程 的过程 http://bao231.iteye.com/blog/1917616?utm_source=tuicool
return null ;
}
@Override
protected void onDestroy() {
super.onDestroy();
if (workerThread != null) {
workerThread.interrupt();
workerThread = null ;
}
}
服务组件的生命周期
服务的使用方式可分为两种,分别是调用服务和绑定服务。这两种不同的使用方式下,生命周期略微有不同。
但不论在何种使用模式下,组件的生命周期都是从onCreate中开始,至onDestory中结束。因为服务组件开发中,可以选择在onCreate中做数据加载等初始化工作,而在onDestory中做数据销毁,线程终止等清理工作。
在绑定模式下,onBind函数被调用时,说明服务以及被前台界面组件绑定。服务组件应根据调用者传递的Intent对象,在该函数内加载资源,构建通信对象,等待绑定者的调用。当界面组件完成相关操作时,需会解除与服务组件的绑定。此时,onUnBind函数会被调用,可以在该函数中做一些统计和资源清理工作。
被绑定服务组件的进程状态,与绑定该服务的界面组件密切相关。如果绑定组件为前台界面组件,则改服务所处的进程即为前台进程。反之也相同。
Android系统不会轻易回收前台进程或者可视进程,所以出于绑定状态的组件通常也不会被强制停止。对于开发者而言,绑定服务后一定不要忘记选择在合适的时机接触绑定,否则将使服务组件停留在前台或可视状态无法回收,从而浪费系统资源。
在调用模式,当服务组件执行onStartCommand函数时,服务所在的进程为前台进程,拥有最高的优先级。当onStartCommand函数执行完成后,如果没有显示的调用stopSelf等相关函数来停止服务组件,那么该服务组件将会成为后台组件继续提供服务,直至调用stopSelf函数停止,或者等待系统强行回收。
onStartCommand函数中增加三个返回值和控制参数,用于指定后台服务组件的运行方式,其中最重要的返回值有三个:
START_STICKY —— 系统会对该服务组件负责到底,在强行回收该组件后后,在资源宽裕的时候还会调用onStartCommand函数重新启动该服务。直到调用stopSelf函数。对于开发者而言,编写返回值为START_STRICKY,一定要在合适的时机调用stopSelf函数主动关闭服务,否则会无限期的消耗系统资源。
START_NOT_SRICKY —— 说明系统可以无条件的回收该组件,而无需关注服务是否完成,也不需要负责服务的重新启动。
START_REDELIVER_INTENT —— 则意味着需要保障该服务组件能够完整的处理完每一个Intent对象。
触发器组件的生命周期:
触发器的生命周期是最短暂的,其整个生命周期就是构造触发器对象,然后执行onReceive函数。对于执行完onReceive函数,系统会立即出发销毁触发器的组件对象,回收其占用的资源。
生命周期内,onReceive函数内部不能够处理耗时任务。
数据源组件的生命周期
理论上来说,数据源组件没有所谓的生命周期,因此数据源组件的状态不作为进程优先级的判断依据。所以系统在回收进程资源时,并不会将数据源的销毁事件告知开发者。
但Android会在构造数据源组件时调用onCreate函数。开发者可以在该函数中数据化数据源所需的数据库或者其他数据内容。
由此可知,在数据源组件中部署延迟写入等写优化策略是不合适,因为数据源组件可能会被系统静默回收,从而导致未持久化的写入数据丢失。所以在数据源组件的实现中,写优化策略应该交由上层调用去实现,或者下层数据存储者去处理。
一旦数据源组件构造出来,就会保持长期运行的状态直至其所在的进程被系统回收。所以不要再数据源组件中缓存过多的数据,以免占用内存空间。