BroadCastReceiver学习笔记(基础篇)

一、BroadCastReceiver简介

BroadcastReceiver是Android四大组件之一,本质是一种全局的监听器,用于监听系统或者应用全局的广播消息,然后根据广播信息做出相应的逻辑处理,也可以用来传输少量、频率低的数据。因此它可以非常方便的实现不同组件之间的通信。
  Android广播分为两个方面:广播发送者和广播接收者,通常情况下,BroadcastReceiver指的就是广播接收者。从实现原理看上,Android中的广播使用了观察者模式,基于消息的发布/订阅事件模型。具体实现流程要点粗略概括如下:

  1. 广播接收者BroadcastReceiver通过Binder机制向AMS(Activity Manager Service)进行注册;
  2. 广播发送者通过binder机制向AMS发送广播;
  3. AMS查找符合相应条件(IntentFilter/Permission等)的BroadcastReceiver,将广播发送到BroadcastReceiver(一般情况下是Activity)相应的消息循环队列中;
  4. 消息循环执行拿到此广播,回调BroadcastReceiver中的onReceive()方法。

二、接收系统广播

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

我们先通过网络状态变化这一系统广播来实践一下BroadCastReceiver的动态注册。

首先,我们需要在AndroidManifest文件中申请网络访问权限。

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.kingdee.zhao.broadcastreceivertest">

    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>

    ... ...

</manifest>

新建NetworkChangeReceiver继承自BroadCastReceiver,重写onReceive()方法。

public class NetworkChangeReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        // 获取手机所有连接管理对象
        ConnectivityManager connectivityManager =
                (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
        // 获取NetworkInfo对象
        NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
        if (networkInfo != null && networkInfo.isAvailable()) {
            Toast.makeText(context, "Network is available!", Toast.LENGTH_SHORT).show();
        } else {
            Toast.makeText(context, "Network is unavailable!", Toast.LENGTH_SHORT).show();
        }
    }
}

首先通过getSystemService() 方法得到了系统服务类ConnectivityManager的实例,专门用于管理网络连接的。然后调用它的getActiveNetworkInfo()方法可以得到NetworkInfo的实例,接着调用NetworkInfo的isAvailable()方法,就可以判断出当前是否有网络连接了。

在MainActivity的onCreate()方法中注册该广播,在onDestroy()方法中注销该广播。

public class MainActivity extends AppCompatActivity {

    private IntentFilter intentFilter;

    private NetworkChangeReceiver networkChangeReceiver;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 设置对应的intent启动条件为网络状态发生变换
        intentFilter = new IntentFilter();
        intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");

        networkChangeReceiver = new NetworkChangeReceiver();
        // 注册广播
        registerReceiver(networkChangeReceiver, intentFilter);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 注销广播
        unregisterReceiver(networkChangeReceiver);
    }
}

我们创建了一个IntentFilter的实例,并给它添加了一个action,因为当网络状态发生变化时,系统发出的正是一条值为"android.net.conn.CONNECTIVITY_CHANGE"的广播,也就是说我们的广播接收器想要监听什么广播,就在这里添加相应的action进行筛选。接下来创建了一个NetworkChangeReceiver的实例,然后调用registerReceiver()方法进行注册,将NetworkChangeReceiver的实例和IntentFilter的实例都传了进去,这样NetworkChangeReceiver就会收到所有值为android.net.conn.CONNECTIVITY_CHANGE的广播,也就实现了监听网络变化的功能。

运行程序,使用下拉通知栏更改网络连接状态,我们会发现以下现象,证明我们接收到了系统发出的广播。

image.png
image.png

现在我们实践一下广播的静态注册,首先新建一个BootCompleteReceiver,Android Studio右键new中的other可以自动创建broadcastReceiver,这样Android Studio就已经帮我们在Manifest文件中静态注册了BootCompleteReceiver。重写onReceive()方法,弹出Toast

public class BootCompleteReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        Toast.makeText(context, "Boot Complete", Toast.LENGTH_SHORT).show();
    }
}

修改AndroidManifest.xml文件,添加监听系统开机广播的权限,并且在receiver中添加对应的action。这样手机重启的时候,该应用就会收到来自系统发出的广播而响应onReceive()。

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.kingdee.zhao.broadcastreceivertest">

    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>

    <application
        
        ... ...

        <receiver
            android:name=".broadcastReceiver.BootCompleteReceiver"
            android:enabled="true"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
            </intent-filter>
        </receiver>
    </application>

</manifest>

三、发送自定义广播

新建一个广播接收器来接受此广播,新建一个MyBroadcastReceiver

public class MyBroadcastReceiver extends BroadcastReceiver{
    @Override
    public void onReceive(Context context, Intent intent) {
        Toast.makeText(context, "Received in MyBroadcastReceiver",Toast.LENGTH_SHORT).show();
    }
}

修改AndroidManifest文件,让MyBroadcastReceiver接收到“com.kingdee.broadcasttest.MY_BROADCAST”时响应。

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    
    ... ...

    <application
    
        ... ...
        
        <receiver
            android:name=".broadcastReceiver.MyBroadcastReceiver"
            android:enabled="true"
            android:exported="true">
            <intent-filter>
                <action android:name="com.kingdee.broadcasttest.MY_BROADCAST"/>
            </intent-filter>
        </receiver>

    </application>

</manifest>

为Activity添加一个Button来发送自定义的广播信号。先在xml文件中添加button,然后再MainActivity中注册监听事件,调用sendBroadcast(intent)来发送广播。

