Android笔记:BroadcastReceiver

374 阅读4分钟

广播

发送广播: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,unregisterReceiverregisterReceiver方法前都加上manager.