当前位置:   article > 正文

Android Service 服务(二)—— BroadcastReceiver_安卓开发:service的程序结束后,receiver才发送广播

安卓开发:service的程序结束后,receiver才发送广播

Android 系统里定义了各种各样的广播,如电池的使用状态,电话的接收和短信的接收,开机启动都会产生一个广播。当然用户也可以自定义自己的广播。

既然说到广播,那么必定有一个广播发送者,以及广播接收器。系统广播的发送者为系统,自定义广播当然是用户定义的了。

我们可以定义一个广播接收器,用来接收我们感兴趣的广播,不论是系统广播还是用户自定义广播。这个广播接收器必须继承至BroadcastReceiver。

老规矩,先来点基础知识。

一、 BroadcastReceiver简介

BroadcastReceiver,用于异步接收广播Intent,广播Intent是通过调用Context.sendBroadcast()发送、BroadcastReceiver接收。

广播Intent的发送是通过调用Context.sendBroadcast()、Context.sendOrderedBroadcast()、Context.sendStickyBroadcast()来实现的。通常一个广播Intent可以被订阅了此Intent的多个广播接收者所接收,广播接收者和JMS中的Topic消息接收者很相似。
广播接收器只能接收广播,对广播的通知做出反应,很多广播都产生于系统代码,如:时区改变的通知、电池电量不足、用户改变了语言偏好,或者开机启动等
广播接收器没有用户界面,但是它可以为它们接收到信息启动一个Activity或者使用NotificationManager来通知用户.

BroadcastReceiver 接收广播方式:
1. Normal broadcasts(正常广播),用 Context.sendBroadcast()发送是完全异步的,它们都运行在一个未定义的顺序,通常是在同一时间。这样会更有效,但意味着receiver不能包含所要使用的结果或中止的API。  
2. Ordered broadcasts(有序广播),用 Context.sendOrderedBroadcast()发送每次被发送到一个receiver。所谓有序,就是每个receiver执行后可以传播到下一个receiver,也可以完全中止传播——不传播给其他receiver。 而receiver运行的顺序可以通过matched intent-filter 里面的android:priority来控制,当priority优先级相同的时候,Receiver以任意的顺序运行。

