Android-BroadcastReceiver

1,573 阅读6分钟

今天认识一个新的Android四大组件-广播。顾名思义,广播起到通信的作用,目的在于通知众多有接收能力的app,例如Android系统级别广播锁屏、解锁、低电量等等,在产生这些条件的时候,系统会发送一条广播,给有需要的app使用。有时也用于Android的进程间通信

广播的接收者与发送者属于观察者模式的中的消息发布和订阅的两端,AMS属于中间的处理中心,广播发送者和广播的接收者的执行是异步的,发送者将消息给了AMS之后,就不管接收者有没有接收到了

Android广播的注册方式

在Android中,注册广播有两种方式,第一种为静态注册,第二种为动态注册

动态注册

动态注册广播即在代码中注册,而不是在清单文件中去声明<receiver />,这种注册方式的优点是灵活,而且占用的空间较少,只能在程序运行的时候接收到广播,在程序退出后,会将广播及时的解绑,防止内存泄露,因此,在程序退出后,是无法接收到广播的。

下面来看一下如何动态的注册广播,通过代码怎么实现

定义一个类,继承BroadcastReceiver

public class LockScreenBroadcastReceiver  extends BroadcastReceiver {

    private final static String TAG = LockScreenBroadcastReceiver.class.getSimpleName();

    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();

        if (Intent.ACTION_SCREEN_OFF.equals(action)) {
            Log.e(TAG, "锁屏了");
        }

        if(Intent.ACTION_SCREEN_ON.equals(action)) {
            Log.e(TAG, "解锁了");
        }
    }
}

接受两种类型的广播,即锁屏广播、解锁广播,广播的事件可以通过形参的intent.getAction()获取到

MainActivity中绑定广播

public class MainActivity extends AppCompatActivity {

    private LockScreenBroadcastReceiver receiver;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
		
        IntentFilter intentFilter = new IntentFilter();
		
        // 添加广播事件(注意,想要接收哪种类型的广播必须在此处添加,否则接收不到)
        intentFilter.addAction(Intent.ACTION_SCREEN_ON);
        intentFilter.addAction(Intent.ACTION_SCREEN_OFF);

        receiver = new LockScreenBroadcastReceiver();
        registerReceiver(receiver, intentFilter);
    }

    @Override
    protected void onDestroy() {
        // 在Activity销毁的时候解绑广播,防止未解绑造成的内存泄露
        unregisterReceiver(receiver);
        super.onDestroy();
    }
}

上述方式就是动态绑定广播,不需要在清单文件中加入任何的配置,只需在Java代码中声明即可

当解锁或者锁屏时,即可接收到广播事件

E/LockScreenBroadcastReceiver: 锁屏了
E/LockScreenBroadcastReceiver: 解锁了

静态广播

静态广播需要将自实现的BroadcastReceiver定义在清单文件中

<receiver android:name=".LockScreenBroadcastReceiver">
    <intent-filter>
        <action android:name="android.intent.action.SCREEN_OFF"/>
        <action android:name="android.intent.action.SCREEN_ON"/>
    </intent-filter>
</receiver>

同样需要在intent-filter中指定要接收的广播类型

一些频繁产生的广播,例如解锁、锁屏、低电量提示等,采用静态注册广播无效,只能使用动态注册广播,在Android 8开始,Google就对静态广播下手了,各大三方应用利用广播进行保活,例如开机发出的广播,三方应用利用这个广播自启动服务,偷偷的在后台跑。而封禁静态广播采用动态广播就可以保证只有应用在前台时才能接收广播,然后进行相应的处理

发送广播

我们自己的app也可以发送广播,主要就是分为有序广播和无序广播,有序广播通过定义广播的优先级,从而一层一层的接收广播,无序广播就比较好理解了,一旦有广播产生,所有人同时都能收到

无序广播

接下来我们自己定义一个广播,用来实现无序广播,第一个例子采用自监听广播,即app发送广播,然后自己接收

首先发送广播

public class MainActivity extends AppCompatActivity {

    private final static String TAG = MainActivity.class.getSimpleName();

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

        Button sendBtn = findViewById(R.id.send_btn);
        sendBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.e(TAG, "按钮被点击了,已经产生了广播事件");
                final String action = "com.sui.CUSTOMER_ACTION";
                Intent intent = new Intent(action);

                if(Build.VERSION.SDK_INT > Build.VERSION_CODES.O) {
                    intent.setComponent(new ComponentName("com.sui.sendstaticbroadcast", 									"com.sui.sendstaticbroadcast.CustomerBroadcastReceiver"));
                }
				
                // 通过sendBroadcast发送无序广播
                sendBroadcast(intent);
            }
        });
    }
}

在Intent中指定发送广播的action,即该广播的标识,只有指定这个标识的广播接收者才能接收到这个广播

在Android 8以后,即API 26以后,发送广播需要指定广播接收者应用的包名,以及接收者的全限定类名,通过ComponentName传入。

ComponentName第一个参数为接收者的应用包名,第二个参数为接收者的全限定类名

然后创建一个广播接收者,并且在清单文件中注册

