广播机制(BroadcastReceiver)

11.1. BroadcastReceiver简介

在实际应用中,我们常需要等,等待系统抑或其他应用发出一道指令,为自己的应用擦亮明灯指明方向。而这种等待,在很多的平台上,都会需要付出不小的代价。比如,在Symbian中,你要等待一个来电消息,显示归属地之类的,必须让自己的应用忍辱负重偷偷摸摸的开机启动,消隐图标隐藏任务项,潜伏在后台,监控着相关事件,等待转瞬即逝的出手机会。这是一件很发指的事情,不但白白耗费了系统资源,还留了个流氓软件的骂名,这真是卖力不讨好的正面典型。

在Android中,充分考虑了广泛的这类需求,于是就有了Broadcast Receiver这样的一个组件。每个Broadcast Receiver都可以接收一种或若干种Intent作为触发事件(有不知道Intent的么,后面会知道了...),当发生这样事件的时候,系统会负责唤醒或传递消息到该Broadcast Receiver,任其处置。在此之前和这以后,Broadcast Receiver是否在运行都变得不重要了,及其绿色环保。

这个实现机制,显然是基于一种注册方式的,Broadcast Receiver将其特征描述并注册在系统中,根据注册时机,可以分为两类,被我冠名为冷热插拔。所谓冷插拔,就是Broadcast Receiver的相关信息写在配置文件中(求配置文件详情?稍安,后续奉上...),系统会负责在相关事件发生的时候及时通知到该Broadcast Receiver,这种模式适合于这样的场景。某事件方式 -> 通知Broadcast -> 启动相关处理应用。比如,监听来电、邮件、短信之类的,都隶属于这种模式。而热插拔,顾名思义,插拔这样的事情,都是由应用自己来处理的,通常是在OnResume事件中通过registerReceiver进行注册,在OnPause等事件中反注册,通过这种方式使其能够在运行期间保持对相关事件的关注。比如,一款优秀的词典软件(比如,有道词典...),可能会有在运行期间关注网络状况变化的需求,使其可以在有廉价网络的时候优先使用网络查询词汇,在其他情况下,首先通过本地词库来查词,从而兼顾腰包和体验,一举两得一石二鸟一箭双雕(注,真实在有道词典中有这样的能力,但不是通过Broadcast Receiver实现的,仅以为例...)。而这样的监听,只需要在其工作状态下保持就好,不运行的时候,管你是天大的网路变化,与我何干。其模式可以归结为:启动应用 -> 监听事件 -> 发生时进行处理。

除了接受消息的一方有多种模式,发送者也有很重要的选择权。通常,发送这有两类,一个就是系统本身,我们称之为系统Broadcast消息,在reference/android/content/Intent.html的Standard Broadcast Actions,可以求到相关消息的详情。除了系统,自定义的应用可以放出Broadcast消息,通过的接口可以是Context.sendBroadcast,抑或是Context.sendOrderedBroadcast。前者发出的称为Normal broadcast,所有关注该消息的Receiver,都有机会获得并进行处理;后者放出的称作Ordered broadcasts,顾名思义,接受者需要按资排辈,排在后面的只能吃前面吃剩下的,前面的心情不好私吞了,后面的只能喝西北风了。

当Broadcast Receiver接收到相关的消息,它们通常做一些简单的处理,然后转化称为一条Notification,一次振铃,

一次震动,抑或是启动一个Activity进行进一步的交互和处理。所以,虽然Broadcast整个逻辑不复杂,却是足够有用和

好用,它统一了Android的事件广播模型,让很多平台都相形见绌了。更多Broadcast Receiver相关内容,参见:

/reference/android/content/BroadcastReceiver.html。

11.2. BroadcastReceiver、Service、Activity共同应用实例

范例一:通过Activity启动广播

BroadcastReceiver:

package com.makyan.demo;

import android.content.BroadcastReceiver;

import android.content.Context;

import android.content.Intent;

import android.widget.Toast;

public class MyBroadcast extends BroadcastReceiver {

public MyBroadcast(){

System.out.println("每次启动广播都会实例化一个广播对象");

}

public void onReceive(Context arg0, Intent arg1) {

Toast.makeText(arg0, "广播已经启动", Toast.LENGTH_LONG).show();

}

}

Activity:

package com.makyan.demo;

import android.app.Activity;

import android.content.Intent;

import android.os.Bundle;

import android.view.View;

import android.view.View.OnClickListener;

import android.widget.Button;

public class BroadcastActivity extends Activity {

private Button but;

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_broadcast);

but = (Button) super.findViewById(R.id.start);

but.setOnClickListener(new OnClickListenerImpl());

}

class OnClickListenerImpl implements OnClickListener{

public void onClick(View arg0) {

Intent it = new Intent(Intent.ACTION_EDIT); //去AndroidMainfest.xml中找到对应的action,映射到对应的name上

BroadcastActivity.this.sendBroadcast(it);

}

}

}

在AndroidMainfest.xml中注册广播:

<?xml version="1.0" encoding="utf-8"?>

<manifest xmlns:android="http://schemas.android.com/apk/res/android"

package="com.makyan.demo"

android:versionCode="1"

android:versionName="1.0" >

<uses-sdk

android:minSdkVersion="17"

android:targetSdkVersion="17" />

<application

android:allowBackup="true"

android:icon="@drawable/ic_launcher"

android:label="@string/app_name"

android:theme="@style/AppTheme" >

<activity

android:name="com.makyan.demo.BroadcastActivity"

