BroadcastReceiver

发送自定义广播分类

1.Normal Broadcast:普通广播
2.System Broadcast: 系统广播
3.Ordered broadcast:有序广播
4.Sticky Broadcast:粘性广播(在 android 5.0/api 21中deprecated,不再推荐使用,相应的还有粘性有序广播,同样已经deprecated)
5.Local Broadcast:App应用内广播

作者:kima
链接:http://www.jianshu.com/p/79134d8b3eba
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

无序广播

广播接收者只要注册接收相应的事件类型,就能接收到的广播。 无序广播数据不可以修改 广播不可以终止

创建一个无序发广播

//创建意图
Intent intent = new Intent();
//设置action--用于接收广播-配置
intent.setAction("com.itheima.xxxx");// ---随意定义
//使用意图携带点数据
intent.putExtra("name", "新闻联播每天晚上7点准时开播");
//sendBroadcast(intent)发送一条无序广播
sendBroadcast(intent);

配置

//配置receiver—在Intent设置的action
<receiver android:name="com.itheima.acceptbroadcast.AcceptCustomReceiver">
    <intent-filter>
        <action android:name="com.itheima.xxxx" />
    </intent-filter>
</receiver>

创建广播接收者

//创建一个工程接收广播
public class AcceptCustomReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        abortBroadcast();
        //获取发送自定义广播携带的数据
        String name = intent.getStringExtra("name");
    }
}

有序广播

当广播把消息发送出去后,消息会根据广播接收者的优先级从高到低一级一级地下发消息。---可以拦截消息,也可以修改消息。
特性
 有序广播使用sendOrderedBroadcast方法来发送
 接收者可以在<intent-filter>中定义android:priority定义优先级,数字越大优先级越高,优先级的取值范围是: 1000(最高) ~ -1000(最低)谷歌规定最大是1000,但是他的类型其实是int。
系统默认优先级是0,那么想在系统程序之前接收到广播,那么就设置大于0的数,相同优先级下,接收的顺序要看在清单文件中声明注册的顺序
 有序广播可以被拦截或添加数据,优先级高的接收者可以拦截优先级低的 ,使用abortBroadcast方法拦截, 添加数据:通过bundle传递
前面的接收者可以将数据通过setResultExtras(Bundle)方法存放进结果对象,
然后传给下一个接收者,下一个接收者通过代码:Bundle bundle = getResultExtras(true))可以获取上一个接收者存入在结果对象中的数据。

发送广播

Intent intent = new Intent();
intent.setAction("com.itheima.rice");

sendOrderedBroadcast(intent, null, new FinalReceiver(), null, 1,
        "xxx给每个ccc发了1000斤大米", null);

// sendOrderedBroadcast(intent, null, null, null, 1,
// "xxx给每个ccc发了1000斤大米", null);
// 参数1:intent,
// 参数2:接受的权限-不需要-null
// 参数3:广播接受者类型,可以视为最终的广播接受者,
// 参数4:handler
// 参数5:int- 初始码
// 参数6:初始化数据--"广播初始"(携带的数据)
// 参数7:额外数据-null

创建最终接受者—不用再清单文件配置—在发广播的参数3中new对象

public class FinalReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {

        // [1]获取发送广播携带的数据
        String rice = getResultData();
        // [2]给用户一个友好提示
        Toast.makeText(context, "钦差大臣:" + rice, 1).show();
    }
}

接收广播—想要几个优先级就建几个

public class ProvienceReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {

        //[1]getResultData()获取发送广播携带的数据
        String rice = getResultData();
        //[2]给用户一个友好提示
        Toast.makeText(context, "省:" + rice, 1).show();
        //[3] setResultData()修改数据
        setResultData("xxx给每个ccc发了1002斤大米");
        //abortBroadcast();终止广播接收者—但最终的接受者永远也能接收
        // abortBroadcast();
    }
}
public class CityReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        // [1]获取发送广播携带的数据
        String rice = getResultData();
        // [2]给用户一个友好提示
        Toast.makeText(context, "市:" + rice, 1).show();
        // [3]修改数据
        setResultData("xxx给每个ccc发了1002斤大米");
    }
}
public class CountryReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        // [1]获取发送广播携带的数据
        String rice = getResultData();
        // [2]给用户一个友好提示
        Toast.makeText(context, "乡:" + rice, 1).show();
        setResultData("xxx给每个ccc发了1002斤大米");
    }
}


