当前位置 博文首页 > 庆述倾述:广播接收器BroadCastReceiver

    庆述倾述:广播接收器BroadCastReceiver

    作者:[db:作者] 时间:2021-08-05 12:48

    每个应用都可以注册广播接收器(BroadCastReceiver)来接受系统和其他应用发来的广播,也可以发送广播给其他应用。

    广播大致有四种类型:

    • 普通广播:即一种完全异步的广播,发出之后,所有广播接收器都可以收到。优点效率高,所有广播接收器都可以收到,但是不安全,因为广播接收器不能拦截。
    • 有序广播:是一种同步执行的广播,在广播发出后,同一时刻只有一个广播接收器可以收到广播,并且该广播接收器可以对该广播进行拦截。如果不拦截,则可以继续传给下一个广播接收器。且谁先收到广播也是有先后顺序的,通常根据AndroidManifest.xml配置文件中注册的广播接收器的intent-filterandroid:priority属性来设置优先级。取值范围-1000~1000,数值越大级别越高。且静态注册优于动态注册,先注册的优于后注册的广播。
    • 本地广播:有序广播和普通广播都是全局广播,而本地广播是在一个应用程序内进行传播,所定义的广播接收器也只能接收来自本应用发来的广播,因此安全。
    • 系统广播:在Android系统中使用了很多广播,比如低电量、开机启动、锁屏、SD卡挂载、打电话等。

    BroadCastReceiver的注册方式有两种,静态和动态。

    1. 动态注册

    public class TestActivity extends AppCompatActivity {
    
        private String TAG = "TestActivity";
        private MyBroadCastReceiver myBroadCastReceiver;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_test);
    
            IntentFilter intentFilter = new IntentFilter();
            intentFilter.addAction("android.intent.action.TIME_TICK");
            myBroadCastReceiver = new MyBroadCastReceiver();
            registerReceiver(myBroadCastReceiver, intentFilter);
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            unregisterReceiver(myBroadCastReceiver);
        }
    
        static class MyBroadCastReceiver extends BroadcastReceiver {
            @Override
            public void onReceive(Context context, Intent intent) {
                Toast.makeText(context, "Time Changed", Toast.LENGTH_LONG).show();
            }
        }
    }
    

    通过使用IntentFilter对象来添加相应的action,从而让广播接收器知道自己监听的是什么广播。
    对应的系统支持的广播行为在Android SDK/platforms/version/data/broadcast_actions.txt文件中。比如:
    在这里插入图片描述

    2. 静态注册

    动态广播存在一个确定就是,需要程序启动才可以接收广播,因为注册是在onCreate中完成的。理论上使用静态广播可以让程序在未启动的情况下也可以接收到广播,但是这样大家都使用静态广播,那么就会导致任何应用都可以频繁的从后台被唤醒,严重影响了手机电量和性能。

    因此每个版本的系统都在削弱静态广播注册功能。

    Android8.0之后,所有隐式广播都不允许使用静态注册的方式来接收了。大多数系统广播都属于隐式广播。还有少数特殊的广播仍然允许使用静态注册的方式来接收。见:隐式广播例外情况

    使用快捷方式创建一个静态广播很容易。

    它会自动在配置文件中注册:

    <receiver
        android:name=".receiver.MyReceiver"
        android:enabled="true"
        android:exported="true">
    </receiver>
    

    我们为之添加系统启动广播过滤器:

    // 权限
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
    // 注册
    <receiver
        android:name=".receiver.MyReceiver"
        android:enabled="true"
        android:exported="true">
        <intent-filter>
            <action android:name="android.intent.action.BOOT_COMPLETED"/>
        </intent-filter>
    </receiver>
    

    对应的java文件添加Toast:

    public class MyReceiver extends BroadcastReceiver {
    
        @Override
        public void onReceive(Context context, Intent intent) {
            // TODO: This method is called when the BroadcastReceiver is receiving
            Toast.makeText(context, "Boot Complete", Toast.LENGTH_LONG).show();
        }
    }
    

    在启动类中加入动态权限申请:

    public class TestActivity extends AppCompatActivity {
    
        private String TAG = "TestActivity";
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_test);
    
            //权限检查
            if (ContextCompat.checkSelfPermission(TestActivity.this,
                    Manifest.permission.RECEIVE_BOOT_COMPLETED) != PackageManager.PERMISSION_GRANTED) {
                //申请权限
                ActivityCompat.requestPermissions(TestActivity.this,
                        new String[]{Manifest.permission.RECEIVE_BOOT_COMPLETED}, 1);
            }
        }
    }
    

    3. 自定义广播

    实现接收自定义广播,我们只需要在action中指定一个自定义的字符串即可。如:

    <receiver
        android:name=".receiver.MyReceiver"
        android:enabled="true"
        android:exported="true">
        <intent-filter>
            <action android:name="com.weizu.broadcast.test"/>
        </intent-filter>
    </receiver>
    

    然后:

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);
    
        Button button = findViewById(R.id.id_button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.d("TAG", "onClick: ");
                Intent intent = new Intent();
                intent.setAction("com.weizu.broadcast.test");
                // 指定发给哪个程序,让隐式广播变为显式广播,才可以被接收到
                intent.setPackage(getPackageName());
                sendBroadcast(intent);
            }
        });
    }
    

    注意,这里必须调用:intent.setPackage(getPackageName());方法。因为默认自定义广播是隐式广播,我们需要使用setPackage()方法来指定这个广播是发送给哪个应用程序,从而让它变成显式广播。否则,静态注册的BroadCastReceiver无法收到这个广播

    4. 发送有序广播

    有序广播可以被拦截,同步执行,有优先级顺序。

    只需要使用:

    sendOrderedBroadcast(intent, null);
    

    可以在intent-filter中设置优先级:

    <receiver
        android:name=".receiver.MyReceiver"
        android:enabled="true"
        android:exported="true">
        <intent-filter android:priority="100">
            <action android:name="com.weizu.broadcast.test"/>
        </intent-filter>
    </receiver>
    

    如果需要某个广播接收器拦截这个广播,可以在onReceive方法中拦截:

    public class MyReceiver extends BroadcastReceiver {
    
        @Override
        public void onReceive(Context context, Intent intent) {
            Toast.makeText(context, "Boot Complete", Toast.LENGTH_LONG).show();
            abortBroadcast(); // 拦截
        }
    }
    

    5. 发送本地广播

    主要使用LocalBroadcastManager来对广播进行管理,需要注意的是,这里的广播接收器的注册不能使用静态注册,而需要使用LocalBroadcastManager对象的registerReceiver(myReceiver, intentFilter);方法进行注册。且注册这里为动态注册。

    public class TestActivity extends AppCompatActivity {
    
        LocalBroadcastManager localBroadCastManager;
        MyReceiver myReceiver;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_test);
    
            localBroadCastManager = LocalBroadcastManager.getInstance(TestActivity.this);
            Button button = findViewById(R.id.id_button);
            button.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Intent intent = new Intent();
                    intent.setAction("com.weizu.broadcast.test");
                    localBroadCastManager.sendBroadcast(intent);
                }
            });
    
            IntentFilter intentFilter