广播
发送广播:Intent
接收广播:BroadcastReceiver
广播的类型
标准广播(Normal Broadcasts):异步,发出之后所有的Receiver同时收到消息,效率高,无法截断
有序广播(Ordered Broadcasts):同步,根据优先级一次只有一个Receiver可以收到消息,执行完后才会继续传递,可以截断/修改
系统广播(System Boardcast)
实现原理
使用了观察者设计模式:基于消息的发布/订阅事件模型

接收广播
在执行onReceive()时属于前台进程
广播接收器默认运行在主线程
动态注册接收器
-
在代码中注册,只要当前上下文有效就可以接受到广播,不会每次都重建BroadcastReceiver
-
优点:灵活,跟随组件的生命周期变化
-
缺点:程序启动之后才可以接收广播
以检查当前网络连接状态为例
public class MainActivity extends AppCompatActivity {
private IntentFilter filter;
private NetworkChangeReceiver receiver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//动态注册
filter = new IntentFilter();
//限定接收的隐式intent(想要监听什么广播,就给过滤器添加相应的action)
filter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
receiver = new NetworkChangeReceiver();
registerReceiver(receiver, filter);//向AMS注册接收器
}
@Override
protected void onDestroy() {
super.onDestroy();
//动态注册的广播接收器必须要取消注册,否则内存泄漏
unregisterReceiver(receiver);
}
class NetworkChangeReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
//获取专门用于管理网络连接的系统服务类
ConnectivityManager manger = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
//得到当前使用的网络的信息
NetworkInfo info = manger.getActiveNetworkInfo();
if (info != null && info.isAvailable())
Toast.makeText(context, "网络已连接", Toast.LENGTH_SHORT).show();
else
Toast.makeText(context, "网络未连接", Toast.LENGTH_SHORT).show();
}
}
}
在Manifest里声明访问系统网络状态的权限
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
静态注册接收器(8.0+无法接收隐式广播)
-
在ManiFest中注册
-
每次广播被接收后会创建新的BoardcastReceiver对象,并在onReceive()执行完时销毁
-
隐式广播是不指定目的地的广播
-
优点:不需要启动程序也可以接收(APP首次启动时会将其注册到AMS中)
右击New→Other→Broadcast Receiver
public class BootCompleteReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
//不要在这个方法里面执行耗时操作,超过10s会ANR
Toast.makeText(context,"开机啦",Toast.LENGTH_LONG).show();
}
}
动态注册是在代码中设置intentFilter,静态注册是在ManiFest中
enabled属性表示是否启用这个Receiver(有intent-filter默认为true,否则为false)
export属性表示是否允许这个Receiver接收本程序以外的广播,设为false即为本地广播
permission属性表示具有相应权限的广播发送者发送的广播才能被此receiver接收
process属性可以指定该receiver的运行进程,默认为APP进程
<receiver
android:name=".BootCompleteReceiver"
android:enabled="true"
android:exported="true"
android:permission="..."
android:process="..." >
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
想要接收开机广播还要加一条权限
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
发送广播
发送给AMS
发送标准广播
无视优先级:动态注册的接收器永远要先于静态注册的接收器接收到广播
类别相同:先扫描的优先于后扫描的(静态)先注册的优先于后注册的(动态)
Intent intent=new Intent("com.example.broadcasttest.MY_BROADCAST");
//安卓8.0+,如果接收器是静态注册的,那么要指定发给哪个包,即变成显式广播,这里是发给自己
intent.setPackage(getPackageName());
//发送广播
sendBroadcast(intent);
发送有序广播
优先级相同:动态注册的接收器优先
优先级和类别都相同:先扫描的优先于后扫描的(静态)先注册的优先于后注册的(动态)
设置优先级(数字越小,优先级越高)
本例为静态注册接收器,动态注册接收器可以在代码中设置
<intent-filter android:priority="100" >
<action android:name="com.example.broadcasttest.MY_BROADCAST" />
</intent-filter>
发送有序广播
Intent intent=new Intent("com.example.broadcasttest.MY_BROADCAST");
//安卓8.0+,如果接收器是静态注册的,那么要指定发给哪个包,即变成显式广播,这里是发给自己
intent.setPackage(getPackageName());
//第二个参数是与权限相关的字符串,用null就可以了
sendOrderedBroadcast(intent,null);
优先级高的Receiver可以截断广播/修改广播后继续发送
abortBroadcast();//截断有序广播
发送本地广播
发出的广播只能在本应用程序内部进行传递,解决安全性问题
实现方式一
静态注册
注册时将exported属性设为false
通过permission属性增设响应权限用于验证
在发送广播时指定包名
intent.setPackage(getPackageName());
实现方式二
动态注册
在发送广播的活动中新建LocalBroadcastManager实例
LocalBroadcastManager manager = LocalBroadcastManager.getInstance(this);
其余步骤和动态注册接收器一样
只是在sendBroadcast,unregisterReceiver和registerReceiver方法前都加上manager.