android:label="@string/app_name" >

<intent-filter>

<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />

</intent-filter>

</activity>

<receiver

            android:name="com.makyan.demo.MyBroadcast"

            android:enabled="true">

            <intent-filter>

                <action android:name="android.intent.action.EDIT"/>

            </intent-filter>

        </receiver>

</application>

</manifest>

现在的广播跟Service一样,都是根据Activity来启动,但是要记住,广播可以通过系统的状态来启动。

也可以不在AndroidMainfest.xml中配置,直接在Activity中手动的为广播注册,如下Activity:

package com.makyan.demo;

import android.app.Activity;

import android.content.Intent;

import android.content.IntentFilter;

import android.os.Bundle;

import android.view.View;

import android.view.View.OnClickListener;

import android.widget.Button;

public class BroadcastActivity extends Activity {

private Button but;

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_broadcast);

but = (Button) super.findViewById(R.id.start);

but.setOnClickListener(new OnClickListenerImpl());

}

class OnClickListenerImpl implements OnClickListener{

public void onClick(View arg0) {

/**

* 通过在AndroidMainfest.xml中注册实现

*/

//                                Intent it = new Intent(Intent.ACTION_EDIT); //去AndroidMainfest.xml中找到对应的action,映射到对应的name上

//                                BroadcastActivity.this.sendBroadcast(it);

/**

* 直接在Activity中注册实现

*/

Intent it = new Intent(Intent.ACTION_EDIT);

                                   it.putExtra("msg","http://www.baidu.com");

                                   IntentFilter filter = new IntentFilter(Intent.ACTION_EDIT);

                                   MyBroadcast broadcast = new MyBroadcast();

                                   BroadcastActivity.this.registerReceiver(broadcast, filter);

                                   BroadcastActivity.this.sendBroadcast(it);

}

}

}

范例:通过BroadcastReceiver启动Service

在BroadcastReceiver中,通过Context中的startService()方法来启动Service

如下BroadcastReceiver:

package com.makyan.demo;

import android.content.BroadcastReceiver;

import android.content.Context;

import android.content.Intent;

public class MyBroadcastReceiver extends BroadcastReceiver {

            public void onReceive(Context arg0, Intent arg1) {

                       arg0.startService(new Intent(arg0,MyService.class));

            }

}

在Activity中:

package com.makyan.demo;

import android.app.Activity;

import android.content.Intent;

import android.content.IntentFilter;

import android.os.Bundle;

public class BroadActivity extends Activity {

MyBroadcastReceiver receiver;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_broad);

Intent it = new Intent(Intent.ACTION_EDIT);

it.putExtra("msg","http://www.baicu.com");

IntentFilter filter = new IntentFilter(Intent.ACTION_EDIT);

receiver = new MyBroadcastReceiver();

this.registerReceiver(receiver, filter); //注册广播

this.sendBroadcast(it);

}

protected void onStop() {

super.unregisterReceiver(receiver);

super.onStop();

}

}

Service:

package com.makyan.demo;

import android.app.Service;

import android.content.Intent;

import android.os.IBinder;

public class MyService extends Service {

public void onCreate() {

System.out.println("*** Service onCreate");

super.onCreate();

}

public void onDestroy() {

System.out.println("*** Service onDestroy");

super.onDestroy();

}

public int onStartCommand(Intent intent, int flags, int startId) {

System.out.println("*** Service onStartCommand");

return super.onStartCommand(intent, flags, startId);

}

public IBinder onBind(Intent arg0) {

return null;

}

}

注意,每个Service都需在AndroidMainfest.xml中进行注册:

<service android:name="com.makyan.demo.MyService"/>

范例二:闹钟

组件配置

<LinearLayout 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"

android:orientation="vertical"

android:paddingBottom="@dimen/activity_vertical_margin"

android:paddingLeft="@dimen/activity_horizontal_margin"

android:paddingRight="@dimen/activity_horizontal_margin"

android:paddingTop="@dimen/activity_vertical_margin"

tools:context=".AlarmClickActivity" >

<TimePicker

android:id="@+id/time"

android:layout_width="wrap_content"

android:layout_height="wrap_content"/>

<Button

android:id="@+id/setBut"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="设置闹钟"

/>

<Button

android:id="@+id/deleteBut"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="删除闹钟"

/>

<TextView

android:id="@+id/msg"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="时间已到,闹钟响起" />

</LinearLayout>

与Broadcast交互的Activity

package com.makyan.demo;

import java.util.Calendar;

import android.app.Activity;

import android.app.AlarmManager;

import android.app.PendingIntent;

import android.content.Intent;

import android.os.Bundle;

import android.view.View;

import android.view.View.OnClickListener;

import android.widget.Button;

import android.widget.TextView;

import android.widget.TimePicker;

import android.widget.TimePicker.OnTimeChangedListener;

import android.widget.Toast;

public class AlarmClickActivity extends Activity {

private AlarmManager alarm = null;//闹钟服务

private Button set = null;

private Button delete = null;

private TextView msg = null;

private TimePicker time = null;

private int hourOfDay;

private int minue;

Calendar calendar = Calendar.getInstance();

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_alarm_click);

alarm = (AlarmManager) super.getSystemService(ALARM_SERVICE);

set = (Button) super.findViewById(R.id.setBut);

delete = (Button) super.findViewById(R.id.deleteBut);

msg = (TextView) super.findViewById(R.id.msg);

time = (TimePicker) super.findViewById(R.id.time);

time.setIs24HourView(true);