public class NongMinReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        // [1]获取发送广播携带的数据
        String rice = getResultData();
        // [2]给用户一个友好提示
        Toast.makeText(context, "农民收到1002斤大米:" + rice, 1).show();
    }
}

配置receiver—谁先接收就先配置谁—和属性priority="1000"
在意图过滤器上配置优先级android:priority="1000"—取值范围是-1000到1000---只要是int类型值就行

<receiver android:name="com.kailing.acceptrice.ProvienceReceiver">
    <intent-filter android:priority="1000">
        <action android:name="com.kailing.rice" />
    </intent-filter>
</receiver>

<receiver android:name="com.kailing.acceptrice.CityReceiver">
    <intent-filter android:priority="500">
        <action android:name="com.kailing.rice" />
    </intent-filter>
</receiver>

<receiver android:name="com.kailing.acceptrice.CountryReceiver">
    <intent-filter android:priority="200">
        <action android:name="com.kailing.rice" />
    </intent-filter>
</receiver>

<receiver android:name="com.kailing.acceptrice.NongMinReceiver">
    <intent-filter android:priority="0">
        <action android:name="com.kailing.rice" />
    </intent-filter>
</receiver>

粘性广播

Sticky:粘性
sendStickyBroadcast(intent) 阴魂不散的广播 (粘性的广播)。粘性广播,会一直等待intent指定的事件处理完毕,才会消失。
广播接受者的生命周期都是比较短的,一般接受到广播之后10s左右就会结束,但是有一些广播事件是比较耗时的。比如WIFI状态改变。
Wifi设置:发送wifi状态改变的广播,系统就是通过sendStickyBroadcast来实现的,因为获取wifi状态改变是一个很耗时的操作(获取手机的SSID,并且会获取IP地址等等一系列操作),如果用一般发送广播方式,还没等wifi状态获取完,广播就结束了。

应用内广播

Android中的广播可以跨进程甚至跨App直接通信,且注册是exported对于有intent-filter的情况下默认值是true,由此将可能出现安全隐患如下:
1.其他App可能会针对性的发出与当前App intent-filter相匹配的广播,由此导致当前App不断接收到广播并处理;
2.其他App可以注册与当前App一致的intent-filter用于接收广播,获取广播具体信息
最常见的增加安全性的方案是:
1.对于同一App内部发送和接收广播,将exported属性人为设置成false,使得非本App内部发出的此广播不被接收;
2.在广播发送和接收时,都增加上相应的permission,用于权限验证;
3.发送广播时,指定特定广播接收器所在的包名,具体是通过intent.setPackage(packageName)指定在,这样此广播将只会发送到此包中的App内与之相匹配的有效广播接收器中。
App应用内广播可以理解成一种局部广播的形式,广播的发送者和接收者都同属于一个App。
更多的考虑到的是Android广播机制中的安全性问题。
相比于全局广播,App应用内广播优势体现在:
1.安全性更高;
2.更加高效。
Android v4兼容包中给出了封装好的LocalBroadcastManager类,用于统一处理App应用内的广播问题,使用方式上与通常的全局广播几乎相同,只是注册/取消注册广播接收器和发送广播时将主调context变成了LocalBroadcastManager的单一实例。

//registerReceiver(mBroadcastReceiver, intentFilter);
//注册应用内广播接收器
localBroadcastManager = LocalBroadcastManager.getInstance(this);
localBroadcastManager.registerReceiver(mBroadcastReceiver, intentFilter);
//unregisterReceiver(mBroadcastReceiver);
//取消注册应用内广播接收器
localBroadcastManager.unregisterReceiver(mBroadcastReceiver);
Intent intent = new Intent();
intent.setAction(BROADCAST_ACTION);
intent.putExtra("name", "qqyumidi");
//sendBroadcast(intent);
//发送应用内广播
localBroadcastManager.sendBroadcast(intent);

注册广播接受者的方式

静态注册
清单文件中声明<receiver>,需要在其中配置<intent-filter>指定接收广播的动作(action)
指sd卡未挂载。,

<receiver android:name="com.kailing.broadcastReciver.SDCardUnmountedReceiver">
            <intent-filter>
<action android:name="android.intent.action.MEDIA_UNMOUNTED"/>
          <data android:scheme="file"></data>
            </intent-filter>
</receiver>

继承broadcastreceiver—onreceive接收广播进行处理
动态注册
这里在清单文件里面注册无效

//动态注册广播接受者—在参1中指定处理广播的接受者
//[1]创建处理广播的screenreceiver广播接受者实例