public class MainActivity extends AppCompatActivity {

    ... ...

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        ... ...
        
        Button sendBtn = findViewById(R.id.btn_send);
        sendBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent("com.kingdee.broadcasttest.MY_BROADCAST");
                sendBroadcast(intent);

            }
        });
    }

    ... ...
    
}

运行程序,点击button会发现弹出了Toast,说明我们在button的点击事件中发出的自定义广播已经被接收到,并执行了onReceive()方法。

image.png

广播是一种可以跨进程的通信方式,这一点从前面接收系统广播的时候就可以看出来了。因此在我们应用程序内自定义发出的广播,其他的应用程序应该也是可以收到的。我们可以再新建一个BroadcastTest2项目,在这个项目下定义一个广播接收器,用于接收第一个项目中发出中的自定义广播。

public class AnotherBroadcastReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        Toast.makeText(context, "received in AnotherBroadcastReceiver", Toast.LENGTH_SHORT).show();
    }
}

同样在Manifest文件中设置广播接收器的响应信号。

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.kingdee.zhao.broadcastreceivertest2">

    <application
    
        ... ...
        
        <receiver
            android:name=".broadcastReceiver.AnotherBroadcastReceiver"
            android:enabled="true"
            android:exported="true">
            <intent-filter>
                <action android:name="com.kingdee.broadcasttest.MY_BROADCAST" />
            </intent-filter>
        </receiver>
    </application>

</manifest>

运行程序,点击BroadCastReceiverTest的button,我们会发现弹出了两次Toast,说明广播被跨进程接收了。

接下来我们让广播的发送有序化。修改BroadCastReceiverTest中发送广播的方式为sendOrderedBroadcast()。

public class MainActivity extends AppCompatActivity {

    ... ...

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        
        ... ...
        
        Button sendBtn = findViewById(R.id.btn_send);
        sendBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent("com.kingdee.broadcasttest.MY_BROADCAST");
                sendOrderedBroadcast(intent, null);

            }
        });
    }

    ... ...
        
}

在AndroidManifest文件中设置MyBroadcastReceiver广播的优先级为100(高于AnotherBroadcasrReceiver)

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.kingdee.zhao.broadcastreceivertest">

    ... ...

    <application
        
        ... ...
        
        <receiver
            android:name=".broadcastReceiver.MyBroadcastReceiver"
            android:enabled="true"
            android:exported="true">
            <intent-filter
                android:priority="100">
                <action android:name="com.kingdee.broadcasttest.MY_BROADCAST"/>
            </intent-filter>
        </receiver>

    </application>

</manifest>

修改MyBroadcastReceiver的代码,在onReceive()的时候调用abortBroadcast()去截断广播。

public class MyBroadcastReceiver extends BroadcastReceiver{
    @Override
    public void onReceive(Context context, Intent intent) {
        Toast.makeText(context, "Received in MyBroadcastReceiver",Toast.LENGTH_SHORT).show();
        abortBroadcast();
    }
}

再次运行程序,点击button,我们会发现只有MyBroadcastReceiver接收到了广播并弹出了Toast,AnotherBroadcastReceiver没有接收到,因为被前一个广播接收器截断了。

四、使用本地广播

全局广播可以被各个应用程序接收处理,本地广播则只在本应用内部传递,增加了广播的安全性。本地广播使用LocalBroadcastManager来管理广播,广播的发送和接收都通过LocalBroadcastManager来进行。因此,也本地广播无法在AndroidManifest中注册。

修改MainActivity的代码:

public class MainActivity extends AppCompatActivity {

    private IntentFilter intentFilter;

//    private NetworkChangeReceiver networkChangeReceiver;

    private LocalReceiver localReceiver;

    private LocalBroadcastManager localBroadcastManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 获取实例
        localBroadcastManager = LocalBroadcastManager.getInstance(this);

//        // 设置对应的intent启动条件为网络状态发生变换
//        intentFilter = new IntentFilter();
//        intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
//
//        networkChangeReceiver = new NetworkChangeReceiver();
//        // 注册广播
//        registerReceiver(networkChangeReceiver, intentFilter);

        Button sendBtn = findViewById(R.id.btn_send);
        sendBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent("com.kingdee.broadcasttest.LOCAL_BROADCAST");
                // 发送本地广播
                localBroadcastManager.sendBroadcast(intent);
            }
        });

        // 设置对应的intent对应的条件过滤器
        intentFilter = new IntentFilter();
        intentFilter.addAction("com.kingdee.broadcasttest.LOCAL_BROADCAST");

        // 注册本地广播监听器
        localReceiver = new LocalReceiver();
        localBroadcastManager.registerReceiver(localReceiver, intentFilter);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 注销本地广播
        localBroadcastManager.unregisterReceiver(localReceiver);
    }
}

这基本上就和我们前面所学的动态注册广播接收器以及发送广播的代码是一样的。只不过现在首先是通过LocalBroadcastManager的getInstance() 方法得到了它的一个实例,然后在注册广播接收器的时候调用的是LocalBroadcastManager的registerReceiver() 方法,在发送广播的时候调用的是LocalBroadcastManager的sendBroadcast()方法,仅此而已。这里我们在按钮的点击事件里面发出了一条com.example.broadcasttest.LOCAL_BROADCAST广播,然后在LocalReceiver里去接收这条广播。重新运行程序,点击Button,我们接收到了本地广播。

image.png

推荐阅读更多精彩内容