set.setOnClickListener(new SetOnClickListener());

delete.setOnClickListener(new DeleteOnClickListener());

time.setOnTimeChangedListener(new OnTimeChangedListenerImpl());

}

class SetOnClickListener implements OnClickListener{

public void onClick(View arg0) {

Intent intent = new Intent(AlarmClickActivity.this,MyBroadcastReceiver.class);

intent.setAction(Intent.ACTION_EDIT);

PendingIntent sender = PendingIntent.getBroadcast(AlarmClickActivity.this, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);

alarm.set(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), sender);

msg.setText("闹钟响起的时间是:" + hourOfDay + "时" + minue + "分");

Toast.makeText(AlarmClickActivity.this, "闹钟设置成功", Toast.LENGTH_LONG).show();

}

}

class DeleteOnClickListener implements OnClickListener{

public void onClick(View arg0) {

if(alarm != null){

Intent intent = new Intent(AlarmClickActivity.this,MyBroadcastReceiver.class);

intent.setAction(Intent.ACTION_EDIT);

PendingIntent sender = PendingIntent.getBroadcast(AlarmClickActivity.this, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);

alarm.cancel(sender);

msg.setText("当前没有设置闹钟");

Toast.makeText(AlarmClickActivity.this, "闹钟删除成功", Toast.LENGTH_LONG);

}

}

}

class OnTimeChangedListenerImpl implements OnTimeChangedListener{

public void onTimeChanged(TimePicker arg0, int hourOfDay, int minue) {

calendar.setTimeInMillis(System.currentTimeMillis());

calendar.set(Calendar.HOUR_OF_DAY, hourOfDay);

calendar.set(Calendar.MINUTE, minue);

calendar.set(Calendar.SECOND, 0);

calendar.set(Calendar.MILLISECOND, 0);

AlarmClickActivity.this.hourOfDay = hourOfDay;

AlarmClickActivity.this.minue = minue;

}

}

}

BroadcastReceiver

package com.makyan.demo;

import android.content.BroadcastReceiver;

import android.content.Context;

import android.content.Intent;

public class MyBroadcastReceiver extends BroadcastReceiver {

public void onReceive(Context context, Intent intent) {

Intent it = new Intent(context,AlarmMessage.class);

it.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

context.startService(it);

}

}

模拟闹钟响的Activity:

package com.makyan.demo;

import java.text.SimpleDateFormat;

import java.util.Date;

import android.app.Activity;

import android.app.AlertDialog;

import android.content.DialogInterface;

import android.os.Bundle;

public class AlarmMessage extends Activity {

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

new AlertDialog.Builder(this).setIcon(R.drawable.dg)

.setTitle("闹钟时间已到")

.setMessage("闹钟响起,现在时间是:" + new SimpleDateFormat("yyyyMMdd日 HH时mm分ss秒").format(new Date()))

.setPositiveButton("关闭",new DialogInterface.OnClickListener() {

public void onClick(DialogInterface dialog, int which) {

AlarmMessage.this.finish();

}

});

}

}

11.3. 使用BroadcastReceiver注意事项

11.3.1.  BrocadcastReceiver生命周期只有十秒左右

如果在 onReceive() 内做超过十秒内的事情,就会报ANR(Application No Response) 程序无响应的错误信息,如果需要完成一项比较耗时的工作 , 应该通过发送 Intent 给 Service, 由Service 来完成 . 这里不能使用子线程来解决 , 因为 BroadcastReceiver 的生命周期很短 , 子线程可能还没有结束BroadcastReceiver 就先结束了。

BroadcastReceiver 一旦结束 , 此时 BroadcastReceiver 的所在进程很容易在系统需要内存时被优先杀死 , 因为它属于空进程 ( 没有任何活动组件的进程 ). 如果它的宿主进程被杀死 , 那么正在工作的子线程也会被杀死 . 所以采用子线程来解决是不可靠的。

11.3.2. 静态注册BrocadcastReceiver和动态注册BrocadcastReceiver的区别

静态注册:指在功能文件Androidmanifest.xml中注册

动态注册:指在Activity程序中使用IntentFilter辅助进行注册。

动态注册广播接收器有一个特点,就是当用来注册的Activity关掉后,广播也就失效了。静态注册无需担忧广播接收器是否被关闭,只要设备是开启状态,广播接收器也是打开着的。也就是说哪怕app本身未启动,该app订阅的广播在触发时也会对它起作用。

系统常见广播Intent,如开机启动、电池电量变化、时间改变等广播

11.4. 最详细的介绍--Android 中的BroadCastReceiver

11.4.1. BroadCastReceiver 简介 (末尾有源码)

BroadCastReceiver 源码位于:

framework/base/core/java/android.content.BroadcastReceiver.java

广播接收者( BroadcastReceiver )用于接收广播 Intent ,广播 Intent 的发送是通过调用 Context.sendBroadcast() 、 Context.sendOrderedBroadcast() 来实现的。通常一个广播 Intent 可以被订阅了此 Intent 的多个广播接收者所接收。

广播是一种广泛运用的在应用程序之间传输信息的机制 。而 BroadcastReceiver 是对发送出来的广播进行过滤接收并响应的一类组件; 来自普通应用程序,如一个应用程序通知其他应用程序某些数据已经下载完毕。

BroadcastReceiver 自身并不实现图形用户界面,但是当它收到某个通知后, BroadcastReceiver 可以启动 Activity 作为响应,或者通过 NotificationMananger 提醒用户,或者启动 Service 等等。