screenReceiver = new ScreenReceiver();
//[2]创建意图过滤器实例
IntentFilter filter = new IntentFilter();
//设置action--- filter.addAction(可以配置多个)
filter.addAction("android.intent.action.SCREEN_OFF");
filter.addAction("android.intent.action.SCREEN_ON");
//registerReceiver动态注册广播接收者
//参数1:处理这个广播的接收者,参数2:匹配可以接收的广播--意图过滤器
registerReceiver(screenReceiver, filter);

当Activity销毁的时候 应该取消注册广播接收者

@Override
protected void onDestroy() {
    // unregisterReceiver取消注册广播接收者—注册什么就注销什么—在页面销毁之前执行
    unregisterReceiver(screenReceiver);
    //调用父类onDestroy();
    super.onDestroy();
}

定义广播接收者—处理广播

public class ScreenReceiver extends BroadcastReceiver {

    // 当手机锁屏的时候执行
    @Override
    public void onReceive(Context context, Intent intent) {

        // [1]获取当前广播的事件类型
        String action = intent.getAction();
        if ("android.intent.action.SCREEN_OFF".equals(action)) {
            System.out.println("屏幕锁屏");
        } else if ("android.intent.action.SCREEN_ON".equals(action)) {
            System.out.println("屏幕解锁");

        }
    }

}

两种注册广播接受者的区别
代码注册
它不是常驻型广播,也就是说广播跟随程序的生命周期,一旦代码所在进程被杀死,广播接收者就失效。
清单文件注册
是常驻型,也就是说当应用程序关闭后,如果有信息广播来,程序也会被系统调用自动运行。一旦应用程序被部署到手机,广播接收者就会生效,高版本的模拟器(3.2以上)中的接收者,需要启动过一次才能接收到广播。
3.2版本以上出于安全问题:从未启动过的广播接收程序,默认是接收不到广播的,必须有一个界面,然后通过界面启动一次这个程序。

生命周期

广播接收者的生命周期是非常短暂的,在接收到广播的时候创建onReceive()方法结束之后销毁
广播接收者中不要做一些耗时的工作,否则会弹出Application No Response错误对话框。
最好也不要在广播接收者中创建子线程做耗时的工作,因为广播接收者被销毁后进程就成为了空进程,很容易被系统杀掉
广播接收者也一样,在执行生命周期方法onReceive时,也是一个前台进程

广播接收者接收系统发的广播

Ip拨号

定义广播接收者

//定义广播接受者继承BroadcastReceiver
public class OutGoingCallReceiver extends BroadcastReceiver {
    //onReceive当接收到外拨电话的时候执行—清单文件中配置
    @Override
    public void onReceive(Context context, Intent intent) {
        //[0]把刚刚用户存的ip号码给取出来 通过sp取
        SharedPreferences sp = context.getSharedPreferences("config", 0);
        String ipnumber = sp.getString("ipnumber", "");
        //[1]getResultData()获取当前拨打的号码 获取发送广播携带的数据
        String currentNumber = getResultData();
        //[2]setResultData()在当前要拨打的号码前面加上17951
        if (currentNumber.startsWith("0")) {
            setResultData(ipnumber + currentNumber);
        }
    }

}

清单文件配置

// 在清单文件中配置(定义广播接收者后)
<!--配置广播接收者-->
<receiver android:name="com.kailing.ipdail.OutGoingCallReceiver"><!--相当于你给收音机安装了一块电池-->
    <intent-filter>
        <!--调频-->
        <action android:name="android.intent.action.NEW_OUTGOING_CALL" />
    </intent-filter>
</receiver>
//加权限PROCESS_OUTGOING_CALLS
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" />

SD卡状态监听器

定义广播接收者

public class SdcardReceiver extends BroadcastReceiver {

    //当挂载sd卡 或者卸载sd卡的时候执行
    @Override
    public void onReceive(Context context, Intent intent) {
        //[1]getAction() 获取一下当前广播的事件类型
        String action = intent.getAction();
        //[2]判断一下当前的类型
        if ("android.intent.action.MEDIA_MOUNTED".equals(action)) {
            System.out.println("挂载了~~~~");

        } else if ("android.intent.action.MEDIA_UNMOUNTED".equals(action)) {
            System.out.println("卸载了~~~++~~");
        }
    }

}

清单文件配置

