安卓开发笔记——Broadcast广播机制(实现自定义小闹钟)
什么是广播机制?
简单点来说,是一种广泛运用在程序之间的传输信息的一种方式。比如,手机电量不足10%,此时系统会发出一个通知,这就是运用到了广播机制。
广播机制的三要素:
Android广播机制包含三个要素:广播(Broadcast) - 用于发送广播;广播接收器(BroadcastReceiver) - 用于接收广播;意图(Intent)-用于保存广播相关信息的媒介。
可以把它理解成我们的传统电视台,我们的电视就是一个广播接收器,然而电视有很多频道,是由电视台发送出来的,也就是广播了,意图可以理解成不同频道所播放的不同电视内容。我们每次只能观看一个频道,也就是说一个广播接收器只能接受一个广播,至于如何去区分到底是哪个广播接收器接收哪个广播,这里会有个过滤器intent-filter,我们只需要去设置intent的action标示符即可(下文会提到)。
首先,先来说下广播接收器(BroadcastReceiver):
我们要使用一个广播需要先注册它,就像我们新添一个Activity一样,需要在AndroidManifest.xml里面声明,广播亦是如此。
广播接收器注册,这里提供了2种方式
1、静态注册,也就是在配置文件AndroidManifest.xml声明:
1 <receiver 2 android:name="广播接收器所在的包名类名" 3 android:enabled="true" 4 android:process=":remote" > 5 <intent-filter> 6 <action android:name="过滤标示符,用来区分接收哪个广播" /> 7 </intent-filter> 8 </receiver>
2、代码动态注册:
1 //创建一个广播接受者 2 MyReceiver receiver = new MyReceiver(); 3 //创建过滤器,并指定action,使之用于接收同action的广播 4 IntentFilter filter = new IntentFilter("过滤标示符,用来区分接收哪个广播");
5 //注册广播接收器
6 Context.registerReceiver(receiver, filter);
通过静态方式注册的广播接收器,不需要手动注销,该对象的实例在onReceive被调用之后就会在任意时间内被销毁,而通过动态方式注册的广播接收器,则需要在Activity进入停止或者销毁状态的时候使用unregisterReceiver方法手动注销。
1 //注销广播接收器 2 unregisterReceiver(receiver);
创建广播接收器也很简单,只需要去继承BroadcastRecevice并实现OnReceive方法即可,当广播发送后,系统会去检查广播接收器的过滤器与广播所发送的Intent是否一致, 如果一致调用OnReceive方法,若没有,广播接收器会一直存在着。
1 public class MyReceiver extends BroadcastReceiver { 2 3 @Override 4 public void onReceive(Context context, Intent intent) { 5 // TODO Auto-generated method stub 6 } 7 }
注意事项:
BroadcastReceiver的生命周期只有10秒,不要在OnReceive方法内执行任何耗时操作,若要执行耗时操作可以通过发送Intent给Service操作,切记不能去开子线程,由于BroadcastReceiver只有10秒的生命周期,当宿主线程挂了,那么子线程也自动销毁了。
广播(Broadcast):
先来说下广播的种类,分为三种:
普通广播(Normal Broadcasts):
1、所有广播接收者都可以接收到的广播,同级别的接收者接收顺序随机不确定。
2、不能拦截广播的继续传播也不能处理处理广播
3、同级别动态注册高于静态注册
有序广播(Ordered Broadcasts):
1、按照接收者的优先级接收,优先级可以在intnt-filter里的priority里设置,值越大,优先级越高。
2、可以拦截广播的继续传播,高级别的广播接收器可以决定低级别的广播接收器是否能接收到广播。可以处理广播。
3、同级别动态注册高于静态注册
黏性广播(Sticky Broadcasts):
1、不能处理广播传递给下一个接收者,而且广播一直存在,不销毁。(不常用)
发送广播的方式:
以上3种广播的发送方式分别是:
Context.sendBroadcast()
Context.sendOrderBroadcast()
Context.sendStickyBroadcast()
随手写了个简单小demo,看下效果:
广播接收类:
1 package com.example.broadcasttest; 2 3 import android.content.BroadcastReceiver; 4 import android.content.Context; 5 import android.content.Intent; 6 import android.widget.Toast; 7 8 public class MyBroadcastReceive extends BroadcastReceiver { 9 10 @Override 11 public void onReceive(Context context, Intent intent) { 12 Toast.makeText(context, "广播被收到了!", Toast.LENGTH_SHORT).show(); 13 } 14 15 }
主程序类:
1 package com.example.broadcasttest; 2 3 import android.app.Activity; 4 import android.content.Intent; 5 import android.os.Bundle; 6 import android.view.View; 7 import android.view.View.OnClickListener; 8 import android.widget.Button; 9 10 public class MainActivity extends Activity { 11 12 private Button bt; 13 @Override 14 protected void onCreate(Bundle savedInstanceState) { 15 super.onCreate(savedInstanceState); 16 setContentView(R.layout.activity_main); 17 18 this.bt=(Button) findViewById(R.id.button); 19 bt.setOnClickListener(new OnClickListener() { 20 21 @Override 22 public void onClick(View v) { 23 Intent intent=new Intent(); 24 intent.putExtra("msg", "你好,我是一个广播"); 25 intent.setAction("com.lcw.broadcast"); 26 intent.setClass(MainActivity.this, MyBroadcastReceive.class); 27 28 MainActivity.this.sendBroadcast(intent); 29 } 30 }); 31 32 } 33 34 }
AndroidManifest.xml
1 <receiver 2 android:name="com.example.broadcasttest.MyBroadcastReceive" 3 > 4 <intent-filter> 5 <action android:name="com.lcw.broadcast"></action> 6 </intent-filter> 7 </receiver>
配合PendingIntent和广播机制实现一个小闹钟:
先看来先什么是PendingIntent:
Intent的主要功能是表示用户的一种操作意图,当用户使用Intent之后则立刻执行用户所需要的操作,但是在Android之中也提供了一个PendingIntent操作,表示的是将要发生的操作,所谓的将要发生的Intent指的是在当前的Activity不立即使用此Intent进行处理,而将此Intent封装后传递给其他的Activity程序,而其他的Activity程序在需要使用此Intent时才进行操作。(更简单来讲:PendingIntent就是Intent的延迟包装类)
下面是PendingIntent的一些常用API:
No.
|
方法及常量
|
类型
|
描述
|
1
|
public static final int FLAG_CANCEL_CURRENT
|
常量
|
重新生成一个新的PendingIntent对象
|
2
|
public static final int FLAG_NO_CREATE
|
常量
|
如果不存在PendingIntent对象,则创建一个新的
|
3
|
public static final int FLAG_ONE_SHOT
|
常量
|
创建的PendingIntent对象只使用一次
|
4
|
public static final int FLAG_UPDATE_CURRENT
|
常量
|
如果PendintIntent对象已经存在,则直接使用,并且实例化一个新的Intent对象
|
5
|
public static PendingIntent getActivity(Context context, int requestCode, Intent intent, int flags)
|
普通
|
通过PendingIntent启动一个新的Activity
|
6
|
public static PendingIntent getBroadcast(Context context, int requestCode, Intent intent, int flags)
|
普通
|
通过PendingIntent启动一个新的Broadcast
|
7
|
public static PendingIntent getService(Context context, int requestCode, Intent intent, int flags)
|
普通
|
通过PendingIntent启动一个新的Service
|
好了,直接代码说话吧,国际惯例先看下效果图:
上图的效果是利用PendingIntent包装一个意图为系统闹钟的Intent延迟到指定时间发送广播,然后定义了一个广播接收器不断扫描系统,一旦接收到此广播立即在广播接受器的OnReceive方法里调用另一个Activity,而这个Activity仅仅是一个对话框用来显示信息,如二图。由于使用到了广播机制,所以就算不开着这个Activity也可以在后台监控着这个广播。
看下具体代码吧,首先是这个对话框类,很简单的一个AlertDialog
1 package com.example.alarmtest; 2 3 import java.text.SimpleDateFormat; 4 import java.util.Date; 5 6 import android.app.Activity; 7 import android.app.AlertDialog; 8 import android.content.DialogInterface; 9 import android.os.Bundle; 10 11 /** 12 * 13 * 闹钟警报类(对话框显示) 14 * 15 */ 16 public class AlarmActivity extends Activity { 17 @Override 18 protected void onCreate(Bundle savedInstanceState) { 19 super.onCreate(savedInstanceState); 20 new AlertDialog.Builder(AlarmActivity.this) 21 .setIcon(R.drawable.ic_launcher) 22 .setTitle("闹钟提醒") 23 .setMessage( 24 "闹钟响起,当前时间为:" 25 + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss") 26 .format(new Date())) 27 .setPositiveButton("关闭", new DialogInterface.OnClickListener() { 28 29 @Override 30 public void onClick(DialogInterface dialog, int which) { 31 AlarmActivity.this.finish(); 32 } 33 }).show(); 34 } 35 }
再来看下广播接收器类,这里只是在Receive方法里调用上面那个Activity
1 package com.example.alarmtest; 2 3 import android.content.BroadcastReceiver; 4 import android.content.Context; 5 import android.content.Intent; 6 /** 7 * 8 * 广播接收类(跳转闹钟提醒类) 9 * 10 */ 11 public class AlarmBroadcastReceiver extends BroadcastReceiver { 12 13 @Override 14 public void onReceive(Context context, Intent intent) { 15 Intent intent2=new Intent(context,AlarmActivity.class); 16 intent2.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 17 context.startActivity(intent2); 18 } 19 20 }
看下主程序类
1 package com.example.alarmtest; 2 3 import java.util.Calendar; 4 5 import android.app.Activity; 6 import android.app.AlarmManager; 7 import android.app.PendingIntent; 8 import android.content.Intent; 9 import android.os.Bundle; 10 import android.view.View; 11 import android.view.View.OnClickListener; 12 import android.widget.Button; 13 import android.widget.TextView; 14 import android.widget.TimePicker; 15 import android.widget.Toast; 16 import android.widget.TimePicker.OnTimeChangedListener; 17 18 public class MainActivity extends Activity { 19 // 声明界面控件 20 private TimePicker timePicker; 21 private TextView textView; 22 private Button set; 23 private Button cancel; 24 25 // 变量 26 private int hourOfDay = 0; 27 private int minute = 0; 28 29 // 日期操作 30 private Calendar calendar; 31 32 // 闹钟管理 33 private AlarmManager alarmManager; 34 35 @Override 36 protected void onCreate(Bundle savedInstanceState) { 37 super.onCreate(savedInstanceState); 38 setContentView(R.layout.activity_main); 39 initView();// 初始化控件 40 initAction();// 初始化事件 41 } 42 43 private void initAction() { 44 this.timePicker.setOnTimeChangedListener(new OnTimeChangedListener() {// 设置时间控件监听 45 46 @Override 47 public void onTimeChanged(TimePicker view, int hourOfDay, 48 int minute) {// 这是回调函数,hourOfDay指当前选中时间,minute指当前选中分钟 49 // 实例化并设置Calendar类 50 MainActivity.this.calendar = Calendar.getInstance(); 51 MainActivity.this.calendar.setTimeInMillis(System 52 .currentTimeMillis()); 53 MainActivity.this.calendar.set(Calendar.HOUR_OF_DAY, 54 hourOfDay); 55 MainActivity.this.calendar.set(Calendar.MINUTE, minute); 56 MainActivity.this.calendar.set(Calendar.SECOND, 0); 57 // 存储变量值,便于一会更新TextView控件 58 MainActivity.this.hourOfDay = hourOfDay; 59 MainActivity.this.minute = minute; 60 61 } 62 }); 63 64 this.set.setOnClickListener(new OnClickListener() {// 设置确定按钮监听 65 66 @Override 67 public void onClick(View v) { 68 Intent intent = new Intent(MainActivity.this, 69 AlarmBroadcastReceiver.class); 70 intent.setAction("com.lcw.alarm"); 71 PendingIntent operation = PendingIntent.getBroadcast( 72 MainActivity.this, 0, intent, 73 PendingIntent.FLAG_UPDATE_CURRENT);// 启动一个广播,PendingIntent为Intent的包装 74 MainActivity.this.alarmManager 75 .set(AlarmManager.RTC_WAKEUP, 76 MainActivity.this.calendar.getTimeInMillis(), 77 operation); 78 MainActivity.this.textView.setText("闹钟时间:" 79 + MainActivity.this.hourOfDay + "时" 80 + MainActivity.this.minute + "分" + "00秒"); 81 Toast.makeText(MainActivity.this, "闹钟设置完毕!", Toast.LENGTH_SHORT) 82 .show(); 83 84 } 85 }); 86 87 this.cancel.setOnClickListener(new OnClickListener() {// 取消按钮监听 88 89 @Override 90 public void onClick(View v) { 91 Intent intent = new Intent(MainActivity.this, 92 AlarmBroadcastReceiver.class); 93 intent.setAction("com.lcw.alarm"); 94 PendingIntent operation = PendingIntent.getBroadcast( 95 MainActivity.this, 0, intent, 96 PendingIntent.FLAG_UPDATE_CURRENT); 97 MainActivity.this.alarmManager.cancel(operation); 98 MainActivity.this.textView.setText("当前闹钟未设置"); 99 Toast.makeText(MainActivity.this, "闹钟已删除!", 100 Toast.LENGTH_SHORT).show(); 101 } 102 }); 103 104 } 105 106 private void initView() { 107 this.timePicker = (TimePicker) findViewById(R.id.timepicker); 108 this.timePicker.setIs24HourView(true);// 设置时间控件24小时制 109 this.textView = (TextView) findViewById(R.id.timetext); 110 this.set = (Button) findViewById(R.id.set); 111 this.cancel = (Button) findViewById(R.id.cancel); 112 113 MainActivity.this.alarmManager = (AlarmManager) MainActivity.this 114 .getSystemService(ALARM_SERVICE);// 获取系统闹钟管理实例 115 } 116 117 }