Android广播机制-Broadcast Receiver

本文深入解析Android广播机制,包括标准广播、有序广播的功能与实现,以及如何发送与接收自定义广播,通过实例演示动态与静态注册方式,并介绍本地广播机制以提高安全性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


广播机制

为了进行系统级别的消息通知,Android引入了一套广播消息机制,就像是我们上小学的时候,每个教室都会有个喇叭,到时间就会播放眼保健操一样。

广播有两种

  1. 标准广播
  2. 有序广播

标准广播是一种异步执行的广播,广播发出之后,所有的广播接收器都可以几乎在同一时间接收到这条广播,没有先后接收的顺序,标准广播也不可以被截断。

有序广播是一种同步执行的广播,广播接收器接收广播是有先后顺序的,优先级高的接收器可以先接受广播并且进行截断,之后的广播接收器就无法接受到此条广播,同一时间只有一个广播接收器能够接收这条广播。

接收广播

Android内置了许多系统级别的广播,例如手机开机会发送一条广播,电量变化会发送一条广播,wifi连接也会发送一条广播,同时我们也可以自定义广播,如果我们想要接收到这些广播就需要使用广播接收器

广播接收器可以对自己感兴趣的广播进行注册,注册之后就可以接收该广播,并且在接收器内部的onReceive()方法进行逻辑处理。注册的方式有两种:

  1. 动态注册:在代码中通过registerReceiver()
  2. 静态注册:在Manifest文件中进行注册

动态注册:

  • 新建一个类继承BroadcastReceiver
  • 重写父类的onReceive()方法实现相应逻辑处理
  • 创建一个IntentFliter并添加action,action就是我们需要监听的广播
  • 通过registerReceiver()方法进行注册

静态注册:

  • 新建一个类继承BroadcastReceiver
  • 重写父类的onReceive()方法实现相应逻辑处理
  • 在AndroidManifest.xml通过标签在中进行注册

动态注册接收系统广播–wifi开关

package com.xiezhen.justwrite;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.NetworkInfo;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.util.Log;
import android.view.View;

public class MainActivity extends Activity {

    private TestBroadCastReceiver tb;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        wifiListener();
    }
    private void wifiListener() {
        IntentFilter intentFilter = new IntentFilter();
        //添加的action就是我们要监听的广播,wifi开关的时候,系统都会发送这条广播。
        intentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
        tb = new TestBroadCastReceiver();
        //通过registerReceiver方法,将我们创建的Receiver以及 InterFilter传入,完成注册。
        registerReceiver(tb, intentFilter);
    }
    class TestBroadCastReceiver extends BroadcastReceiver {
        //重写onReceive方法来进行一些接收到广播后的逻辑处理,对用户进行提醒。
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) {
                NetworkInfo networkInfo = intent.getParcelableExtra(
                WifiManager.EXTRA_NETWORK_INFO);
                //判断当前wifi连接状态
                NetworkInfo.State state = networkInfo.getState();
                if (state == NetworkInfo.State.DISCONNECTED) {
                    Log.d("xiezhen", "DISCONNECTED");
                } else if (state == NetworkInfo.State.CONNECTING) {
                    Log.d("xiezhen", "CONNECTING");
                } else if (state == NetworkInfo.State.CONNECTED) {
                    Log.d("xiezhen", "CONNECTED");
                }
            }
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unregisterReceiver(tb);
    }
}

需要注意的是上面这个例子,我们访问了网络连接状态,因此需要在AndroidManifest.xml中添加相应的权限<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />。这样我们就可以在切换wifi开关的时候,在logcat查看输出的信息。

静态注册接收系统广播–开机

package com.xiezhen.justwrite;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;

public class StaticReceiver extends BroadcastReceiver{
    @Override
    public void onReceive(Context context, Intent intent) {
        Log.d("xiezhen","boot complete");
    }
}
 <receiver android:name=".StaticReceiver" >
    <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED" />
    </intent-filter>
 </receiver>

创建一个类继承BroadcastReceiver,之后在AndroidManifest.xml当中进行注册,action就是需要监听的广播,也是系统开机后会发送的一条广播。别忘了添加监听开机广播的权限<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />在模拟器上测试的话,我们只需要重启模拟器就可以在logcat当中看到boot complete这条信息。

动态注册与静态注册对比

  1. 动态注册的广播要在onDestroy方法中销毁
  2. 可以自由注册,销毁广播,但是程序未启动时无法监听广播
  3. 静态注册的广播在程序安装后就自动启动,监听广播

发送自定义广播

之前我们都是接收系统发送的广播,现在我们将发送自己定义的广播,之前我们说了广播有两种:标准广播和有序广播。

发送标准广播:

  1. 定义一个接收器
  2. 进行注册(动态静态都可)
  3. 通过sendBroadcast方法发送广播
package com.xiezhen.justwrite;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;

public class CustomBroadCastReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Log.d("xiezhen", "my custom receiver");
    }
}
 <receiver android:name=".CustomBroadCastReceiver" >
   <intent-filter>
       <action android:name="my.custom.receiver" />
    </intent-filter>
  /receiver>
  @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button button = (Button) findViewById(R.id.button);
        button.setOnClickListener(new OnClickListener(){
            @Override
            public void onClick(View view){
            //构建一个Intent,然后将要发送的广播传入
                Intent intent=new Intent("my.custom.receiver");
                //调用sendBroadcast方法发送标准广播
                sendBroadcast(intent);
            }
        };
    }