下面给出系统定义好的广播,即系统广播的Action

  1. Intent.ACTION_AIRPLANE_MODE_CHANGED;
  2. //关闭或打开飞行模式时的广播
  3. Intent.ACTION_BATTERY_CHANGED;
  4. //充电状态,或者电池的电量发生变化
  5. //电池的充电状态、电荷级别改变,不能通过组建声明接收这个广播,只有通过Context.registerReceiver()注册
  6. Intent.ACTION_BATTERY_LOW;
  7. //表示电池电量低
  8. Intent.ACTION_BATTERY_OKAY;
  9. //表示电池电量充足,即从电池电量低变化到饱满时会发出广播
  10. Intent.ACTION_BOOT_COMPLETED;
  11. //在系统启动完成后,这个动作被广播一次(只有一次)。
  12. Intent.ACTION_CAMERA_BUTTON;
  13. //按下照相时的拍照按键(硬件按键)时发出的广播
  14. Intent.ACTION_CLOSE_SYSTEM_DIALOGS;
  15. //当屏幕超时进行锁屏时,当用户按下电源按钮,长按或短按(不管有没跳出话框),进行锁屏时,android系统都会广播此Action消息
  16. Intent.ACTION_CONFIGURATION_CHANGED;
  17. //设备当前设置被改变时发出的广播(包括的改变:界面语言,设备方向,等,请参考Configuration.java)
  18. Intent.ACTION_DATE_CHANGED;
  19. //设备日期发生改变时会发出此广播
  20. Intent.ACTION_DEVICE_STORAGE_LOW;
  21. //设备内存不足时发出的广播,此广播只能由系统使用,其它APP不可用?
  22. Intent.ACTION_DEVICE_STORAGE_OK;
  23. //设备内存从不足到充足时发出的广播,此广播只能由系统使用,其它APP不可用?
  24. Intent.ACTION_DOCK_EVENT;
  25. //
  26. //发出此广播的地方frameworks\base\services\java\com\android\server\DockObserver.java
  27. Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE;
  28. 移动APP完成之后,发出的广播(移动是指:APP2SD)
  29. Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE;
  30. //正在移动APP时,发出的广播(移动是指:APP2SD)
  31. Intent.ACTION_GTALK_SERVICE_CONNECTED;
  32. //Gtalk已建立连接时发出的广播
  33. Intent.ACTION_GTALK_SERVICE_DISCONNECTED;
  34. //Gtalk已断开连接时发出的广播
  35. Intent.ACTION_HEADSET_PLUG;
  36. //在耳机口上插入耳机时发出的广播
  37. Intent.ACTION_INPUT_METHOD_CHANGED;
  38. //改变输入法时发出的广播
  39. Intent.ACTION_LOCALE_CHANGED;
  40. //设备当前区域设置已更改时发出的广播
  41. Intent.ACTION_MANAGE_PACKAGE_STORAGE;
  42. //
  43. Intent.ACTION_MEDIA_BAD_REMOVAL;
  44. //未正确移除SD卡(正确移除SD卡的方法:设置--SD卡和设备内存--卸载SD卡),但已把SD卡取出来时发出的广播
  45. //广播:扩展介质(扩展卡)已经从 SD 卡插槽拔出,但是挂载点 (mount point) 还没解除 (unmount)
  46. Intent.ACTION_MEDIA_BUTTON;
  47. //按下"Media Button" 按键时发出的广播,假如有"Media Button" 按键的话(硬件按键)
  48. Intent.ACTION_MEDIA_CHECKING;
  49. //插入外部储存装置,比如SD卡时,系统会检验SD卡,此时发出的广播?
  50. Intent.ACTION_MEDIA_EJECT;
  51. //已拔掉外部大容量储存设备发出的广播(比如SD卡,或移动硬盘),不管有没有正确卸载都会发出此广播?
  52. //广播:用户想要移除扩展介质(拔掉扩展卡)。
  53. Intent.ACTION_MEDIA_MOUNTED;
  54. //插入SD卡并且已正确安装(识别)时发出的广播
  55. //广播:扩展介质被插入,而且已经被挂载。
  56. Intent.ACTION_MEDIA_NOFS;
  57. //
  58. Intent.ACTION_MEDIA_REMOVED;
  59. //外部储存设备已被移除,不管有没正确卸载,都会发出此广播?
  60. // 广播:扩展介质被移除。
  61. Intent.ACTION_MEDIA_SCANNER_FINISHED;
  62. //广播:已经扫描完介质的一个目录
  63. Intent.ACTION_MEDIA_SCANNER_SCAN_FILE;
  64. //
  65. Intent.ACTION_MEDIA_SCANNER_STARTED;
  66. //广播:开始扫描介质的一个目录
  67. Intent.ACTION_MEDIA_SHARED;
  68. // 广播:扩展介质的挂载被解除 (unmount),因为它已经作为 USB 大容量存储被共享。
  69. Intent.ACTION_MEDIA_UNMOUNTABLE;
  70. //
  71. Intent.ACTION_MEDIA_UNMOUNTED
  72. // 广播:扩展介质存在,但是还没有被挂载 (mount)。
  73. Intent.ACTION_NEW_OUTGOING_CALL;
  74. Intent.ACTION_PACKAGE_ADDED;
  75. //成功的安装APK之后
  76. //广播:设备上新安装了一个应用程序包。
  77. //一个新应用包已经安装在设备上,数据包括包名(最新安装的包程序不能接收到这个广播)
  78. Intent.ACTION_PACKAGE_CHANGED;
  79. //一个已存在的应用程序包已经改变,包括包名
  80. Intent.ACTION_PACKAGE_DATA_CLEARED;
  81. //清除一个应用程序的数据时发出的广播(在设置--应用管理--选中某个应用,之后点清除数据时?)
  82. //用户已经清除一个包的数据,包括包名(清除包程序不能接收到这个广播)
  83. Intent.ACTION_PACKAGE_INSTALL;
  84. //触发一个下载并且完成安装时发出的广播,比如在电子市场里下载应用?
  85. //
  86. Intent.ACTION_PACKAGE_REMOVED;
  87. //成功的删除某个APK之后发出的广播
  88. //一个已存在的应用程序包已经从设备上移除,包括包名(正在被安装的包程序不能接收到这个广播)
  89. Intent.ACTION_PACKAGE_REPLACED;
  90. //替换一个现有的安装包时发出的广播(不管现在安装的APP比之前的新还是旧,都会发出此广播?)
  91. Intent.ACTION_PACKAGE_RESTARTED;
  92. //用户重新开始一个包,包的所有进程将被杀死,所有与其联系的运行时间状态应该被移除,包括包名(重新开始包程序不能接收到这个广播)
  93. Intent.ACTION_POWER_CONNECTED;
  94. //插上外部电源时发出的广播
  95. Intent.ACTION_POWER_DISCONNECTED;
  96. //已断开外部电源连接时发出的广播
  97. Intent.ACTION_PROVIDER_CHANGED;
  98. //
  99. Intent.ACTION_REBOOT;
  100. //重启设备时的广播
  101. Intent.ACTION_SCREEN_OFF;
  102. //屏幕被关闭之后的广播
  103. Intent.ACTION_SCREEN_ON;
  104. //屏幕被打开之后的广播
  105. Intent.ACTION_SHUTDOWN;
  106. //关闭系统时发出的广播
  107. Intent.ACTION_TIMEZONE_CHANGED;
  108. //时区发生改变时发出的广播
  109. Intent.ACTION_TIME_CHANGED;
  110. //时间被设置时发出的广播
  111. Intent.ACTION_TIME_TICK;
  112. //广播:当前时间已经变化(正常的时间流逝)。
  113. //当前时间改变,每分钟都发送,不能通过组件声明来接收,只有通过Context.registerReceiver()方法来注册
  114. Intent.ACTION_UID_REMOVED;
  115. //一个用户ID已经从系统中移除发出的广播
  116. //
  117. Intent.ACTION_UMS_CONNECTED;
  118. //设备已进入USB大容量储存状态时发出的广播?
  119. Intent.ACTION_UMS_DISCONNECTED;
  120. //设备已从USB大容量储存状态转为正常状态时发出的广播?
  121. Intent.ACTION_USER_PRESENT;
  122. //
  123. Intent.ACTION_WALLPAPER_CHANGED;
  124. //设备墙纸已改变时发出的广播