11.4.2. BroadCastReceiver 的机制

11.4.2.1. 机制

在 Android 里面有各种各样的广播,比如电池的使用状态,电话的接收和短信的接收都会产生一个广播,应用程序开发者也可以监听这些广播并做出程序逻辑的处理。如图:

11.4.2.2.实现

用接收短信举例:

第一种方式继承广播接收BroadcastReceiver

public class MyBroadcastReceiver extends BroadcastReceiver {

// action 名称

String SMS_RECEIVED = "android.provider.Telephony.SMS_RECEIVED" ;

public void onReceive(Context context, Intent intent) {

if (intent.getAction().equals( SMS_RECEIVED )) {

// 相关处理 : 地域变换、电量不足、来电来信;

}

}

}

系统注册:在 AndroidManifest.xml 中注册

< receiver android:name = ".MyBroadcastReceiver" >

< intent-filter android:priority = "1000" >

<action android:name = " android.provider.Telephony.SMS_RECEIVED" />

</ intent-filter >

</ receiver >

权限配置

< uses-permission android:name = "android.permission.RECEIVE_SMS" />

< uses-permission android:name = "android.permission.SEND_SMS" />

第二种方式: 广播的接收

// 广播接收者 - 广播的接收

private BroadcastReceiver myBroadcastReceiver = new BroadcastReceiver() {

@Override

public void onReceive(Context context, Intent intent) {

// 相关处理,如收短信,监听电量变化信息

}

};

代码中注册:

IntentFilter intentFilter = new IntentFilter( "android.provider.Telephony.SMS_RECEIVED " );

registerReceiver( mBatteryInfoReceiver , intentFilter);

11.4.2.3.生命周期

描述了 Android 中广播的生命周期,其次它并不像 Activity 一样复杂,运行原理很简单如下图:

生命周期只有十秒左右,如果在 onReceive() 内做超过十秒内的事情,就会报错

每次广播到来时 , 会重新创建 BroadcastReceiver 对象 , 并且调用 onReceive() 方法 , 执行完以后 , 该对象即被销毁 . 当 onReceive() 方法在 10 秒内没有执行完毕, Android 会认为该程序无响应 . 所以在 BroadcastReceiver 里不能做一些比较耗时的操作 , 否侧会弹出 ANR(Application No Response) 的对话框 . 。(如图):

怎么用好 BroadcastReceiver

如果需要完成一项比较耗时的工作 , 应该通过发送 Intent 给 Service, 由 Service 来完成 . 这里不能使用子线程来解决 , 因为 BroadcastReceiver 的生命周期很短 , 子线程可能还没有结束 BroadcastReceiver 就先结束了 .BroadcastReceiver 一旦结束 , 此时 BroadcastReceiver 的 所在进程很容易在系统需要内存时被优先杀死 , 因为它属于空进程 ( 没有任何活动组件的进程 ). 如果它的宿主进程被杀死 , 那么正在工作的子线程也会被杀死 . 所以采用子线程来解决是不可靠的 .

11.4.3.广播类型及广播的收发

11.4.3.1.广播类型

普通广播 (Normal broadcasts)

发送一个广播,所以监听该广播的广播接收者都可以监听到改广播。

异步广播

当处理完之后的Intent ,依然存在,这时候registerReceiver(BroadcastReceiver, IntentFilter) 还能收到他的值,直到你把它去掉 , 不能将处理结果传给下一个接收者 , 无法终止广播 .

有序广播 (Ordered broadcasts)

按照接收者的优先级顺序接收广播 , 优先级别在 intent-filter 中的 priority 中声明 ,-1000 到 1000 之间 , 值越大 , 优先级越高 . 可以终止广播意图的继续传播 . 接收者可以篡改内容 .

11.4.3.2.广播的收发

该组件接收被广播的 intent,Context 可以通过 sendBroadcast() 和 sendOrderedBroadcast() 方法实现广播的发送 .

首先在需要发送信息的地方 ,把要发送的信息和用于过滤的信息 ( 如 Action 、 Category) 装入一个 Intent 对象 ,然后通过调用 Context.sendBroadcast() 、 sendOrderBroadcast() 或 sendStickyBroadcast() 方法,把 Intent 对象以广播方式发送出去。

使用 sendBroadcast() 或 sendStickyBroadcast() 方法发出去的 Intent ,所有满足条件的 BroadcastReceiver 都会随机地执行其 onReceive() 方法

11.4.3.3.普通广播的发送和接收:

sendBroadcast(intent);

Intent intent = new Intent( "cn.lenovo.yangguangf " );

sendBroadcast(intent);

priority :这个是 AndroidManifest.xml intent-filter 的参数。

< receiver android:name = ".MyBroadcastReceiver" >

< intent-filter android:priority = "1000" >

< action android:name = "cn.lenovo.yangguangfu" />

</ intent-filter >

</ receiver >

sendOrderedBroadcast(intent, receiverPermission);

1.他决定该广播的级别,级别数值是在 -1000 到 1000 之间 , 值越大 , 优先级越高;

2.同级别接收是先后是随机的;级别低的收到广播;

3.在 android 系统中只要监听该广播的接收者,都能够收到 sendBroadcast(intent) 发出的广播 ;

4.不能截断广播的继续传播,

5.实验现象,在这个方法发来的广播中,代码注册方式中,收到的广播的先后和注明优先级最高的他们的先后是随机。如果都没有优先级,代码注册收到为最先。

 11.4.3.4.有序广播的发送和接收:

sendOrderedBroadcast(intent, receiverPermission);

sendOrderedBroadcast(intent, receiverPermission, resultReceiver, scheduler, initialCode, initialData, initialExtras)

意图,广播,所有匹配的这一意图将接收机接收广播。

receiverPermission 这是权限,一个接收器必须持以接收您的广播。如果为 null ,不经许可的要求。

resultReceiver 您自己 BroadcastReceiver 来当作最后的广播接收器。

调度自定义处理程序,用以安排 resultReceiver 回调 ; 如果为 null 将语境中的主线程举行。

initialCode 一种结果代码的初始值。通常为 Activity.RESULT_OK 。这个值是 -1 ;为其他 int 型 也可以,如 0,1,2 ;

initialData 一种结果数据的初始值。通常情况下为空 , 是 String 类型 ;

initialExtras 一种结果额外的初始值。通常情况下为空 , 是 Bundle;

intent The Intent to broadcast; all receivers matching this Intent will receive the broadcast.

receiverPermission String naming a permissions that a receiver must hold in order to receive your broadcast. If null, no permission is required.

resultReceiver Your own BroadcastReceiver to treat as the final receiver of the broadcast.

scheduler A custom Handler with which to schedule the resultReceiver callback; if null it will be scheduled in the Context's main thread.

initialCode An initial value for the result code. Often Activity.RESULT_OK.

initialData An initial value for the result data. Often null.

initialExtras An initial value for the result extras. Often null.

1.该广播的级别有级别之分,级别数值是在 -1000 到 1000 之间 , 值越大 , 优先级越高;

2.同级别接收是先后是随机的,再到级别低的收到广播;

3.同级别接收是先后是随机的,如果先接收到的把广播截断了,同级别的例外的接收者是无法收到该广播的。( abortBroadcast() )

4.能截断广播的继续传播,高级别的广播收到该广播后,可以决定把该钟广播是否截断掉。

5.实验现象,在这个方法发来的广播中,代码注册方式中,收到广播先后次序为:注明优先级的、代码注册的、没有优先级的;如果都没有优先级,代码注册收到为最先。

11.4.3.5.异步广播的发送和接收:

sendStickyBroadcast(intent);

当处理完之后的Intent ,依然存在,直到你把它去掉。

发这个广播需要权限

<uses-permission android:name="android.permission.BROADCAST_STICKY" />

去掉是用这个方法removeStickyBroadcast(intent); 但别忘了在执行这个方法的应用里面 AndroidManifest.xml 同样要加上面的权限;

sendStickyOrderedBroadcast(intent, resultReceiver, scheduler,

initialCode, initialData, initialExtras)

这个方法具有有序广播的特性也有异步广播的特性;

发送这个广播要:

<uses-permission android:name="android.permission.BROADCAST_STICKY" />

这个权限。才能使用这个方法。如果您并不拥有该权限,将抛出 SecurityException 的。

实验现象( sendStickyOrderedBroadcast ()中),在这个方法发来的广播中,代码注册方式中,收到广播先后次序为:注明优先级的、代码注册的、没有优先级的;如果都没有优先级,代码注册收到为最先。

11.4.4.广播注册与注销

11.4.4.1.代码中注册广播:

注册广播方法一: registerReceiver(BroadcastReceiver receiver, IntentFilter filter) ,第一个参数是我们要处理广播的 BroadcastReceiver (广播接收者,可以是系统的,也可以是自定义的);第二个参数是意图过滤器。

注册广播方法二: registerReceiver(receiver, filter, broadcastPermission, scheduler) ,第一个参数是 BroadcastReceiver (广播接收者,可以是系统的,也可以是自定义的);第二个参数是意图过滤器;第三个参数是广播权限;第四个参数是 Hander ;

注意:权限重复现象,如果功能清单文件里注册了权限,在该方法再注册,则 receiver 无法收到广播,如果 功能清单文件里没有注册了权限,该方法注册也无法收到。当该方法没有注册权限,功能清单里注册的时候, receiver 能收到广播。

总结:在 Activity 中代码注册广播建议在: onResume() 中注册;

思维拓展:

1.如果在代码调用 registerReceiver(BroadcastReceiver receiver, IntentFilter filter) 十次( receiver , filter 的参数是同一参数),那么是否当该广播发送来的时候会收到十次呢?

2.注销是否也要注销十次才能把广播全部注销呢?

11.4.4.2.系统中注册广播:(在 AndroidManifest.xml 中 )

< receiver android:name = ".MyBroadcastReceiver" >

< intent-filter android:priority = "900" >

< action android:name = "cn.lenovo.yangguangfu" />

</ intent-filter >

</ receiver >

有时候还要根据发送广播是否指定权限,来决定是否要权限;

  11.4.4.3.广播注销

// 代码中注销广播

/unregisterReceiver(mBatteryInfoReceiver);

在 Activity 中代码注销广播建议在: onPuase() 中注销;

不要这这里面注销 Activity.onSaveInstanceState(), 因为这个方法是保存 Intent 状态的。

11.4.5.BroadCastReceiver 的 API

11.4.5.1.abortBroadcast ():

这个方法可以截获由 sendOrderedBroadcast () 发送来的 广播,让其它广播接收者无法收到这个广播。

11.4.5.2.clearAbortBroadcast ()

这个方法是针对上面的 abortBroadcast() 方法的,用于取消截获广播。这样它的下一级广播接收者就能够收到该广播了。

11.4.5.3.getAbortBroadcast ()

