学而实习之 不亦乐乎

Android:广播(broadcast)的使用

2021-09-26 13:35:12

一、Android广播简介

Android中的广播主要可以分为两种类型:标准广播和有序广播

标准广播(Normal broadcasts):一种完全异步执行的广播,在广播发出之后,所有的广播接收器几乎都会在同一时间接收到这条广播,因此他们之间没有任何的先后顺序。
特点:效率高;缺点:无法拦截。

有序广播(Ordered broadcasts):一种同步执行的广播,在广播发出之后,同一时刻只会有一个广播接收器能够接收到这条广播,当该广播接收器执行完OnReceive()方法逻辑后,广播才会继续传递。 
特点:优先级高者会先接收到广播,并且前面的广播接受器可以拦截正在传递的广播,这样后面的广播就无法收到广播消息了。

二、广播的使用

1.系统广播

Android内置了很多系统级别的广播,我们可以在应用程序中通过监听这些广播来得到各种系统的状态信息。如手机开机完成、电池电量变化、时间或时区改变等。如果想要接收到这些广播,就需要使用广播接收器。广播接收器可以自由地对自己感兴趣的广播进行注册,当相应的广播发出时,广播接收器就能收到此广播。

动态注册监听网络变化

动态注册即在代码中注册。

注意,首先添加权限 

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

private NetWorkChangeReceiver netWorkChangeReceiver;
private IntentFilter intentFilter;

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

         intentFilter = new IntentFilter();
         intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
         netWorkChangeReceiver = new NetWorkChangeReceiver();
         //注册广播
        registerReceiver(netWorkChangeReceiver,intentFilter);
    }

    private class NetWorkChangeReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            //系统服务类
            ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);

            NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();
            //判断网络是否可用
            if (activeNetworkInfo!=null && activeNetworkInfo.isAvailable()){
                Toast.makeText(BroadCastsActivity.this,"网络可用",Toast.LENGTH_SHORT).show();

            }else {
                Toast.makeText(BroadCastsActivity.this,"网络不可用",Toast.LENGTH_SHORT).show();

            }
        }
    }

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

上面我们定义了一个内部类 NetWorkChangeReceiver 继承自 BroadcastReceiver ,并重写 onReceive() 方法,这样当网络状态变化时就会执行 onReceive().
在 onCreate() 中,首先创建了一个 IntentFilter,并给它添加一个 android.net.conn.CONNECTIVITY_CHANGE 的action (当网络状态变化时,系统发出的正是值为android.net.conn.CONNECTIVITY_CHANGE 的广播,也就想要监听什么广播,就添加相应的action),然后用 registerReceiver() 注册广播,将 NetWorkChangeReceiver 的实例和 IntentFilter的实例传入,这样就实现了对网络的监听。

最后,不要忘记取消广播的注销,这里把注销放在了 onDestroy() 中了。

静态注册实现开机启动

动态注册的广播接收器可以自由地控制注册与注销,在灵活性方面有很大的优势,但是它也存在着一个缺点,即必须要在程序启动之后才能接受到广播,因为注册的逻辑是写在 onCreate()方法中的。那么有没有什么方法可以让程序在未启动的情况下就能接受到广播呢?这就需要使用静态注册方式了。

我们将广播接受器命名为 MyReceiver;

  • Export 属性表示是否允许这个广播接收器接受本程序以外的广播;
  • Enable 属性表示是否启用这个广播接受器;

我们使用 Android的快捷方式创建广播接收器,因此注册被自动完成了,打开 AndroidManifest.xml文件看一下:

<receiver
          android:name=".service.MyReceiver"
          android:enabled="true"
          android:exported="true">
          <!--监听开机广播 -->
          <intent-filter>
              <action android:name="android.intent.action.BOOT_COMPLETED"/>
          </intent-filter>
</receiver>

也可以通过Android Studio 提供的快捷方式来创建一个广播接收器,右击项目包名 -> New -> Other -> Broadcast Receiver,然后根据提示进行操作。
权限必须手动配置:开机广播

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

注意:不要在 onReceive()方法中添加过多的逻辑或者进行任何的耗时操作,因为在广播接收器中是不允许开启线程的,当onReceive()方法运行了较长时间而没有结束时,程序就会报错。因此广播接收器更多的是扮演一种打开程序其它组件的角色,比如创建一条状态栏通知,或者启动一个服务等。