二、 BroadcastReceiver注册方式

1 静态注册
AndroidManifest.xml中,application里面,定义receiver并设置要接收的action

  1. <receiver android:name=".receiver.MusicReceiver" >
  2. <intent-filter>
  3. <action android:name="com.homer.receiver.musicReceiver" />
  4. </intent-filter>
  5. </receiver>

2 动态注册
Activity中,需在onStart()中调用registerReceiver()进行注册和在onStop中调用unregisterReceiver()释放服务

  1. private MusicReceiver receiver;
  2. @Override
  3. protected void onStart(){
  4. super.onStart();
  5. receiver = new MusicReceiver();
  6. IntentFilter filter = new IntentFilter();
  7. filter.addAction("com.homer.receiver.musicReceiver");
  8. this.registerReceiver(receiver, filter);
  9. }
  10. @Override
  11. protected void onStop(){
  12. this.unregisterReceiver(receiver);
  13. super.onStop();
  14. }
3 两种注册方式的比较
静态注册方式,由系统来管理receiver,而且程序里的所有receiver,可以在xml里面一目了然
动态注册方式,隐藏在代码中,比较难发现;需要特别注意的是,在退出程序前要记得调用Context.unregisterReceiver()方法。一般在activity的onStart()里面进行注册, onStop()里面进行注销。官方提醒,如果在Activity.onResume()里面注册了,就必须在Activity.onPause()注销。


三、 BroadcastReceiver生命周期

一个BroadcastReceiver 对象只有在被调用onReceive(Context, Intent)的才有效,当从该函数返回后,该对象就无效的了,结束生命周期。
因此从这个特征可以看出,在所调用的onReceive(Context, Intent)函数里,不能有过于耗时的操作,不能使用线程来执行。对于耗时的操作,应该在startService中来完成。因为当得到其他异步操作所返回的结果时,BroadcastReceiver 可能已经无效了。


四、 BroadcastReceiver示例