这个方法作用是:判断是否调用了 abortBroadcast (),如果先调用 abortBroadcast (),接着再调用 getAbortBroadcast (),将返回 true; 如果在调用 abortBroadcast() 、 clearAbortBroadcast ()

getAbortBroadcast (),将返回 false;

11.4.5.4.public final boolean getDebugUnregister ()

Since: API Level 1

Return the last value given to setDebugUnregister(boolean) .

11.4.5.5.getResultCode ()

如果用下面四个方法发送得广播,返回码为: -1 ;

// sendBroadcast(intent);

// sendBroadcast(intent, receiverPermission);

// sendOrderedBroadcast(intent, receiverPermission);

// sendStickyBroadcast(intent);

如果用下面两个方法发送得广播,返回码为:根据你设置 initialCode 的数字是多少就是多少;

// sendStickyOrderedBroadcast(intent, resultReceiver, scheduler,

// initialCode, initialData, initialExtras)

// sendOrderedBroadcast(intent, receiverPermission, resultReceiver,

// scheduler, initialCode, initialData, initialExtras)

11.4.5.6.getResultData ()

得到发送广播时设置的 initialData 的数据;

11.4.5.7.getResultExtras (boolean makeMap)

If true then a new empty Map will be made for you if the current Map is null; if false you should be prepared to receive a null Map.

得到由

