BroadcastReceiver的生命周期——从程序的一个bug说起
2011-01-11 16:29
288 查看
前几天遇到了这样一个问题。程序本身定义并注册了一个BroadcastReceiver用来响应某种广播消息,BroadcastReceiver收到消息后需要做一系列的处理,其中包括了比较费时的网络查询操作。于是问题出现了:当手机处于home screen时,BroadcastReceiver收到消息并开始处理,而此时点击该应用程序的图标并试图进入该程序时,会出现ANR错误。这是怎么回事?
经过试验,我发现当系统处于待机状态时,BroadcastReceiver收到消息并开始调用onReceive(Context, Intent)函数时,是运行在进程的主线程中。因此当点击应用程序图标试图启动程序时,由于onReceive(Context, Intent)函数还没有完成,因此只有等待。而Android系统本身会在程序响应超时时弹出ANR错误。
那么我们把这些耗时的处理操作单开一个线程运行,会不会解决这个问题呢?答案是否定的。虽然我在试验中确实发现这样做能让程序正常运行,而且没有ANR错误报出。但是这样方法是错误的。在Android API中关于BroadcastReceiver类生命周期的说明中有这么几段话:
A BroadcastReceiver object is only valid for the duration of the call to
(一个BroadcastReceiver对象只在调用onReceive(Context, Intent)期间有效。一旦从该函数返回,系统会认为该对象已经完成,不再活跃。)
This has important repercussions to what you can do in an
(这会对你能在onReceive(Context, Intent)函数中能做什么有非常大的影响:任何异步操作都是不可用的。因为你需要从该函数返回去进行异步操作,但此时BroadcastReceiver已经不再活跃,因此系统可以在异步操作完成之前杀掉它的进程。)
In particular, you may not show a dialog or bind to a service from within a BroadcastReceiver. For the former, you should instead use the
(尤其是你不能在BroadcastReceiver显示一个对话框或者bind到一个service。对与前者,你应该使用NotificationManager API,而后者,你可以使用Context.startService()去发送一个命令给service。)
这就解释了为什么把这些耗时的处理操作单开一个线程运行是错误的。Android系统并不能保证该操作能够完成。其实正确的做法是使用Context.startService()去发送一个命令给service,让service去做这件事。实际上,我在修改该处代码时使用的是IntentService。根据Android API的说明,IntentService就是一个用于处理异步请求的Service类。Clients通过startService(Intent)方法发送请求(实际是Intent),IntentService会根据需要启动,在工作线程中按序处理到达的请求,当所有请求都完成之后,自动停止服务。由于所有的请求都是在一个线程中按序处理,因此后来的请求需要等待。因此IntentService的使用还是有局限性的。但是在我的程序中,因为请求实际上基本上间隔都比较长,因此,IntentService对于这种情况就已经足够了。
经过试验,我发现当系统处于待机状态时,BroadcastReceiver收到消息并开始调用onReceive(Context, Intent)函数时,是运行在进程的主线程中。因此当点击应用程序图标试图启动程序时,由于onReceive(Context, Intent)函数还没有完成,因此只有等待。而Android系统本身会在程序响应超时时弹出ANR错误。
那么我们把这些耗时的处理操作单开一个线程运行,会不会解决这个问题呢?答案是否定的。虽然我在试验中确实发现这样做能让程序正常运行,而且没有ANR错误报出。但是这样方法是错误的。在Android API中关于BroadcastReceiver类生命周期的说明中有这么几段话:
A BroadcastReceiver object is only valid for the duration of the call to
onReceive(Context, Intent). Once your code returns from this function, the system considers the object to be finished and no longer active.
(一个BroadcastReceiver对象只在调用onReceive(Context, Intent)期间有效。一旦从该函数返回,系统会认为该对象已经完成,不再活跃。)
This has important repercussions to what you can do in an
onReceive(Context, Intent)implementation: anything that requires asynchronous operation is not available, because you will need to return from the function to handle the asynchronous operation, but at that point the BroadcastReceiver is no longer active and thus the system is free to kill its process before the asynchronous operation completes.
(这会对你能在onReceive(Context, Intent)函数中能做什么有非常大的影响:任何异步操作都是不可用的。因为你需要从该函数返回去进行异步操作,但此时BroadcastReceiver已经不再活跃,因此系统可以在异步操作完成之前杀掉它的进程。)
In particular, you may not show a dialog or bind to a service from within a BroadcastReceiver. For the former, you should instead use the
NotificationManagerAPI. For the latter, you can use
Context.startService()to send a command to the service.
(尤其是你不能在BroadcastReceiver显示一个对话框或者bind到一个service。对与前者,你应该使用NotificationManager API,而后者,你可以使用Context.startService()去发送一个命令给service。)
这就解释了为什么把这些耗时的处理操作单开一个线程运行是错误的。Android系统并不能保证该操作能够完成。其实正确的做法是使用Context.startService()去发送一个命令给service,让service去做这件事。实际上,我在修改该处代码时使用的是IntentService。根据Android API的说明,IntentService就是一个用于处理异步请求的Service类。Clients通过startService(Intent)方法发送请求(实际是Intent),IntentService会根据需要启动,在工作线程中按序处理到达的请求,当所有请求都完成之后,自动停止服务。由于所有的请求都是在一个线程中按序处理,因此后来的请求需要等待。因此IntentService的使用还是有局限性的。但是在我的程序中,因为请求实际上基本上间隔都比较长,因此,IntentService对于这种情况就已经足够了。
相关文章推荐
- 开源日志库log4c存在的一个bug,程序重启后,每次都重新写新的文件,不是接在在原来的文件中写;
- 一个尚未发现bug的小程序
- Erlang 程序引发共享内存 bug 的一个例子
- VC6 DEBUG版下内存控制的一个BUG,导致debug版程序必将崩溃
- posix多线程程序使用条件变量的一个常见bug
- 控制一个 Java 程序运行生命周期结束前不能再次被执行
- SWT组件的生命周期(续一个简单的SWT程序实例及详解)
- 减少等待时间:软件开发生命周期的一个“等待减少”程序
- php扩展开发(二 一个程序的生命周期)
- Android之一个很奇怪的生命周期bug
- posix多线程程序使用条件变量的一个常见bug
- 中国的大多数软件的一个bug和我眼中最保险的防止程序运行多次的方法
- WPF中程序启动时的一个BUG
- 一上午搞java编码问题,写了一个将批量文件编码转为UTF-8的小程序(有BUG-.-!)
- 我的一个模拟内存分配的小程序,还存在些Bug,希望高手能够指点!
- 以亲身感受浅谈程序的注释和一个bug的代价(单位:RMB)
- 一个和viewcontroller生命周期相关的bug
- 用C# WinForm写的一个简单的计算器程序(可以输入复杂的表达式),欢迎大家指出Bug
- opencv学习(7) 一个锐化程序(有bug)
- 现在为了让程序兼容vista,我们需要给程序加上一个manifest文件,可是有时候vc6它就会弹出Resource Compiler Error RC2170 的错误,可能是vc6一个bug,我个人对vc6还是很喜欢的,不过经常会有编译死掉或出错的问题,