//清单文件中配置
<receiver android:name="com.kailing.sdcardstatelistener.SdcardReceiver">
    <intent-filter>
        <action android:name="android.intent.action.MEDIA_MOUNTED" />
        <action android:name="android.intent.action.MEDIA_UNMOUNTED" />

        <data android:scheme="file"></data>
    </intent-filter>
</receiver>

开机启动广播

定义广播接收者

public class BootReceiver extends BroadcastReceiver {
    // 当手机重新启动的时候执行
    @Override
    public void onReceive(Context context, Intent intent) {
        //把mainActivity这个页面打开
        Intent intent2 = new Intent(context, MainActivity.class);
        //setFlags() 告诉系统添加一个任务栈的环境
        // 注意 当在广播接收者中开启Activity 是没有任务栈的 所以需要我们告诉系统添加一个任务栈的环境
        intent2.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        // 开启activity
        context.startActivity(intent2);
    }
}

清单文件配置

<receiver android:name="com.kailing.money.BootReceiver">
    <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED"/>
    </intent-filter>
</receiver>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>

安装和卸载应用

<receiver android:name="com.kailing.appstate.AppStateReceiver">
    <intent-filter>
        <action android:name="android.intent.action.PACKAGE_ADDED"/>
        <action android:name="android.intent.action.PACKAGE_REMOVED"/>
        <data android:scheme="package"/>
    </intent-filter>
</receiver>
public class AppStateReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        //getAction() 获取当前广播的事件类型
         String action = intent.getAction();
         if ("android.intent.action.PACKAGE_INSTALL".equals(action)) {
             System.out.println("这个没啥用~~~");
        } else if ("android.intent.action.PACKAGE_ADDED".equals(action)) {
            intent.getData(); //拿到应用的包名
             System.out.println("应用被安装了++++" + intent.getData());
        } else if ("android.intent.action.PACKAGE_REMOVED".equals(action)) {
           System.out.println("应用被卸载了~~~");
        }
    }
}

短信监听器—6.0去掉了

<receiver android:name="com.kailing.smslistener.SmsReceiver">
    <intent-filter>
        <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"/>
//定义广播接受者
public class SmsReceiver extends BroadcastReceiver {
    //当短信到来的时候执行
    @SuppressWarnings("deprecation")
    @Override
    public void onReceive(Context context, Intent intent) {
        // [1] intent.getExtras().get("pdus");获取短信发送的内容 和 是谁发送的---强转成Object[]
        Object[] object = (Object[]) intent.getExtras().get("pdus");
        //Pdu协议数据单元
        // [2]创建Smsmessage.createFromPdu((byte[]) object2)对象来获取短信的内容
        for (Object object2 : object) {
            SmsMessage smsMessage = SmsMessage.createFromPdu((byte[]) object2);
            //[3] getMessageBody()获取短信发送的内容
            String body = smsMessage.getMessageBody();
            String address = smsMessage.getOriginatingAddress();//谁发送的
            System.out.println("address:" + address + "~~~~~" + body);
        }
    }
}

没有界面的监听器
在清单文件中,将MainActivity的intent-filter配置删除掉,
安卓4.0版本之后为了安全考虑,要求应用程序必须要有界面,必须被用户运行过一次,广播接受者才会生效。
安卓4.0版本的强行停止相当于冻结一个应用,一旦应用程序被用户强行停止了,广播接受者就不会生效了,直到用户手动打开这个应用程序为止
6.0时短信监听完全屏蔽

http://www.jianshu.com/p/79134d8b3eba

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,254评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,875评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,682评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,896评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,015评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,152评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,208评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,962评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,388评论 1 304
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,700评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,867评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,551评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,186评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,901评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,142评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,689评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,757评论 2 351

推荐阅读更多精彩内容

  • 使用Android手机的时候,我们的手机管家中经常会出现开机自启动某某app,那么对于这个某某APP来说,他是怎么...
    徐爱卿阅读 5,943评论 8 14
  • 现实中的广播:电台为了传达一些消息而发送广播,通过广播携带要传达的消息,群众只要买一个收音机,就可以收到广播了。 ...
    stevewang阅读 4,234评论 0 8
  • HandlerThread是一个Android 已封装好的轻量级异步类。HandlerThread本质上是一个线程...
    kjy_112233阅读 1,273评论 0 9
  • 敏感的人易缺乏安全感和过分自卑:遇到点事常会设身处地的为他人着想,生怕自己的言行举止惹得他人不愉快;其实,往往不愉...
    吴虞公子阅读 204评论 0 0
  • Romit_lee阅读 119评论 0 0