sendStickyOrderedBroadcast(intent, resultReceiver, scheduler,

// initialCode, initialData, initialExtras) ;

// sendOrderedBroadcast(intent, receiverPermission, resultReceiver,

// scheduler, initialCode, initialData, initialExtras)

中 initialExtras 传入的参数。

实验:我用上面两个方法发了 initialExtras (这个一个 Bundle )传入的参数时,只要不为空,那么 makeMap 是否为 true 和 false 都能够得到数据。

11.4.5.8.isInitialStickyBroadcast ()

Returns true if the receiver is currently processing the initial value of a sticky broadcast -- that is, the value that was last broadcast and is currently held in the sticky cache, so this is not directly the result of a broadcast right now.

如果广播接收者是目前处理的一个宿主的广播的初始值,将返回 true , - 也就是说,这个值是最后的广播出的值,目前正在举行的宿主缓存,所以这并不是直接导致了现在的广播。

实验:在第三个应用中调用这个方法,无论你用哪种方式发送广播,这个方法得到的总是 false ;在发送广播 的 resultReceiver 广播接收者里面调用,得到的也是 false ;

11.4.5.9.isOrderedBroadcast ()

sendStickyOrderedBroadcast(intent, resultReceiver, scheduler,

initialCode, initialData, initialExtras)

上面这个方法发送时,得到的是 true;

判断是否是有序广播;

11.4.5.10.onReceive (Context context, Intent intent)

 

11.4.5.11.public IBinder peekService (Context myContext, Intent service)

Provide a binder to an already-running service. This method is synchronous and will not start the target service if it is not present, so it is safe to call from onReceive.

Parameters:

myContext The Context that had been passed to onReceive(Context, Intent)

service The Intent indicating the service you wish to use. See Context.startService(Intent) for more information.

11.4.5.12.setDebugUnregister (boolean debug)

Control inclusion of debugging help for mismatched calls to {@ Context#registerReceiver(BroadcastReceiver, IntentFilter) Context.registerReceiver()}. If called with true, before given to registerReceiver(), then the callstack of the following Context.unregisterReceiver() call is retained, to be printed if a later incorrect unregister call is made. Note that doing this requires retaining information about the BroadcastReceiver for

11.5.系统定义好的广播

Intent.ACTION_AIRPLANE_MODE_CHANGED;

//关闭或打开飞行模式时的广播

Intent.ACTION_BATTERY_CHANGED;

//充电状态,或者电池的电量发生变化

//电池的充电状态、电荷级别改变,不能通过组建声明接收这个广播,只有通过Context.registerReceiver()注册

Intent.ACTION_BATTERY_LOW;

//表示电池电量低

Intent.ACTION_BATTERY_OKAY;

//表示电池电量充足,即从电池电量低变化到饱满时会发出广播

Intent.ACTION_BOOT_COMPLETED;

//在系统启动完成后,这个动作被广播一次(只有一次)。

Intent.ACTION_CAMERA_BUTTON;

//按下照相时的拍照按键(硬件按键)时发出的广播

Intent.ACTION_CLOSE_SYSTEM_DIALOGS;

//当屏幕超时进行锁屏时,当用户按下电源按钮,长按或短按(不管有没跳出话框),进行锁屏时,android系统都会广播此Action消息

Intent.ACTION_CONFIGURATION_CHANGED;

//设备当前设置被改变时发出的广播(包括的改变:界面语言,设备方向,等,请参考Configuration.java)

Intent.ACTION_DATE_CHANGED;

//设备日期发生改变时会发出此广播

Intent.ACTION_DEVICE_STORAGE_LOW;

//设备内存不足时发出的广播,此广播只能由系统使用,其它APP不可用?

Intent.ACTION_DEVICE_STORAGE_OK;

//设备内存从不足到充足时发出的广播,此广播只能由系统使用,其它APP不可用?

Intent.ACTION_DOCK_EVENT;

//发出此广播的地方frameworks\base\services\java\com\android\server\DockObserver.java

Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE;

//移动APP完成之后,发出的广播(移动是指:APP2SD)

Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE;

//正在移动APP时,发出的广播(移动是指:APP2SD)

Intent.ACTION_GTALK_SERVICE_CONNECTED;

//Gtalk已建立连接时发出的广播

Intent.ACTION_GTALK_SERVICE_DISCONNECTED;

//Gtalk已断开连接时发出的广播

Intent.ACTION_HEADSET_PLUG;

//在耳机口上插入耳机时发出的广播

Intent.ACTION_INPUT_METHOD_CHANGED;

//改变输入法时发出的广播

Intent.ACTION_LOCALE_CHANGED;

//设备当前区域设置已更改时发出的广播

Intent.ACTION_MANAGE_PACKAGE_STORAGE;

Intent.ACTION_MEDIA_BAD_REMOVAL;

//未正确移除SD卡(正确移除SD卡的方法:设置--SD卡和设备内存--卸载SD卡),但已把SD卡取出来时发出的广播

//广播:扩展介质(扩展卡)已经从 SD 卡插槽拔出,但是挂载点 (mount point) 还没解除 (unmount)

Intent.ACTION_MEDIA_BUTTON;

//按下"Media Button" 按键时发出的广播,假如有"Media Button" 按键的话(硬件按键)

Intent.ACTION_MEDIA_CHECKING;

//插入外部储存装置,比如SD卡时,系统会检验SD卡,此时发出的广播?

Intent.ACTION_MEDIA_EJECT;

//已拔掉外部大容量储存设备发出的广播(比如SD卡,或移动硬盘),不管有没有正确卸载都会发出此广播?

//广播:用户想要移除扩展介质(拔掉扩展卡)。

Intent.ACTION_MEDIA_MOUNTED;

//插入SD卡并且已正确安装(识别)时发出的广播

//广播:扩展介质被插入,而且已经被挂载。

Intent.ACTION_MEDIA_NOFS;

Intent.ACTION_MEDIA_REMOVED;

//外部储存设备已被移除,不管有没正确卸载,都会发出此广播?

// 广播:扩展介质被移除。

Intent.ACTION_MEDIA_SCANNER_FINISHED;

//广播:已经扫描完介质的一个目录

Intent.ACTION_MEDIA_SCANNER_SCAN_FILE;

Intent.ACTION_MEDIA_SCANNER_STARTED;

//广播:开始扫描介质的一个目录

Intent.ACTION_MEDIA_SHARED;

// 广播:扩展介质的挂载被解除 (unmount),因为它已经作为 USB 大容量存储被共享。

Intent.ACTION_MEDIA_UNMOUNTABLE;

Intent.ACTION_MEDIA_UNMOUNTED

// 广播:扩展介质存在,但是还没有被挂载 (mount)。

Intent.ACTION_NEW_OUTGOING_CALL;

Intent.ACTION_PACKAGE_ADDED;

//成功的安装APK之后

//广播:设备上新安装了一个应用程序包。

//一个新应用包已经安装在设备上,数据包括包名(最新安装的包程序不能接收到这个广播)

Intent.ACTION_PACKAGE_CHANGED;

//一个已存在的应用程序包已经改变,包括包名

Intent.ACTION_PACKAGE_DATA_CLEARED;

//清除一个应用程序的数据时发出的广播(在设置--应用管理--选中某个应用,之后点清除数据时?)

//用户已经清除一个包的数据,包括包名(清除包程序不能接收到这个广播)

Intent.ACTION_PACKAGE_INSTALL;

//触发一个下载并且完成安装时发出的广播,比如在电子市场里下载应用?

Intent.ACTION_PACKAGE_REMOVED;

//成功的删除某个APK之后发出的广播

//一个已存在的应用程序包已经从设备上移除,包括包名(正在被安装的包程序不能接收到这个广播)

Intent.ACTION_PACKAGE_REPLACED;

//替换一个现有的安装包时发出的广播(不管现在安装的APP比之前的新还是旧,都会发出此广播?)

Intent.ACTION_PACKAGE_RESTARTED;

//用户重新开始一个包,包的所有进程将被杀死,所有与其联系的运行时间状态应该被移除,包括包名(重新开始包程序不能接收到这个广播)

Intent.ACTION_POWER_CONNECTED;

//插上外部电源时发出的广播

Intent.ACTION_POWER_DISCONNECTED;

//已断开外部电源连接时发出的广播

Intent.ACTION_PROVIDER_CHANGED;

Intent.ACTION_REBOOT;

//重启设备时的广播

Intent.ACTION_SCREEN_OFF;

//屏幕被关闭之后的广播

Intent.ACTION_SCREEN_ON;

//屏幕被打开之后的广播

Intent.ACTION_SHUTDOWN;

//关闭系统时发出的广播

Intent.ACTION_TIMEZONE_CHANGED;

//时区发生改变时发出的广播

Intent.ACTION_TIME_CHANGED;

//时间被设置时发出的广播

Intent.ACTION_TIME_TICK;

//广播:当前时间已经变化(正常的时间流逝)。

//当前时间改变,每分钟都发送,不能通过组件声明来接收,只有通过Context.registerReceiver()方法来注册

Intent.ACTION_UID_REMOVED;

//一个用户ID已经从系统中移除发出的广播

Intent.ACTION_UMS_CONNECTED;

//设备已进入USB大容量储存状态时发出的广播?

Intent.ACTION_UMS_DISCONNECTED;

//设备已从USB大容量储存状态转为正常状态时发出的广播?

Intent.ACTION_USER_PRESENT;

Intent.ACTION_WALLPAPER_CHANGED;

//设备墙纸已改变时发出的广播

Android详细教程(基础篇):二十八、Android 广播机制BroadcastReceiver相关推荐

  1. 关闭数字健康 android 魅族,数字体验 篇二十八:精雕细刻,只为给魅友更好的选择,魅族16s Pro体验分享...

    数字体验 篇二十八:精雕细刻,只为给魅友更好的选择,魅族16s Pro体验分享 2019-09-06 17:31:22 14点赞 10收藏 15评论 当我还一直在称赞魅族16s所拥有的舒适手感表现时, ...

  2. WF4.0 基础篇 (二十八) WF调用PowerShell

    PowerShell 提供了命令行管理系统的功能,但通常情况下,操作人员要完成某种业务需要多组PowerShell 命令,这样会很烦琐,错误率也比效高.操作人员可以编写powerShell脚本来组织命 ...

  3. Vue实战篇二十八:实现一个手机版的购物车

    系列文章目录 Vue基础篇一:编写第一个Vue程序 Vue基础篇二:Vue组件的核心概念 Vue基础篇三:Vue的计算属性与侦听器 Vue基础篇四:Vue的生命周期(秒杀案例实战) Vue基础篇五:V ...

  4. android游戏性能测试,评测 篇二十九:用游戏数据来彰显实力,华为nova5i Pro游戏性能篇...

    评测 篇二十九:用游戏数据来彰显实力,华为nova5i Pro游戏性能篇 2020-01-02 00:20:00 4点赞 1收藏 2评论 笔者带着大家对华为nova5i Pro做了开箱测试,了解外观与 ...

  5. python进阶记录之基础篇二十六_Python进阶记录之基础篇(十六)

    回顾 在Python进阶记录之基础篇(十五)中,我们介绍了面向对象的基本概念以及Python中类和对象的基础知识,需要重点掌握类的创建和对象的使用.今天我们继续讲一下Python中面向对象的相关知识点 ...

  6. [网络安全自学篇] 二十八.文件上传入门及防御原理(一)

    这是作者的系列网络安全自学教程,主要是关于安全工具和实践操作的在线笔记,特分享出来与博友共勉,希望您们喜欢,一起进步.前文分享了Sqlmap的基本用法.CTF实战,包括设置HTTP.POST请求.参数 ...

  7. Android开发笔记(一百二十八)手机制式适配

    查看运营商与网络类型 虽然现在4G网络很普及了,但是我国幅员辽阔,4G信号在某些地方接收不良,手机连接很容易掉到3G甚至2G网络.为了让用户在低速环境也能使用App的基础功能,而不至于还在老牛破车地缓 ...

  8. MySQL基础(二十八)索引优化与查询优化

    都有哪些维度可以进行数据库调优?简言之: 索引失效.没有充分利用到索引--索引建立 关联查询太多JOIN (设计缺陷或不得已的需求)--SQL优化 服务器调优及各个参数设置(缓冲.线程数等)---调整 ...

  9. Android Multimedia框架总结(二十八)NuPlayer到OMX过程

    原址 NuPlayer是谷歌新研发的.  AwesomePlayer存在BUG,谷歌早已在android m 版本中弃用. sp<MediaPlayerBase> MediaPlayerS ...

  10. WF4.0 基础篇 (二十九) WorkflowInspectionServices

    本文例子下载: http://files.cnblogs.com/foundation/WorkflowInspectionServicesSample.rar WorkflowInspectionS ...

最新文章

  1. Relay外部库使用
  2. 微信小程序开发中如何实现侧边栏的滑动效果?
  3. 【剑指offer-Java版】49把字符串转换为整数
  4. 如何在PowerShell中使用带有空格和引号的参数运行EXE文件
  5. 解决IndexClosedException: closed
  6. java从Object类型转换成double类型
  7. 近世代数--整环与域--有限的整环是域
  8. 实验:PIO外部中断
  9. 利用HttpSessionListener实现网站在线人数统计功能
  10. c++gdal如何在大图像中截取小图像并获取其图像信息_【图像处理】OpenCV系列十 --- 边缘检测之Canny算子...
  11. vue密码强度提示条
  12. iOS 2x 3x
  13. python : sha256 、ripemd160
  14. 设计模式之模板模式(模板方法)
  15. kwgt公式代码大全_电脑文字识别ocr 数学公式 识别 mathtype
  16. 个人独资有限公司章程模板
  17. tampermonkey油猴实现自动定时刷新页面,刷访问量
  18. 打开微信键盘自动弹出_微信一打开就弹出键盘 微信打字键盘怎么恢复
  19. NetBeans IDE教程
  20. Autovue Client/Server 性能优化

热门文章

  1. 10年软件测试成长、迷茫、奋斗故事,送给刚入行测试迷茫的你(文字较多)
  2. 【CMake】CMakeLists.txt的超傻瓜手把手教程(附实例源码)
  3. 【董天一】Filecoin挖矿进展
  4. 浅谈档案数字化建设的意义
  5. 群晖php pear,Synology 群晖DSM7.0 40850 beta 版本各机型固件下载链接
  6. 身边的人脸安全:员工用人脸作弊工具打卡,企业该如何防范?
  7. vue表单验证手机号非必填验证
  8. 华为自研服务器芯片再下一城,从中国移动招标中获取近两成份额
  9. python点击屏幕坐标_Appium+python自动化(二十二)- 三个臭皮匠顶个诸葛亮-控件坐标获取(超详解)...
  10. 如何使用3CX阿里云镜像部署3CX到阿里云平台