Activity

  1. public class PlayMusicRecevicer extends Activity implements OnClickListener {
  2. private Button playBtn;
  3. private Button stopBtn;
  4. private Button pauseBtn;
  5. private Button exitBtn;
  6. private Button closeBtn;
  7. private Intent intent;
  8. @Override
  9. public void onCreate(Bundle savedInstanceState) {
  10. super.onCreate(savedInstanceState);
  11. setContentView(R.layout.music_receiver);
  12. playBtn = (Button) findViewById(R.id.play);
  13. stopBtn = (Button) findViewById(R.id.stop);
  14. pauseBtn = (Button) findViewById(R.id.pause);
  15. exitBtn = (Button) findViewById(R.id.exit);
  16. closeBtn = (Button) findViewById(R.id.close);
  17. playBtn.setOnClickListener(this);
  18. stopBtn.setOnClickListener(this);
  19. pauseBtn.setOnClickListener(this);
  20. exitBtn.setOnClickListener(this);
  21. closeBtn.setOnClickListener(this);
  22. }
  23. @Override
  24. public void onClick(View v) {
  25. int op = -1;
  26. intent = new Intent("com.homer.receiver.musicReceiver");
  27. switch (v.getId()) {
  28. case R.id.play: // play music
  29. op = 1;
  30. break;
  31. case R.id.stop: // stop music
  32. op = 2;
  33. break;
  34. case R.id.pause: // pause music
  35. op = 3;
  36. break;
  37. case R.id.close: // close activity
  38. this.finish();
  39. break;
  40. case R.id.exit: // process by MusicReceiver
  41. op = 4;
  42. this.finish();
  43. break;
  44. }
  45. Bundle bundle = new Bundle();
  46. bundle.putInt("op", op);
  47. intent.putExtras(bundle);
  48. sendBroadcast(intent); // sendBroadcast
  49. }
  50. // private MusicReceiver receiver;
  51. //
  52. // @Override
  53. // protected void onStart(){
  54. // super.onStart();
  55. //
  56. // receiver = new MusicReceiver();
  57. // IntentFilter filter = new IntentFilter();
  58. // filter.addAction("com.homer.receiver.musicReceiver");
  59. // this.registerReceiver(receiver, filter);
  60. // }
  61. //
  62. // @Override
  63. // protected void onStop(){
  64. // this.unregisterReceiver(receiver);
  65. //
  66. // super.onStop();
  67. // }
  68. @Override
  69. public void onDestroy(){
  70. super.onDestroy();
  71. if(intent != null){
  72. stopService(intent);
  73. }
  74. }
  75. }
BroadcastReceiver

  1. public class MusicReceiver extends BroadcastReceiver { // receive Broadcast
  2. @Override
  3. public void onReceive(Context context, Intent intent) {
  4. if(intent != null){
  5. Bundle bundle = intent.getExtras();
  6. Intent it = new Intent(context, MusicReceiverService.class); // call service for MusicReceiverService.class
  7. it.putExtras(bundle);
  8. if(bundle != null){
  9. int op = bundle.getInt("op");
  10. if(op == 4){
  11. context.stopService(it); // stopService
  12. }else{
  13. context.startService(it); // startService
  14. }
  15. }
  16. }
  17. }
  18. }
