Android四大组件之Broadcast简析

一,广播的分类

1,标准广播(Normal broadcasts)是一种完全异步执行的广播,在广播发出之后,所有的广播接收器几乎都会在同一时刻接收到这条广播消息,因此它们之间没有任何先后顺序可言。这种广播的效率会比较高,但同时也意味着它是无法被截断的。

2,有序广播(Ordered broadcasts)则是一种同步执行的广播,在广播发出之后,同一时刻只会有一个广播接收器能够收到这条广播消息,当这个广播接收器中的逻辑执行完毕后,广播才会继续传递。所以此时的广播接收器是有先后顺序的,优先级高的广播接收器就可以先收到广播消息,并且前面的广播接收器还可以截断正在传递的广播,这样后面的广播接收器就无法收到广播消息了。

二,广播的注册方式

注册广播的方式一般有两种,在代码中注册和在AndroidManifest.xml 中注册,其中前者也被称为动态注册,后者也被称为静态注册。

那么该如何创建一个广播接收器呢?其实只需要新建一个类,让它继承自BroadcastReceiver,并重写父类的onReceive()方法就行了。这样当有广播到来时,onReceive()方法就会得到执行,具体的逻辑就可以在这个方法中处理。

1,动态注册

registerReceiver(networkChangeReceiver, intentFilter);和unregisterReceiver(networkChangeReceiver);

public class MainActivity extends Activity {

private IntentFilter intentFilter;

private NetworkChangeReceiver networkChangeReceiver;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

//添加监听的广播类型

intentFilter = new IntentFilter();

intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");

//新建广播实体类,并动态注册该广播

networkChangeReceiver = new NetworkChangeReceiver();

registerReceiver(networkChangeReceiver, intentFilter);

}

@Override

protected void onDestroy() {

super.onDestroy();

//取消广播的注册

unregisterReceiver(networkChangeReceiver);

}

class NetworkChangeReceiver extends BroadcastReceiver {

@Override

public void onReceive(Context context, Intent intent) {

Toast.makeText(context, "network changes",Toast.LENGTH_SHORT).show();

}

    }

}

观察onCreate()方法,首先我们创建了一个IntentFilter 的实例,并给它添加了一个值为android.net.conn.CONNECTIVITY_CHANGE 的action,为什么要添加这个值呢?因为

当网络状态发生变化时, 系统发出的正是一条值为android.net.conn.CONNECTIVITY_CHANGE 的广播,也就是说我们的广播接收器想要监听什么广播,就在这里添加相应的action 就行了。

最后要记得,动态注册的广播接收器一定都要取消注册才行,这里我们是在onDestroy()方法中通过调用unregisterReceiver()方法来实现的。

2,静态注册  可实现开机启动

动态注册的广播接收器可以自由地控制注册与注销,在灵活性方面有很大的优势,但是它也存在着一个缺点,即必须要在程序启动之后才能接收到广播,因为注册的逻辑是写在

onCreate()方法中的。那么有没有什么办法可以让程序在未启动的情况下就能接收到广播呢?这就需要使用静态注册的方式了。

<receiver

android:name="com.hjt.receiver.BootCompleteReceiver">

<intent-filter>

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

</intent-filter>

</receiver>

在<application>标签内出现了一个新的标签<receiver>,所有静态注册的广播接收器都是在这里进行注册的。它的用法其实和<activity>标签非常相似,首先通过android:name来指定具体注册哪一个广播接收器,然后在<intent-filter>标签里加入想要接收的广播就行了,由于Android 系统启动完成后会发出一条值为android.intent.action.BOOT_COMPLETED 的广播,因此我们在这里添加了相应的action。

注意:对于静态注册不再使用内部类的方式来定义广播接收器,因为稍后我们需要在AndroidManifest.xml 中将这个广播接收器的类名注册进去。

到目前为止,我们在广播接收器的onReceive()方法中都只是简单地使用Toast 提示了一段文本信息,当你真正在项目中使用到它的时候,就可以在里面编写自己的逻辑。需要注意的是,不要在onReceive()方法中添加过多的逻辑或者进行任何的耗时操作,因为在广播接收器中是不允许开启线程的,当onReceive()方法运行了较长时间而没有结束时,程序就会报错。因此广播接收器更多的是扮演一种打开程序其他组件的角色,比如创建一条状态栏通知,或者启动一个服务等。

三,发送自定义广播

在发送广播之前,我们还是需要先定义一个广播接收器来准备接收此广播才行,不然发出去也是白发。

