多应用+插件架构,代码干净,二开方便,首家独创一键云编译技术,文档视频完善,免费商用码云13.8K 广告
> 编写:[kesenhoo](https://github.com/kesenhoo) - 原文:[http://developer.android.com/training/run-background-service/report-status.html](http://developer.android.com/training/run-background-service/report-status.html) 这章节会演示如何回传IntentService中执行的任务状态与结果给发送方。 例如,回传任务的状态给[Activity](# "An activity represents a single screen with a user interface.")并进行更新UI。推荐的方式是使用[LocalBroadcastManager](http://developer.android.com/reference/android/support/v4/content/LocalBroadcastManager.html),这个组件可以限制broadcast Intent只在自己的App中进行传递。 ### 利用IntentService 发送任务状态 为了在IntentService中向其他组件发送任务状态,首先创建一个Intent并在data字段中包含需要传递的信息。作为一个可选项,还可以给这个Intent添加一个action与data URI。 下一步,通过执行[LocalBroadcastManager.sendBroadcast()](http://developer.android.com/reference/android/support/v4/content/LocalBroadcastManager.html#sendBroadcast(android.content.Intent)) )来发送Intent。Intent被发送到任何有注册接受它的组件中。为了获取到LocalBroadcastManager的实例,可以执行getInstance().代码示例如下: ~~~ public final class Constants { ... // 定义一个自定义的Intent动作名 public static final String BROADCAST_ACTION = "com.example.android.threadsample.BROADCAST"; ... // 定义一个附加键名来表示状态用于包装在一个Intent中 public static final String EXTENDED_DATA_STATUS = "com.example.android.threadsample.STATUS"; ... } public class RSSPullService extends IntentService { ... /* * 创建一个新的包含名Uri的对象 * BROADCAST_ACTION是一个自定义的Intent动作 * */ Intent localIntent = new Intent(Constants.BROADCAST_ACTION) // 把status放到Intent中 .putExtra(Constants.EXTENDED_DATA_STATUS, status); // 广播Intent给应用中的接收器 LocalBroadcastManager.getInstance(this).sendBroadcast(localIntent); ... } ~~~ 下一步是在发送任务的组件中接收发送出来的broadcast数据。 ### 接收来自IntentService的状态广播 为了接受广播的数据对象,需要使用BroadcastReceiver的子类并实现[BroadcastReceiver.onReceive()](http://developer.android.com/reference/android/content/BroadcastReceiver.html#onReceive(android.content.Context,android.content.Intent) )的方法,这里可以接收LocalBroadcastManager发出的广播数据。 ~~~ // 广播接收器,用于接受IntentService更新的状态 private class ResponseReceiver extends BroadcastReceiver { // 防止实例化 private DownloadStateReceiver() { } // 它会被注册来接收Intent,当广播接收器获得这个Intent时调用。 @Override public void onReceive(Context context, Intent intent) { ... /* * 在这里处理Intent. */ ... } } ~~~ 一旦定义了BroadcastReceiver,也应该定义actions,categories与data用来做广播过滤。为了实现这些,需要使用[IntentFilter](http://developer.android.com/reference/android/content/IntentFilter.html).如下所示: ~~~ // 用于现实照片的类 public class DisplayActivity extends FragmentActivity { ... public void onCreate(Bundle stateBundle) { ... super.onCreate(stateBundle); ... // 过滤器的动作条件是自定义的BROADCAST_ACTION IntentFilter mStatusIntentFilter = new IntentFilter( Constants.BROADCAST_ACTION); // 为http协议添加一个数据过滤器 mStatusIntentFilter.addDataScheme("http"); ~~~ 为了给系统注册这个BroadcastReceiver和IntentFilter,需要通过LocalBroadcastManager执行registerReceiver()的方法。如下所示: ~~~ // 实例化一个新的 DownloadStateReceiver DownloadStateReceiver mDownloadStateReceiver = new DownloadStateReceiver(); // 注册DownloadStateReceiver和Intent过滤器 LocalBroadcastManager.getInstance(this).registerReceiver( mDownloadStateReceiver, mStatusIntentFilter); ... ~~~ 一个BroadcastReceiver可以处理多种类型的广播数据。每个广播数据都有自己的ACTION。这个功能使得不用定义多个不同的BroadcastReceiver来分别处理不同的ACTION数据。为BroadcastReceiver定义另外一个IntentFilter,只需要创建一个新的IntentFilter并重复执行registerReceiver()即可。例如: ~~~ /* * 实例化一个新的动作过滤器. * 不需要数据类型过滤器. */ statusIntentFilter = new IntentFilter(Constants.ACTION_ZOOM_IMAGE); ... // Registers the receiver with the new filter // 用新的意图过滤器注册广播接收器 LocalBroadcastManager.getInstance(getActivity()).registerReceiver( mDownloadStateReceiver, mIntentFilter); ~~~ 发送一个广播Intent并不会启动或重启一个[Activity](# "An activity represents a single screen with a user interface.")。即使是你的app在后台运行,[Activity](# "An activity represents a single screen with a user interface.")的BroadcastReceiver也可以接收、处理Intent对象。但是这不会迫使你的app进入前台。当你的app不可见时,如果想通知用户一个发生在后台的事件,建议使用[Notification](http://developer.android.com/reference/android/app/Notification.html)。永远不要为了响应一个广播Intent而去启动[Activity](# "An activity represents a single screen with a user interface.")。 **笔者评论:**使用LocalBroadcastManager结合IntentService其实是一种很典型高效的做法,同时也更符合OO的思想,通过广播注册与反注册的方式,对两个组件进行解耦。如果使用Handler传递到后台线程作为回调,容易带来的内存泄漏。原因是:匿名内部类对外面的Actvitiy持有引用,如果在Acitivity被销毁的时候,没有对Handler进行显式的解绑,会导致[Activity](# "An activity represents a single screen with a user interface.")无法正常销毁,这样自然就有了内存泄漏。当然,如果用文章中的方案,通常也要记得在[Activity](# "An activity represents a single screen with a user interface.")的onPause的时候进行unRegisterReceiver,除非你有充足的理由为解释这里为何要继续保留。