本文目录结构:
一、广播机制简介
二、接收系统广播
三、发送自定义广播
四、使用本地广播
一、广播机制简介
定义:
类似于网络通信原理中的广播,Android中也有一套类似的广播机制。通俗来讲,广播分为发送者和接收者,发送者发出一条广播,相应的接收者就会收到一条广播。简单来讲,广播就是Android中的一种通信方式。
使用场景:
(1)同一app内部的同一组件内的消息通信(单个或多个线程之间);
(2)同一app内部的不同组件之间的消息通信(单个进程);
(3)同一app具有多个进程的不同组件之间的消息通信;
(4)不同app之间的组件之间消息通信;
(5)Android系统在特定情况下与App之间的消息通信。
基本原理和实现流程
对于基本原理
广播使用了观察者模式,是基于消息的发布/订阅模型,与EventBus相似。广播发送者和广播接收者分别属于观察者模式中的消息发布和订阅两端,AMS属于中间的处理中心。广播发送者和广播接收者的执行是异步的,发出去的广播不会关心有无接收者接收,也不确定接收者到底是何时才能接收到。
对于实现流程,主要分为以下四步:
(1)首先,广播接收者BroadcastReceiver通过Binder机制向AMS(Activity Manager Service)进行注册;
(2)然后,广播发送者通过binder机制向AMS发送广播;
(3)接着,AMS查找符合相应条件(IntentFilter/Permission等)的BroadcastReceiver,将广播发送到BroadcastReceiver(一般情况下是Activity)相应的消息循环队列中;
(4)最后,消息循环执行拿到此广播,回调BroadcastReceiver中的onReceive()方法。
分类
Android中的广播主要分为两类:标准广播和有序广播
标准广播
标准广播是一种完全异步执行的广播,在广播发出之后,所有的广播接收器几乎都会在同一时刻接收到这条广播,所以他们之间没有任何顺序可言。这种广播的效率会比较高,但同时也意味着它是无法被截断的。

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

