前言:
现存在的 大喇叭 ,还有我们的网络通信中使用的 IP 都无一的利用了类似广播这一原理来进行对信息来进行传递,为了便于进行系统级别的消息通知,Android引入了一套类似的广播消息机制。相比于我前面举出的两个例子,Android中的广播机制会显得更加的灵活,本文就对这一机制的方方面进行详细的讲解。
通过本文你将会学到以下几个方面
(注意以下代码例子均为个人案例)
- 广播是什么
- 广播接收器
- 本地广播
- 系统广播
- 广播内部的实现机制
广播是什么
android通过广播来实现不同进程间的通信对应于广播(broadcat)还有一个广播接收器(broadcast receiver)每个广播指定了对应的action 、 type等信息,每个接收器根据这些信息来过滤是否自己要接收的广播。
广播接收器
广播接收器主要分为两种类型:标准广播 和 有序广播
1、标准广播
标准广播是一种完全异步执行的广播,在广播发出之后,所有的广播接收器几乎都会在同一时刻接收到这条广播的信息,因此他们之间没有任何的先后顺序可言。传播如下图所示。
标准广播又区分为两种:动态注册 和 静态注册
下面我们来简单讲述一下标准广播的使用方法,我们这里先来说一下 动态注册 这一种吧。
在我们发送广播之前,我们还是需要先定义一个广播接收器来准备接收此广播才行,不然发出去也没人去进行接收,代码如下:
class Demo1Recceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Log.e("onReceive:","内部的接收到");
}
}
当收到广播是会打印出如上日志,我们有了广播接收器之后,我们就可以为所欲为的在我们想接收的地方进行注册一个广播来进行接收,代码如下:
IntentFilter filter=new IntentFilter("com.example.jie.Broad");
receiver=new Demo1Recceiver();
registerReceiver(receiver,filter);
以上的 IntentFilter 为广播过滤器,意味着我们只能够接收到 带有 “com.example.jie.Broad” (自定义)发出的广播。
上面我们已经有了广播接收器,并且在我们想要的地方进行注册了,我们就可以发送广播了。
Intent intent=new Intent("com.example.jie.Broad");
intent.putExtra("temp","Lin");
sendBroadcast(intent);
如上我们可以通过 intent 进行携带信息。
还有一点需要我们记住的是,当我们不需要这个广播的时候我们应该对广播进行注销
@Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(receiver);
}
这就是动态广播的简单使用。
下面就讲述我们的 静态注册
广播接收器还是跟动态注册的一样:
class Demo2Receiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Log.e("onReceive:","BroadCastDemo" );
}
}
如果我们设置为静态注册的时候,我们的广播接收器就一个设置为 独立外部类 或者是 静态内部类
可能有些人会问为什么要设置成为静态内部类呢 原因如下
你在XML中声明而且使用内部类当然需要使用静态化,系统从XML实例化时只会实例化你的类,不会实例化父类,而非静态内部类需要依赖父类的实例去实例化,所以必然是实例化不了的。如果在XML中定义Receiver,你要么使用静态内部类,要么直接使用独立的类来实现。如果你的类有依赖实例,必须依赖一些实例才能正常运行,那就在程序中实例化和注册,不要使用XML来声明。
唯一的不同就是静态注册我们就不需要在代码中进行注册,只需要在AndroidManifest.xml 中进行一次注册就可以,如下:
<receiver android:name=".MainActivity$Demo2Receiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.example.jie.Broad"/>
</intent-filter>
</receiver>
上面的 enabled 设置为 true 意味着能够接受到信息 ,exported 为 true 意味着能够接收到外部apk广播发送的信息
发送还是跟动态发送一般:
Intent intent=new Intent("com.example.jie.Broad");
intent.putExtra("temp","Lin");
sendBroadcast(intent);
静态注册的讲解也就到此就结束
我们通过上面广播的方式可以实现两个相同或不同的 apk 进行信息交流。
经过我这么一讲你也许会想到,既然我们的应用发出的广播别的应用也可以收到,那会不会造成安全隐患呢?是的,上述普通广播 的确存在安全问题。接下来我们就带着这一问题讲解下面的内容。
带权限的标准广播
可能引发的安全问题:
1. 如果别的应用程序监听我们的广播,那么会造成我们应用程序的数据泄露;
2. 如果别的应用程序冒充我们的应用发送广播,那么就会频繁的启动我们的广播接收程序,造成我们应用的混乱,甚至崩溃。
好在,Android早已考虑了这个问题,为我们提供了权限机制。首先针对第一个问题,在我们发送广播时,可以为它指定一个权限,只有具有该权限的应用才能接收到广播,如下所示:
注意(我们把下面 发送信息 的apk称为 甲,接收的信息 的apk称为 乙)
为解决第一个问题我们可以如下例子
甲:
Intent intent=new Intent("com.example.jie.Broad");
intent.putExtra("temp","Lin");
sendBroadcast(intent,"com.cn.customview.permissions.MY_BROADCAST");
乙 我们在乙中添加如下权限我们就可以获取得到已经得到甲加了权限的
<protected-broadcast android:name="com.example.jie.Broad" />
<uses-permission android:name="com.cn.customview.permissions.MY_BROADCAST"/>
<permission android:name="com.cn.customview.permissions.MY_BROADCAST" />
这就是我们解决第一个问题的方法
然后我们就可以来解决第二个问题
如何不随意接收别的程序发过来的程序(甲乙如同上面一样)
乙 这里我们可以分为 静态注册 和 动态注册 两种
静态注册
<receiver android:name=".MainActivity$Demo2Receiver"
android:enabled="true"
android:permission="com.cn.customview.permissions.MY_BROADCAST"
android:exported="true">
<intent-filter>
<action android:name="com.example.jie.Broad"/>
</intent-filter>
</receiver>
动态注册
IntentFilter filter=new IntentFilter("com.example.jie.Broad");
receiver=new Demo2Receiver();
registerReceiver(receiver,filter,"com.cn.customview.permissions.MY_BROADCAST",null);
这就可以解决我们的第二个问题
有序广播
我们的有序广播看起来其实没什么区别,不过,这个时候的广播接收器是有先后顺序的,而且前面的广播可以对后续的广播进行截断,以阻止让其继续广播。传播如下图所示,权限高者会先获取得到广播的信息
如上图我们可以猜想到前面的广播可以先截取到信息,然后进行截断,或者更改信息内容。
权限的高低设置方法如下
IntentFilter filter=new IntentFilter("com.example.jie.Broad");
filter.setPriority(100);
receiver=new Demo2Receiver();
registerReceiver(receiver,filter,"com.cn.customview.permissions.MY_BROADCAST",null);
调用IntentFilter的setPriority(int priority)方法设置优先级,参数值可以是-1000~1000,值越大,优先级越高。同样的在静态注册中,通过设置intent-filter标签的priority属性来设置优先级,代码如下:
<receiver android:name=".MainActivity$Demo2Receiver"
android:enabled="true"
android:permission="com.cn.customview.permissions.MY_BROADCAST"
android:exported="true">
<intent-filter android:priority="100">
<action android:name="com.example.jie.Broad"/>
</intent-filter>
</receiver>
截断的方法如下 abortBroadcast();
class Demo2Receiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Log.e("onReceive:","BroadCastDemo" );
abortBroadcast();
}
}
其实为了解决安卓的广播安全问题安卓还引入了另一个 LocalBroadcastManger 类
本地广播
(注意下方甲乙如上一样,甲为发送方,乙为接收方)
甲
LocalBroadcastManager localManager=LocalBroadcastManager.getInstance(this);
Intent localIntent=new Intent("com.example.jie.Broad");
localManager.sendBroadcast(localIntent);
乙
IntentFilter localFilter=new IntentFilter("com.example.jie.Broad");
LocalReceiver localReceiver=new LocalReceiver();
localManager.registerReceiver(localReceiver,localFilter);
系统广播
由名可知,我们可以通过注册来接收到广播的发出来的一些系统通知,如飞行模式被打开,音量键被按,等等,下面就是监听系统广播的各个 action
(注意甲乙如上文所示)
这里我们也可以划分动态注册,静态注册
动态注册
IntentFilter filter=new IntentFilter();
filter.addAction(getIntent().ACTION_AIRPLANE_MODE_CHANGED);
receiver=new Demo1Recceiver();
registerReceiver(receiver,filter);
静态注册
<receiver android:name=".MainActivity$Demo2Receiver"
android:enabled="true"
android:exported="true">
<intent-filter >
<action android:name="android.intent.action.AIRPLANE_MODE"/>
</intent-filter>
</receiver>
这里我们是监视飞行模式的行为进而发出广播,如下是系统的一些常见的行为
关闭或打开飞行模式时的广播 | Intent.ACTION_AIRPLANE_MODE_CHANGED |
充电状态,或者电池的电量发生变化//电池的充电状态 | Context.registerReceiver()注册 ;Intent.ACTION_BATTERY_CHANGED |
表示电池电量低 | Intent.ACTION_BATTERY_LOW |
表示电池电量充足,即从电池电量低变化到饱满时会发出广播 | Intent.ACTION_BATTERY_OKAY |
在系统启动完成后,这个动作被广播一次 | Intent.ACTION_BOOT_COMPLETED |
按下照相时的拍照按键(硬件按键)时发出的广播 | Intent.ACTION_CAMERA_BUTTON |
当屏幕超时进行锁屏时,当用户按下电源按钮,长按或短按 | Intent.ACTION_CLOSE_SYSTEM_DIALOGS |
设备当前设置被改变时发出的广播:(包括的改变:界面语言,设备方向,等,请参考Configuration.java) | Intent.ACTION_CONFIGURATION_CHANGED |
设备日期发生改变时会发出此广播 | Intent.ACTION_DATE_CHANGED; |
设备内存不足时发出的广播,此广播只能由系统使用,其它APP不可用 | Intent.ACTION_DEVICE_STORAGE_LOW |
设备内存从不足到充足时发出的广播,此广播只能由系统使用,其它APP不可用 | Intent.ACTION_DEVICE_STORAGE_OK |
移动APP完成之后,发出的广播 | ntent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE |
正在移动APP时,发出的广播 | Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE |
在耳机口上插入耳机时发出的广播 | Intent.ACTION_HEADSET_PLUG |
改变输入法时发出的广播 | Intent.ACTION_INPUT_METHOD_CHANGED |
设备当前区域设置已更改时发出的广播 | Intent.ACTION_LOCALE_CHANGED |
按下”Media Button” 按键时发出的广播,假如有”Media Button” 按键的话 | Intent.ACTION_MEDIA_BUTTON |
插入外部储存装置,比如SD卡时,系统会检验SD卡,此时发出的广播 | Intent.ACTION_MEDIA_CHECKING |
已拔掉外部大容量储存设备发出的广播 | Intent.ACTION_MEDIA_EJECT |
设备墙纸已改变时发出的广播 | Intent.ACTION_WALLPAPER_CHANGED |
当前时间已经变化(正常的时间流逝), 当前时间改变,每分钟都发送,不能通过组件声明来接收 | Context.registerReceiver();Intent.ACTION_TIME_TICK |
时间被设置时发出的广播 | Intent.ACTION_TIME_CHANGED |
时区发生改变时发出的广播 | Intent.ACTION_TIMEZONE_CHANGED |
关闭系统时发出的广播 | Intent.ACTION_SHUTDOWN |
屏幕被打开之后的广播 | Intent.ACTION_SCREEN_ON |
屏幕被关闭之后的广播 | Intent.ACTION_SCREEN_OFF |
重启设备时的广播 | Intent.ACTION_REBOOT |
已断开外部电源连接时发出的广播 | Intent.ACTION_POWER_DISCONNECTED |
插上外部电源时发出的广播 | Intent.ACTION_POWER_CONNECTED |
成功的删除某个APK之后发出的广播, 一个已存在的应用程序包已经从设备上移除 | Intent.ACTION_PACKAGE_REMOVED |
替换一个现有的安装包时发出的广播 | Intent.ACTION_PACKAGE_REPLACED |
清除一个应用程序的数据时发出的广播 | Intent.ACTION_PACKAGE_DATA_CLEARED |
成功的安装APK之后//广播 | Intent.ACTION_PACKAGE_ADDED |
我们可以通过对系统行为的监测,实施我们想要完成的行为。
广播内部的实现机制
简单地说,Android广播机制的主要工作是为了实现一处发生事情,多处得到通知的效果。这种通知工作常常要牵涉跨进程通讯,所以需要由AMS(Activity Manager Service)集中管理。
可参考文章:http://my.oschina.net/youranhongcha/blog/226274?fromerr=kZfhK8dQ
本文就到此结束,thanks