Service (BroadcastReceiver调用的后台服务)
  1. public class MusicReceiverService extends Service {
  2. private MediaPlayer mediaPlayer;
  3. @Override
  4. public IBinder onBind(Intent arg0) {
  5. return null;
  6. }
  7. @Override
  8. public void onCreate() {
  9. Toast.makeText(this, "show media player", Toast.LENGTH_SHORT).show();
  10. if (mediaPlayer == null) {
  11. mediaPlayer = MediaPlayer.create(this, R.raw.tmp);
  12. mediaPlayer.setLooping(false);
  13. }
  14. }
  15. @Override
  16. public void onDestroy() {
  17. Toast.makeText(this, "stop media player", Toast.LENGTH_SHORT);
  18. if (mediaPlayer != null) {
  19. mediaPlayer.stop();
  20. mediaPlayer.release();
  21. }
  22. }
  23. @Override
  24. public void onStart(Intent intent, int startId) {
  25. if (intent != null) {
  26. Bundle bundle = intent.getExtras();
  27. if (bundle != null) {
  28. int op = bundle.getInt("op");
  29. switch (op) {
  30. case 1:
  31. play();
  32. break;
  33. case 2:
  34. stop();
  35. break;
  36. case 3:
  37. pause();
  38. break;
  39. }
  40. }
  41. }
  42. }
  43. public void play() {
  44. if (!mediaPlayer.isPlaying()) {
  45. mediaPlayer.start();
  46. }
  47. }
  48. public void pause() {
  49. if (mediaPlayer != null && mediaPlayer.isPlaying()) {
  50. mediaPlayer.pause();
  51. }
  52. }
  53. public void stop() {
  54. if (mediaPlayer != null) {
  55. mediaPlayer.stop();
  56. try {
  57. mediaPlayer.prepare(); // 在调用stop后如果需要再次通过start进行播放,需要之前调用prepare函数
  58. } catch (IOException ex) {
  59. ex.printStackTrace();
  60. }
  61. }
  62. }
  63. }
AndroidManifest.xml

  1. <service
  2. android:name=".receiver.MusicReceiverService"
  3. android:enabled="true" >
  4. <intent-filter>
  5. <action android:name="com.homer.service.musicReceiverService" />
  6. </intent-filter>
  7. </service>
  8. <receiver android:name=".receiver.MusicReceiver" >
  9. <intent-filter>
  10. <action android:name="com.homer.receiver.musicReceiver" />
  11. </intent-filter>
  12. </receiver>

五、代码解析

1、Activity中,PlayMusicService中通过重写OnClickListener 接口onClick()方法实现对播放音乐的控制,把音乐各种操作用数字通过Intent传递给service

然后通过构造一个Intent , intent = new Intent("com.homer.receiver.musicReceiver"); 

其中,com.homer.receiver.musicReceiver是 AndroidManifest.xml 对receiver的定义(或动态注册addAction为filter.addAction("com.homer.receiver.musicReceiver");)

2、Activity中,音乐播放的控制,利用Bundle绑定数字op后,通过 sendBroadcast(intent); 广播出去
Bundle bundle = new Bundle();
bundle.putInt("op", op);
intent.putExtras(bundle);

startService(intent);

3、 BroadcastReceiver中,会处理Activity启动的 sendBroadcast(intent); 广播,通过实现onReceive()方法,解析Activity中Intent的Bundle数据。

然后通过Intent it = new Intent(context, MusicReceiverService.class); 初始化一个启动Service服务的Intent

最后根据解析bundle的op数值决定启动context.startService(it); 服务 或 关闭context.stopService(it); 服务

4、Service中,处理BroadcastReceiver广播启动的MusicReceiverService服务,即依次调用service的启动过程:onCreate --> onStart(可多次调用) --> onDestroy

onCreate(),  创建mediaPlayer

onStart(),      通过获取Bundle bundle = intent.getExtras();,提取int op = bundle.getInt("op");,然后执行响应的音乐播放操作

onDestroy(),停止并释放mediaPlayer音乐资源,如果当执行context.stopService()时调用此方法

5、Activity中,onClick()函数中close与exit是执行含义是不同的:

close : 只是执行了this.finish(); 关闭了本Activity窗体,service并没有被关掉,音乐依然会继续在后台播放

exit  : 先调用了stopService(intent); 关闭了service服务,在Service中会调用3中的onDestroy()停止并释放音乐资源,后才执行this.finish(); 关闭了本Activity窗体


六、BroadcastReceiver总结

BroadcastReceiver需要先注册receriver(静态或动态)—> 发送广播sendBroadcast(intent) —> 处理广播onReceive(Context context, Intent intent) —> 启动服务startService(it) —> 关闭服务stopService(it) 

其中,receriver两种注册方式,静态注册在AndroidManifest.xml中的receiver和动态注册在PlayMusicRecevicer注释的代码部分,两者选择一种即可

代码下载

文章出处:http://www.cnblogs.com/zhangdongzi/archive/2012/01/08/2316217.html

http://blog.csdn.net/ithomer/article/details/7365147


声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/902023
推荐阅读
相关标签
  

闽ICP备14008679号