广播是一种可以跨进程的通信方式,这一点从前面接收系统广播的时候就可以看出来了。因此在我们应用程序内发出的广播,其他的应用程序应该也是可以收到的。

1,发送标准广播:sendBroadcast()

2,发送有序广播:sendOrderedBroadcast()

button.setOnClickListener(new OnClickListener() {

@Override

public void onClick(View v) {

//发送自定义的(有序)广播com.example.broadcasttest.MY_BROADCAST

Intent intent = new Intent("com.example.broadcasttest.MY_BROADCAST");

sendOrderedBroadcast(intent, null);

}

});

发送有序广播只需要改动一行代码, 即将sendBroadcast() 方法改成sendOrderedBroadcast()方法。sendOrderedBroadcast()方法接收两个参数,第一个参数仍然是Intent,第二个参数是一个与权限相关的字符串,这里传入null 就行了。

那么该如何设定广播接收器的先后顺序呢?当然是在注册的时候进行设定的了,修改AndroidManifest.xml 中的代码

<receiver android:name=".MyBroadcastReceiver">

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

<action android:name="com.example.broadcasttest.MY_BROADCAST"/>

</intent-filter>

</receiver>

可以看到,我们通过android:priority 属性给广播接收器设置了优先级,优先级比较高的广播接收器就可以先收到广播。这里将MyBroadcastReceiver 的优先级设成了100,以保证它一定会在AnotherBroadcastReceiver 之前收到广播。

public class MyBroadcastReceiver extends BroadcastReceiver {

@Override

public void onReceive(Context context, Intent intent) {

Toast.makeText(context, "received in MyBroadcastReceive",Toast.LENGTH_SHORT).show();

//截断广播

abortBroadcast();

}

}

如果在onReceive()方法中调用了abortBroadcast()方法,就表示将这条广播截断,后面的广播接收器将无法再接收到这条广播。

四,本地广播(为了安全考虑)

前面我们发送和接收的广播全部都是属于系统全局广播,即发出的广播可以被其他任何的任何应用程序接收到,并且我们也可以接收来自于其他任何应用程序的广播。这样就很容易会引起安全性的问题,比如说我们发送的一些携带关键性数据的广播有可能被其他的应用程序截获,或者其他的程序不停地向我们的广播接收器里发送各种垃圾广播。

为了能够简单地解决广播的安全性问题,Android 引入了一套本地广播机制,使用这个机制发出的广播只能够在应用程序的内部进行传递,并且广播接收器也只能接收来自本应用程序发出的广播,这样所有的安全性问题就都不存在了。本地广播的用法并不复杂,主要就是使用了一个LocalBroadcastManager 来对广播进行管理,并提供了发送广播和注册广播接收器的方法。

public class MainActivity extends Activity {

private IntentFilter intentFilter;

private LocalReceiver localReceiver;

private LocalBroadcastManager localBroadcastManager;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

localBroadcastManager = LocalBroadcastManager.getInstance(this);

// 获取实例

Button button = (Button) findViewById(R.id.button);

button.setOnClickListener(new OnClickListener() {

@Override

public void onClick(View v) {

Intent intent = new Intent("com.example.broadcasttest.

LOCAL_BROADCAST");

localBroadcastManager.sendBroadcast(intent); // 发送本地广播

}

});

intentFilter = new IntentFilter();

intentFilter.addAction("com.example.broadcasttest.LOCAL_BROADCAST");

localReceiver = new LocalReceiver();

localBroadcastManager.registerReceiver(localReceiver, intentFilter);

// 注册本地广播监听器

}

@Override

protected void onDestroy() {

super.onDestroy();

localBroadcastManager.unregisterReceiver(localReceiver);

}

class LocalReceiver extends BroadcastReceiver {

@Override

public void onReceive(Context context, Intent intent) {

Toast.makeText(context, "received local broadcast",

Toast.LENGTH_SHORT).show();

}

}

}

另外还有一点需要说明,本地广播是无法通过静态注册的方式来接收的。其实这也完全可以理解,因为静态注册主要就是为了让程序在未启动的情况下也能收到广播,而发送本地广播时,我们的程序肯定是已经启动了,因此也完全不需要使用静态注册的功能。最后我们再来盘点一下使用本地广播的几点优势吧。

1. 可以明确地知道正在发送的广播不会离开我们的程序,因此不需要担心机密数据泄漏的问题。

2. 其他的程序无法将广播发送到我们程序的内部,因此不需要担心会有安全漏洞的隐患。

3. 发送本地广播比起发送系统全局广播将会更加高效。

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

推荐阅读更多精彩内容