/**
 * 广播接受器
 */
public class MyReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        Toast.makeText(context,"开机广播",Toast.LENGTH_LONG).show();
    }
}

2.发送自定义广播

发送标准广播

注册广播

<receiver
           android:name=".service.MyBroadcastReceiver"
           android:enabled="true"
           android:exported="true">
           <intent-filter>
               <action android:name="com.bhs.MyBroadcastReceiver" />
           </intent-filter>
</receiver>

重写广播方法:

public class MyBroadcastReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        Toast.makeText(context,"发送标准广播",Toast.LENGTH_LONG).show();
    }
}

调用广播方法: 

  Intent intent = new Intent("com.bhs.MyBroadcastReceiver");
  sendBroadcast(intent);
发送有序广播

广播是一种跨进程的通信方式,这一点从前面接受系统广播的时候就可以看出来了。因此在我们应用程序内发出的广播,其它应用程序也是可以接收的。

//发送有序广播
 Intent intent = new Intent("com.bhs.MyBroadcastReceiver");
sendOrderedBroadcast(intent,null);

注意这里用的是sendOrderedBroadcast(),不在是sendBroadcast()
第一个参数是 Intent;
第二个参数是一个与权限相关的字符串,这里传 null 就行了

<receiver
       android:name=".service.MyBroadcastReceiver"
       android:enabled="true"
       android:exported="true">       
       <intent-filter android:priority="100" >
         <action android:name="com.bhs.MyBroadcastReceiver" />
       </intent-filter>
</receiver>

android:priority="100" 设置优先级,优先级较高的广播接收器就可以先收到广播。

public class MyBroadcastReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        Toast.makeText(context,"发送标准广播",Toast.LENGTH_LONG).show();
        //拦截广播
        abortBroadcast();
    }
}

在onReceive()方法中调用了 abortBroadcast()方法,就表示这条广播截断,后面的广播接收器无法再接受到这条广播。

三、本地广播

简介:前面我们发送和接受的广播全部属于系统全局广播,即发出的广播可以被其它任何应用程序接收到,并且我们也可以接受来自于其它任何应用程序的广播。
为了解决广播安全性问题,Android 引入了一套本地广播机制,使用这个机制发出的广播只能在应用程序内部进行传递,并且广播接受器也只能接受来自本应用程序发出的广播,这样所有的安全性问题就都不存在了。
使用本地广播很简单,主要使用一个LocalBroadcastManager 来对广播进行管理,并提供了发送广播和注册广播接受器的方法。
初始化广播:

    private IntentFilter intentFilter;
    private LocalReceiver localReceiver;
    private LocalBroadcastManager localBroadcastManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
      intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
        localBroadcastManager = LocalBroadcastManager.getInstance(this); // 获取实例
        Button button = (Button) findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent("com.example.broadcasttest.LOCAL_BROADCAST");
                localBroadcastManager.sendBroadcast(intent); // 发送本地广播
            }
        });
        intentFilter = new IntentFilter();      intentFilter.addAction("com.example.broadcasttest.LOCAL_BROADCAST");
        localReceiver = new LocalReceiver();
        localBroadcastManager.registerReceiver(localReceiver, intentFilter); // 注册本地广播监听器
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
//        unregisterReceiver(networkChangeReceiver);
        localBroadcastManager.unregisterReceiver(localReceiver);
    }

private class LocalReceiver extends BroadcastReceiver {
//重写广播接收方法
        @Override
        public void onReceive(Context context, Intent intent) {
            Toast.makeText(context, "本地广播.....", Toast.LENGTH_LONG).show();
        }
    }

注意:本地广播是无法通过静态注册的方式来接收的,其实这也完全可以理解,因为静态注册主要是为了让程序在未启动的情况下也能接受到广播,而发送本地广播时,我们的程序已经启动了,因此也完全不需要使用静态注册的功能。

本地广播的优势

  • 可以明确知道正在发送的广播不会离开我们的程序,因此不必担心机密数据泄露。
  • 其它的程序无法将广播发送到我们程序内部,因此不需要担心会有安全漏洞问题。
  • 发送本地广播比发送系统全局广播更加高效。

广播常用的地方:类似 QQ强制下线功能,出现一个弹窗,杀死所有 Activity,强制进入登录页面(可以选用,静态广播,动态广播或者本地广播)。