二、接收系统广播
Android内置了很多系统级别的广播,我们可以在应用程序中通过监听这些广播来得到系统的各种状态信息。比如手机开机完成后会发出一条广播,电池的电量发生变化会发出一条广播,时间或时区发生改变也会发出一条广播等等。
广播接收器的注册分为两类:动态在代码中注册和静态在AndroidManifest.xml文件中注册
动态注册监听网络变化
整体思路:
首先,新建一个类让它继承BroadcastReceiver并重写父类onReceive方法。
然后,实例化IntentFilter对象并添加Action。
接着,在activity的生命周期函数中注册广播接收器。
最后,在activity的生命周期函数中取消注册广播接收器。
public class NetworkChangeReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
ConnectivityManager connectivityManager = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
//这里需要申请权限
//<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
if (networkInfo != null && networkInfo.isConnected()){
Toast toast = Toast.makeText(context, "网络恢复咯!", Toast.LENGTH_SHORT);
toast.setGravity(Gravity.CENTER, 0, 0);
toast.show();
}else {
Toast toast = Toast.makeText(context, "网络开了点小差!", Toast.LENGTH_SHORT);
toast.setGravity(Gravity.CENTER, 0, 0);
toast.show();
}
}
}
public class MainActivity extends AppCompatActivity {
private IntentFilter intentFilter;
private NetworkChangeReceiver mNetworkChangeReceiver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
intentFilter = new IntentFilter();
//系统发出的广播就是android.net.conn.CONNECTIVITY_CHANGE
//所以我们的广播接收器想要监听什么样的广播,在这里添加相应的Action即可。
intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
mNetworkChangeReceiver = new NetworkChangeReceiver();
registerReceiver(mNetworkChangeReceiver,intentFilter);
}
@Override
protected void onDestroy() {
super.onDestroy();
//动态注册的广播接收器一定都要取消注册才行
unregisterReceiver(mNetworkChangeReceiver);
}
}
动态注册可以自由地控制注册与注销,比较灵活,但这也意味着必须要启动该程序才会起作用。而静态注册可以解决该问题。
静态注册实现开机提醒
整体思路:
首先,同样是让一个类继承BrocastReceiver,并重写onReceive方法。
接着,在AndroidManifest文件中申请监听开机广播。
然后,在AndroidManifest文件中注册广播接收器即可。
public class BootCompleteReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Toast toast = Toast.makeText(context, "开机啦!", Toast.LENGTH_SHORT);
toast.setGravity(Gravity.CENTER, 0, 0);
toast.show();
}
}
<manifest ...>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
...
<!--exported属性表示是否允许这个广播接收器接收本程序之外的广播-->
<!--enabled属性表示是否启用这个广播接收器-->
<application ...>
<receiver
android:name=".BootCompleteReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.RECEIVE_BOOT_COMPLETED"/>
</intent-filter>
</receiver>
</application>
</manifest>
注意,不要在onReceive方法中添加过多的逻辑或者进行任何的耗时操作,因为在广播接收器中是不允许开线程的。当onReceive方法运行了较长时间(最长10s)而没有结束时,程序就会报错。
三、发送自定义广播
发送标准广播
整体思路:
首先,构建一个Intent对象。
然后,把要发送的广播的值传进去。
最后,调用Context的sendBroadcast方法发送出去即可。
//这里发出了一条为top.longsh1z.www.broadcasttest.MY_BROADCAST的广播
Intent intent = new Intent("top.longsh1z.www.broadcasttest.MY_BROADCAST");
sendBroadcast(intent);
//因为广播是使用Intent进行传递的,所以还可以在Intent中携带一些数据传递给广播接收器
//onReceive方法中的第二个参数为Intent对象,取出来使用即可。
发送有序广播
整体思路:
和发送自定义广播类似,都是构建Intent对象,设置广播的值,最后发送出去即可。但这里的发送从sendBroadcast变成了sendOrderedBroadcast,然后给广播接收器设置优先级,在onReceive方法设置是否截断广播。
Intent intent = new Intent("top.longsh1z.www.broadcasttest.MY_BROADCAST");
sendOrderedBroadcast(intent,null);
//若为动态注册的广播接收器,设置优先级为如下步骤:
intentFilter = new IntentFilter();
intentFilter.addAction("top.longsh1z.www.broadcasttest.MY_BROADCAST");
intentFilter.setPriority(100); //设置优先级
myReceiver = new MyReceiver();
registerReceiver(myReceiver,intentFilter);
//若为静态注册的广播接收器,则在Manifest文件中设置:
<receiver
android:name=".MyReceiver"
android:enabled="true"
android:exported="true">
<intent-filter android:priority="100">
<action android:name="top.longsh1z.www.broadcasttest.MY_BROADCAST"/>
</intent-filter>
</receiver>
public class MyReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Toast toast = Toast.makeText(context, "发送有序广播啦!", Toast.LENGTH_SHORT);
toast.setGravity(Gravity.CENTER, 0, 0);
toast.show();
//截断广播,后面的广播接收器就收不到该条广播了。
abortBroadcast();
}
}
四、使用本地广播
前面我们发送和接收的广播全部属于系统全局广播,即发出的广播可以被其他任何应用程序接收到,并且我们也可以接收到来自其他任何应用程序的广播。所以就存在着安全性的问题。而使用本地广播可以只在本应用程序内起作用,不涉及安全问题。
整体思路:
和动态注册广播接收器差不多,只不过是加多了一个LocalBroadcastManager类对象来注册和注销广播接收器而已。
public class MainActivity extends AppCompatActivity {
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 = findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener(){
@Override
protected void onClick(View v){
Intent intent = new Intent("top.longsh1z.www.broadcasttset.LOCAL_BROADCAST");
localBroadcastManager.sendBroadcast(intent);
}
});
intentFilter = new IntentFilter();
intentFilter.addAction("top.longsh1z.www.broadcasttset.LOCAL_BROADCAST");
localReceiver= new LocalReceiver();
localBroadcastManager.registerReceiver(localReceiver,intentFilter);
}
@Override
protected void onDestroy() {
super.onDestroy();
//动态注册的广播接收器一定都要取消注册才行
localBroadcastManager.unregisterReceiver(mNetworkChangeReceiver);
}
class LocalReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
Toast toast = Toast.makeText(context, "收到本地广播啦!", Toast.LENGTH_SHORT);
toast.setGravity(Gravity.CENTER, 0, 0);
toast.show();
}
}
}
本地广播是无法通过静态注册的方式来接收的,因为静态注册主要是为了让程序在未启动的情况下也能接收到广播,而发送本地广播时程序已经启动了。所以就没必要静态注册了。
网友评论