这样所有监听my.custom.receiver这条广播的接收器都可以接收到这条广播。

发送有序广播:

我们通过sendBroadcast发送标准广播。
只需要将sendBroadcast方法换成sendOrderBroadcast方法即可发送有序广播,此方法接收两个参数,第一个参数就是构建广播的Intent,第二个参数是一个与权限有关的字符串,传入null即可。

package com.xiezhen.justwrite;


import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;

public class FirstOrderReceiver extends BroadcastReceiver{
    @Override
    public void onReceive(Context context, Intent intent) {
        Log.d("xiezhen","first");
        //abortBroadcast();
    }
}
<receiver android:name=".FirstOrderReceiver" >
    <intent-filter>
        <action android:name="order.broadcast" />
    </intent-filter>
</receiver>
  @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button button = (Button) findViewById(R.id.button);
        button.setOnClickListener(new OnClickListener(){
            @Override
            public void onClick(View view){
            //构建一个Intent,然后将要发送的广播传入
                Intent intent=new Intent("order.broadcast");
                //调用sendBroadcast方法发送有序广播
                sendOrderBroadcast(intent,null);
            }
        };
    }

我们重新创建一个项目,然后定义一个广播接收器,同样接收order.broadcast这条广播

package com.xiezhen.justwrite;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;


public class SecondOrderReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {

        Log.d("xiezhen","second");
    }
}
<receiver android:name=".SecondOrderReceiver" >
    <intent-filter>
        <action android:name="order.broadcast" />
    </intent-filter>
</receiver>

FirstOrderReceiver和SecondOrderReceiver两个广播接收器都可以接收到我们发送的自定义有序广播,既然是有序广播那么就存在接收的先后顺序,我们可以在注册FirstOrderReceiver的时候,通过android:priority属性设置优先级:

<receiver android:name=".FirstOrderReceiver" >
    <intent-filter android:priority="100">
        <action android:name="order.broadcast" />
    </intent-filter>
</receiver>

同时在FirstOrderReceiver的onReceive方法中,调用abortBroadcast()方法对这条广播进行截断,这样就只有FirstOrderReceiver可以接受到这条广播,之后的SecondOrderReceiver无法接受到这条广播。

使用本地广播

广播是跨进程的,我们可以发送的广播可以被其他应用程序接收,我们也可以接受其他应用程序发送的广播,这些广播有可能是垃圾广播,这种跨进程的全局性质的广播会带来一系列的安全问题,Android系统提供了一套本地广播机制用来解决可能的安全性问题:用LocalBroadcastManager对广播进行管理,提供注册以及发送广播的方法,使用LocalBroadcastManager发送的广播只能在本应用程序内传递,同样的广播接收器也只能接收本应用程序内的广播。

package com.xiezhen.justwrite;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.NetworkInfo;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;
import android.view.View;

public class MainActivity extends Activity {

    private LocalBroadCastReceiver lb;
    private LocalBroadcastManager localBroadcastManager;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        localBroadcastManager = LocalBroadcastManager.getInstance(this);
        lb = new LocalBroadCastReceiver();
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction("local.broadcast");
        localBroadcastManager.registerReceiver(lb, intentFilter);
    }

    public void btnSend(View view) {

        Intent intent = new Intent("local.broadcast");
        localBroadcastManager.sendBroadcast(intent);
    }

    class LocalBroadCastReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            Log.d("xiezhen","local broadcast");
        }
    }


    @Override
    protected void onDestroy() {
        super.onDestroy();
        localBroadcastManager.unregisterReceiver(lb);
    }
}

布局文件:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">
    <Button
        android:onClick="btnSend"
        android:layout_centerInParent="true"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="send local broadcast" />
</RelativeLayout>

基本上和我们发送全局广播的方式一样,不同的是,通过LocalBroadcastManager.getInstance(this)获取一个实例,然后调用LocalBroadcastManagerregisterReceiver()方法济宁注册,调用LocalBroadcastManagersendBroadcast()方法来发送广播而已。同样别忘了在onDestroy()方法中通过 localBroadcastManager.unregisterReceiver()方法注销广播,

附加

  1. 本地广播只能通过动态注册的方式进行注册
  2. 使用本地广播不用担心泄露数据
  3. 不用担心接收到垃圾信息,不用担心安全隐患
  4. 比全局广播更加高效

几点注意事项

  1. onReceive()方法中不能进行耗时操作,也不能开线程进行异步操作,因为广播接收器仅当onReceive这个回调方法执行的时候被认为是活跃的,onReceive方法执行完毕后,接收器是不活跃的,这个时候广播接收器所在的进程很有可能会被杀死,如果进行异步操作的话,有可能进程已经被杀死,异步操作还没有结束。可以选择在onReceive方法中开启一个服务去进行耗时的操作。
  2. 在onReceive中弹出一个Dialog可能会报错Unable to add window android.view.ViewRootImpl$W@535ab190 -- permission denied for this window type,你可以将 AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(context)中的context,改成xxx.this;或者将Dialog设置为系统对话框dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);并且在AndroidManifest.xml中设置权限<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
  3. 在BroadcastReceiver的onReceive(Context context, Intent intent)方法当中通过context.startActivity来启动一个Activity的时候,需要设置启动模式为FLAG_ACTIVITY_NEW_TASK,否则会报错,因为不设置这个NEW_TASK启动模式的话,就没有栈来存放启动的Activity。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值