public class CustomerBroadcastReceiver extends BroadcastReceiver {

    private static final String TAG = CustomerBroadcastReceiver.class.getSimpleName();

    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();

        if("com.sui.CUSTOMER_ACTION".equals(action)) {
            Log.e(TAG, "接收到了自定义的广播事件");
        }
    }
}
<receiver android:enabled="true" android:name=".CustomerBroadcastReceiver">
    <intent-filter>
        <action android:name="com.sui.CUSTOMER_ACTION"/>
    </intent-filter>
</receiver>

启动app,点击产生广播的按钮,即可看到自定义广播发送以及监听打印出来的Log

E/MainActivity: 按钮被点击了,已经产生了广播事件
E/CustomerBroadcastReceiver: 接收到了自定义的广播事件

如果想要在其他app中接收到自定义广播,需要在产生广播的时候通过ComponentName指定接收者app的包名以及接收者的全限定类名,否则接收不到。

有序广播

有序广播可以分层级的接收广播信息,这个层级的由来就是通过指定广播接收者的优先级,这个优先级理论上官方说是[-1000, 1000],但是实际上在整数范围内[-2147483648, 2147483647]即可,数字越大,优先级越高,越先收到广播

创建三个广播接收者

public class MyBroadcastReceiver {

    private final static String TAG = MyBroadcastReceiver.class.getSimpleName();

    public static class HighBroadcastReceiver extends BroadcastReceiver {

        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if ("com.sui.CUSTOMER_ACTION".equals(action)) {
                boolean orderedBroadcast = isOrderedBroadcast();
            			Log.e(TAG, "是否是有序广播:" + orderedBroadcast);
                Log.e(TAG, "我是HighBroadcastReceiver");
                int code = 0;
                String data = "Hello";
                Bundle bundle = null;

                setResult(code, data, bundle);
            }
        }
    }

    public static class MidBroadcastReceiver extends BroadcastReceiver {

        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if ("com.sui.CUSTOMER_ACTION".equals(action)) {
                Log.e(TAG, "我是MidBroadcastReceiver");

                int resultCode = getResultCode();
                String resultData = getResultData();

                Log.e(TAG, "result code: " + resultCode + " resultData: " + resultData);

                resultCode += 1;
                resultData += "World";

                setResult(resultCode, resultData, null);
            }
        }
    }

    public static class LowBroadcastReceiver extends BroadcastReceiver {

        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if ("com.sui.CUSTOMER_ACTION".equals(action)) {
                Log.e(TAG, "我是LowBroadcastReceiver");

                int resultCode = getResultCode();
                String resultData = getResultData();

                Log.e(TAG, "result code: " + resultCode + " resultData: " + resultData);
            }
        }
    }
}

在MainActivity中动态注册广播接收者,并且为这三个广播接收者设置不同的优先级

public class MainActivity extends AppCompatActivity {

    private static final String TAG = MainActivity.class.getSimpleName();

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

        init();

        Button sendBtn = findViewById(R.id.send_btn);
        sendBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.e(TAG, "产生了广播信息");
                Intent intent = new Intent("com.sui.CUSTOMER_ACTION");

                intent.putExtra("count", 100);
				
                // 发送有序广播采用的方法
                sendOrderedBroadcast(intent, null);
            }
        });
    }

    public void init() {
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction("com.sui.CUSTOMER_ACTION");
        
        intentFilter.setPriority(3000);
        registerReceiver(new MyBroadcastReceiver.HighBroadcastReceiver(), intentFilter);
        intentFilter.setPriority(2000);
        registerReceiver(new MyBroadcastReceiver.MidBroadcastReceiver(), intentFilter);
        intentFilter.setPriority(1000);
        registerReceiver(new MyBroadcastReceiver.LowBroadcastReceiver(), intentFilter);
    }
}

如果不指定优先级,则广播的接收顺序则会按照注册的顺序

在广播接收者中,可以通过isOrderedBroadcast()方法来获取是不是有序广播,并且可以在有序广播传播的时候,通过abortBroadcast()终止广播,即广播不向下传播,例如下面这样

public static class HighBroadcastReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        boolean orderedBroadcast = isOrderedBroadcast();
        Log.e(TAG, "是否是有序广播:" + orderedBroadcast);
        String action = intent.getAction();
        if ("com.sui.CUSTOMER_ACTION".equals(action)) {
            Log.e(TAG, "我是HighBroadcastReceiver");
            int code = 0;
            String data = "Hello";
            Bundle bundle = null;

            setResult(code, data, bundle);
			
            // 终止广播
            abortBroadcast();
        }
    }
}

只有有序广播才能使用setResult()getResult()方法,在无序广播中使用将报错

当系统或其他程序发出广播时,Android系统会检查每一个已经安装的app是否配置了此广播的action,如果配置了此广播的action,则会实例化此BroadcastReceiver,然后执行receiver()方法,BroadcastReceiver是在匹配到action之后才实例化的!!!

广播的生命周期非常短,并且不要在receiver中执行耗时的操作,一旦10s还没有返回结果(资料上说是10s,但是我试了15s也没有ANR),则会抛出ANR异常