概念
- 现实中的广播:电台为了传达一些消息,而发送的广播,通过广播携带要传达的消息,群众只要买一个收音机,就可以收到广播了。
- Android中的广播:系统在运行过程中,会发生很多事件,系统为了让其他应用知道系统发生了这个事件,会发送一个对应该事件的广播,比如:电量改变、收到短信、拨打电话、屏幕解锁、系统开机,应用只要注册一个广播接收者,就可以接收到系统发出的广播。
注册
静态注册
- 定义一个类继承BroadcastReceiver,要求实现一个onReceive()方法,该方法在接收到广播时调用。
- 在清单文件中配置该类,指定接收的广播种类。注意,这种方式的注册是常驻型的,也就是说当应用关闭后,如果有广播信息传来,Receiver也会被系统调用而自动运行。
<receiver android:name=".Receive">
<intent-filter>
<action android:name="android.intent.action.ACTION_POWER_CONNECTED"></action>
</intent-filter>
</receiver>
- 广播是通过intent发送的,intent中会携带一个action,系统会在所有清单文件中寻找,看哪一个广播接收者的intent-filter和广播中的intent是匹配的,那么这个广播接收者就会收到这条广播。
- 一条广播可被多个广播接收者接收,一个广播接收者可以接收多条广播,广播接收者本身不带界面,但是它可以接收到广播后启动一个avtivity或者启动一个service。如下图:
动态注册
- 动态注册是通过代码来实现的,通常在一个activity或者一个service中通过代码来指定广播的地址。
MyReceiver receiver = new MyReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction("android.intent.action.MY_BROADCAST");
registerReceiver(receiver, filter);
注意,registerReceiver是android.content.ContextWrapper类中的方法,Activity和Service都继承了ContextWrapper,所以可以直接调用。在实际应用中,我们在Activity或Service中注册了一个BroadcastReceiver,当这个Activity或Service被销毁时如果没有解除注册,系统会报一个异常,提示我们是否忘记解除注册了。所有我们的恰当的地方要解除注册。
@Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(receiver);
}
执行这样行代码就可以解决问题了。注意,这种注册方式与静态注册相反,不是常驻型的,也就是说广播会跟随程序的生命周期。
我们可以根据以上任意一种方法完成注册,当注册完成之后,这个接收者就可以正常工作了。我们可以用以下方式向其发送一条广播:
public void send(View view) {
Intent intent = new Intent("android.intent.action.MY_BROADCAST");
intent.putExtra("msg", "hello receiver.");
sendBroadcast(intent);
}
注意,sendBroadcast也是android.content.ContextWrapper类中的方法,它可以将一个指定地址和参数信息的Intent对象以广播的形式发送出去。
广播分类
普通广播
普通广播对于多个接收者来说是完全异步的,通常每个接收者都无需等待即可以接收到广播,接收者相互之间不会有影响。对于这种广播,接收者无法终止广播,即无法阻止其他接收者的接收动作。所有与广播中的intent匹配的广播接收者,都可以收到这条广播,并且不分先后顺序,视为同时收到。
自定义广播
我们可以自定义一个广播,自己来发。
public void click(View v ){
Intent intent = new Intent();
intent.setAction("a.b.c");
sendBroadcast(intent);
}
有序广播
有序广播比较特殊,它每次只发送到优先级较高的接收者那里,然后由优先级高的接受者再传播到优先级低的接收者那里,优先级高的接收者有能力终止这个广播和修改广播中的值。
有序广播只需要在清单文件定义广播的时候,多加一个android:priority属性,并且依次减小。这个属性的范围在-1000到1000,数值越大,优先级越高。
对于有序广播可以通过下面两种方法来修改广播中的参数值和拦截广播:
setResultData();
abortBroadcast();
在发送有序广播时,可以不在清单文件中定义广播接收者,而是把广播接收者的类直接作为参数传到方法里面,意味着这个广播接收者是最后接收到这个广播的人。不管前面有没有拦截,这个最后的广播接收者一定会接收到这条广播。
public void click(View v){
Intent intent = new Intent();
intent.setAction("com.center.fdm");
//发送自定义有序广播
//resultReceiver:在所有广播接收者都收到广播之后,才会收到,一定是最后一个收到,并且一定能收到
sendOrderedBroadcast(intent, null, new MyReceiver(), null, 0, "哈哈哈", null);}
class MyReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
String order = getResultData();
System.out.println("对方说:" + order);
}
}
}
闹钟
闹钟运用了广播,当设定时间到时,发广播出去,设置一个广播接收者接收这个广播后做提醒的操作即可。
闹钟需要用到AlarmManager.
1、AlarmManager,顾名思义,就是“提醒”,是Android中常用的一种系统级别的提示服务,在特定的时刻为我们广播一个指定的Intent。简单的说就是我们设定一个时间,然后在该时间到来时,AlarmManager为我们广播一个我们设定的Intent,通常我们使用 PendingIntent,PendingIntent可以理解为Intent的封装包,简单的说就是在Intent上在加个指定的动作。在使用Intent的时候,我们还需要在执行startActivity、startService或sendBroadcast才能使Intent有用。而PendingIntent的话就是将这个动作包含在内了。
定义一个PendingIntent对象。
PendingIntent pi = PendingIntent.getBroadcast(this,0,intent,0);
2、AlarmManager的常用方法有三个:
(1)set(int type,long startTime,PendingIntent pi);
该方法用于设置一次性闹钟,第一个参数表示闹钟类型,第二个参数表示闹钟执行时间,第三个参数表示闹钟响应动作。
(2)setRepeating(int type,long startTime,long intervalTime,PendingIntent pi);
该方法用于设置重复闹钟,第一个参数表示闹钟类型,第二个参数表示闹钟首次执行时间,第三个参数表示闹钟两次执行的间隔时间,第三个参数表示闹钟响应动作。
(3)setInexactRepeating(int type,long startTime,long intervalTime,PendingIntent pi);
该方法也用于设置重复闹钟,与第二个方法相似,不过其两个闹钟执行的间隔时间不是固定的而已。
3、三个方法各个参数详悉:
(1)int type: 闹钟的类型,常用的有5个值:AlarmManager.ELAPSED_REALTIME、 AlarmManager.ELAPSED_REALTIME_WAKEUP、AlarmManager.RTC、 AlarmManager.RTC_WAKEUP、AlarmManager.POWER_OFF_WAKEUP。
AlarmManager.ELAPSED_REALTIME表示闹钟在手机睡眠状态下不可用,该状态下闹钟使用相对时间(相对于系统启动开始),状态值为3;
AlarmManager.ELAPSED_REALTIME_WAKEUP表示闹钟在睡眠状态下会唤醒系统并执行提示功能,该状态下闹钟也使用相对时间,状态值为2;
AlarmManager.RTC表示闹钟在睡眠状态下不可用,该状态下闹钟使用绝对时间,即当前系统时间,状态值为1;
AlarmManager.RTC_WAKEUP表示闹钟在睡眠状态下会唤醒系统并执行提示功能,该状态下闹钟使用绝对时间,状态值为0;
AlarmManager.POWER_OFF_WAKEUP表示闹钟在手机关机状态下也能正常进行提示功能,所以是5个状态中用的最多的状态之一,该状态下闹钟也是用绝对时间,状态值为4;不过本状态好像受SDK版本影响,某些版本并不支持;
(2)long startTime: 闹钟的第一次执行时间,以毫秒为单位,可以自定义时间,不过一般使用当前时间。需要注意的是,本属性与第一个属性(type)密切相关,如果第一个参数对 应的闹钟使用的是相对时间(ELAPSED_REALTIME和ELAPSED_REALTIME_WAKEUP),那么本属性就得使用相对时间(相对于 系统启动时间来说),比如当前时间就表示为:SystemClock.elapsedRealtime();如果第一个参数对应的闹钟使用的是绝对时间 (RTC、RTC_WAKEUP、POWER_OFF_WAKEUP),那么本属性就得使用绝对时间,比如当前时间就表示 为:System.currentTimeMillis()。
(3)long intervalTime:对于后两个方法来说,存在本属性,表示两次闹钟执行的间隔时间,也是以毫秒为单位。
(4)PendingIntent pi: 绑定了闹钟的执行动作,比如发送一个广播、给出提示等等。PendingIntent是Intent的封装类。需要注意的是,如果是通过启动服务来实现闹钟提 示的话,PendingIntent对象的获取就应该采用Pending.getService(Context c,int i,Intent intent,int j)方法;如果是通过广播来实现闹钟提示的话,PendingIntent对象的获取就应该采用 PendingIntent.getBroadcast(Context c,int i,Intent intent,int j)方法;如果是采用Activity的方式来实现闹钟提示的话,PendingIntent对象的获取就应该采用 PendingIntent.getActivity(Context c,int i,Intent intent,int j)方法。如果这三种方法错用了的话,虽然不会报错,但是看不到闹钟提示效果。
activity_main.xml设置时钟按钮,代码如下:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.administrator.alarmclock.MainActivity">
<Button android:id="@+id/btn"
android:text="设置闹钟"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
</RelativeLayout>
清单文件AndroidManifest.xml注册广播接收者,代码如下:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.administrator.alarmclock">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".AlarmActivity" />
<!-- android:process=":remote": 新开一个进程 -->
<receiver android:name=".AlarmReceiver" android:process=":remote"/>
</application>
</manifest>
设置时间的MainActivity,代码如下:
public class MainActivity extends Activity{
private Button btn = null;
private AlarmManager alarmManager = null;
Calendar cal= Calendar.getInstance();
final int DIALOG_TIME = 0; //设置对话框id
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
btn = (Button)findViewById(R.id.btn);
btn.setOnClickListener(new View.OnClickListener(){
public void onClick(View view) {
showDialog(DIALOG_TIME);//显示时间选择对话框
}
});
}
@Override
protected Dialog onCreateDialog(int id) {
Dialog dialog;
dialog = new TimePickerDialog(
this,
new TimePickerDialog.OnTimeSetListener(){
public void onTimeSet(TimePicker timePicker, int hourOfDay,int minute) {
Calendar c=Calendar.getInstance();//获取日期对象
c.setTimeInMillis(System.currentTimeMillis()); //设置Calendar对象
c.set(Calendar.HOUR, hourOfDay); //设置闹钟小时数
c.set(Calendar.MINUTE, minute); //设置闹钟的分钟数
c.set(Calendar.SECOND, 0); //设置闹钟的秒数
c.set(Calendar.MILLISECOND, 0); //设置闹钟的毫秒数
Intent intent = new Intent(MainActivity.this, AlarmReceiver.class); //创建Intent对象
PendingIntent pi = PendingIntent.getBroadcast(MainActivity.this, 0, intent, 0); //创建PendingIntent
//alarmManager.set(AlarmManager.RTC_WAKEUP, c.getTimeInMillis(), pi); //设置闹钟
alarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), pi); //设置闹钟,当前时间就唤醒
Toast.makeText(MainActivity.this, "闹钟设置成功", Toast.LENGTH_LONG).show();//提示用户
}
},
cal.get(Calendar.HOUR_OF_DAY),
cal.get(Calendar.MINUTE),
false);
return dialog;
}
}
广播接收者AlarmReceive.java,代码如下:
public class AlarmReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Intent i = new Intent(context, AlarmActivity.class);
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(i);
}
}
接受到广播做提醒操作AlarmActivity,代码如下:
public class AlarmActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//显示对话框
new AlertDialog.Builder(AlarmActivity.this).
setTitle("闹钟").//设置标题
setMessage("时间到了!").//设置内容
setPositiveButton("知道了", new OnClickListener(){//设置按钮
public void onClick(DialogInterface dialog, int which) {
AlarmActivity.this.finish();//关闭Activity
}
}).create().show();
}
}
效果如下图: