Android 8.0之后 取消大部分静态注册广播(动态注册可以,静态注册接收不到广播)

Andorid 8.0 对广播的使用做了变更。

当广播接收器使用静态注册方式使用时,除了一些例外,这个接收器接收不到隐式广播。 注意这个“隐式”是重点。

看了网上几篇文章,对这个变更理解有误。错误的理解是:8.0后,广播接收器使用静态注册,是无法使用的。
实时并非如此。

先看一个例子:

首先,定义一个简单的广播接收器:

public class MyReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        // TODO: This method is called when the BroadcastReceiver is receiving
        // an Intent broadcast.
        Toast.makeText(context,"收到广播了~",Toast.LENGTH_LONG).show();
        //throw new UnsupportedOperationException("Not yet implemented");
    }
}

它对接收到广播的行为就是打印一句话。

第二,我们将他注册到Manifest文件中。

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

最后,在Activity中发送一个广播,intent通过设置Action为com.demo.recriver的形式发送隐式广播。

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button button=(Button)findViewById(R.id.button) ;
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
//                MyReceiver myReceiver=new MyReceiver();
//                IntentFilter intentFilter=new IntentFilter();
//                intentFilter.addAction("test.example.com");
//                registerReceiver(myReceiver,intentFilter);

                Intent intent=new Intent("test.example.com");
                //intent.setComponent(new ComponentName(MainActivity.this,MyReceiver.class));
                sendBroadcast(intent);
            }
        });

    }
}

运行这个demo,发现在8.0以下的手机上,会有Toast显示,8.0以上的手机不会弹出,说明没有接收到广播。

原因在于这个广播 是“隐式” 发送的,8.0中,静态注册的广播接收者无法接受 隐式 广播。

为了解决这个问题,有两个方法:

1 在Activity或其他组件中动态注册广播

2 发送显示广播

对于1 ,如果想广播让接收者工作,必须要在某个Activity或者其他组件中调用registerReceiver()进行注册,在onDestroy()时还要反注册,代码稍显复杂,而且静态注册的广播接收者仍处于不可用的状态。不合理。

而后一种方法 ,因为sendBroaccast是自己主动发送的,明显知道要哪个broadcastReceiver来进行处理,直接发送显示广播即可。

具体的代码如下,将MainActivity做修改:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button button=(Button)findViewById(R.id.button) ;
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
//                MyReceiver myReceiver=new MyReceiver();
//                IntentFilter intentFilter=new IntentFilter();
//                intentFilter.addAction("test.example.com");
//                registerReceiver(myReceiver,intentFilter);

                Intent intent=new Intent();
                intent.setComponent(new ComponentName(MainActivity.this,MyReceiver.class));
                sendBroadcast(intent);
            }
        });

    }
}

实际上,这种写法与更常见的以下写法相同:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button button=(Button)findViewById(R.id.button) ;
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent=new Intent(MainActivity.this,MyReceiver.class);
                sendBroadcast(intent);
            }
        });

    }
}

运行,发现可以弹出Toast,静态注册的BroadcastReceiver接收到了广播。证明静态注册是可以接收到广播的。

顺便插一句:

Intent指定action, 这个Intent则为隐式Intent,使用它发送的广播则为隐式广播。隐式广播接收者是通过IntentFilter去查找的。
Intent指定了组件名称,这个Intent为显式Intent,用他发送的广播为显式广播。广播接收者直接就是指定的组件名称对应的广播接收者。

猜测显式Intent不使用IntentFilter去查找组件(Activtiy,Service,BroadcastReceiver),这点读者有兴趣可以验证是否正确。

再来看一下Intent的setComponent()方法:


图片.png

ntent设置了组件名称(比如 new Intent(MainActivity.this,MyReceiver.class);)则通过IntentFilter匹配所需要的action,data,type,category信息会被忽略。

如果刚才的例子做如下改动:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button button=(Button)findViewById(R.id.button) ;
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent=new Intent("test.example.com");
                intent.setComponent(new ComponentName(MainActivity.this,MyReceiver.class));
                sendBroadcast(intent);
            }
        });

    }
}

同时指定了隐式的action 以及显式的组件名称,action是一个不存在的action。这时仍可以接收到广播。
虽然通过action不可能匹配到一个广播接收者,但显示设置了组件,action就被忽略了。

扯远了。回到最初的那个话题。8.0以后,静态注册的广播接受者是可以接收到广播的,只要广播是通过显示方式发送的。


Android 8.0新特性-取消大部分静态注册广播
解决方法:

(1)使用动态广播代替静态广播。

(2)保留原来的静态广播,但是加入组件参数。

(3)发送广播的时候携带intent.addFlags(0x01000000); 即能让广播突破隐式广播限制。


8.0还可以接受的隐式广播:
隐式广播例外
作为Android 8.0(API级别26)后台执行限制的一部分,针对API级别26或更高级别的应用程序无法再在其清单中为隐式广播注册广播接收器。但是,目前有几个广播免于这些限制。无论应用程序所针对的API级别如何,应用程序都可以继续为以下广播注册监听器。

注意:即使这些隐式广播仍然在后台工作,您应该避免为它们注册侦听器。

ACTION_LOCKED_BOOT_COMPLETED, ACTION_BOOT_COMPLETED

免除,因为这些广播仅在首次启动时发送一次,并且许多应用需要接收此广播以安排作业,警报等。

ACTION_USER_INITIALIZE,“android.intent.action.USER_ADDED”,“android.intent.action.USER_REMOVED”

这些广播受特权权限保护,因此大多数普通应用程序无论如何都无法接收它们。

“android.intent.action.TIME_SET”,ACTION_TIMEZONE_CHANGED,ACTION_NEXT_ALARM_CLOCK_CHANGED

当时间,时区或警报发生变化时,时钟应用可能需要接收这些广播以更新警报。

ACTION_LOCALE_CHANGED

仅在区域设置更改时发送,这不常见。应用可能需要在区域设置更改时更新其数据。

ACTION_USB_ACCESSORY_ATTACHED,ACTION_USB_ACCESSORY_DETACHED,ACTION_USB_DEVICE_ATTACHED,ACTION_USB_DEVICE_DETACHED

如果应用程序需要了解这些与USB相关的事件,目前还没有一个很好的替代方案来注册广播。

ACTION_CONNECTION_STATE_CHANGED,ACTION_CONNECTION_STATE_CHANGED,ACTION_ACL_CONNECTED,ACTION_ACL_DISCONNECTED

如果应用接收这些蓝牙事件的广播,则用户体验不太可能受到影响。

ACTION_CARRIER_CONFIG_CHANGED,TelephonyIntents.ACTION_*_SUBSCRIPTION_CHANGED,“TelephonyIntents.SECRET_CODE_ACTION”,ACTION_PHONE_STATE_CHANGED,ACTION_PHONE_ACCOUNT_REGISTERED,ACTION_PHONE_ACCOUNT_UNREGISTERED

OEM电话应用可能需要接收这些广播。

LOGIN_ACCOUNTS_CHANGED_ACTION

某些应用需要了解登录帐户的更改,以便他们可以为新帐户和已更改帐户设置计划操作。

ACTION_ACCOUNT_REMOVED

删除帐户后,可以看到帐户的应用会收到此广播。如果这是应用程序需要执行的唯一帐户更改,则强烈建议应用程序使用此广播 而不是已弃用LOGIN_ACCOUNTS_CHANGED_ACTION。

ACTION_PACKAGE_DATA_CLEARED

仅在用户明确清除“设置”中的数据时发送,因此广播接收器不太可能显着影响用户体验。

ACTION_PACKAGE_FULLY_REMOVED

某些应用可能需要在删除其他包时更新其存储的数据; 对于这些应用程序,注册此广播没有其他好的选择。

注意:其他与包相关的广播(例如ACTION_PACKAGE_REPLACED)不受新限制的豁免这些广播很常见,对豁免它们有潜在的性能影响。

ACTION_NEW_OUTGOING_CALL

响应用户拨打电话而采取措施的应用需要接收此广播。

ACTION_DEVICE_OWNER_CHANGED

这种广播不经常发送; 一些应用需要接收它,以便他们知道设备的安全状态已经改变。

ACTION_EVENT_REMINDER

由日历提供商发送,以向日历应用发布活动提醒。由于日历提供程序不知道日历应用程序是什么,因此该广播必须是隐含的。

ACTION_MEDIA_MOUNTED,ACTION_MEDIA_CHECKING,ACTION_MEDIA_UNMOUNTED,ACTION_MEDIA_EJECT,ACTION_MEDIA_UNMOUNTABLE,ACTION_MEDIA_REMOVED,ACTION_MEDIA_BAD_REMOVAL

这些广播是由于用户与设备的物理交互(安装或删除存储卷)或作为启动初始化的一部分(因为可用卷已安装)而发送的,因此它们不常见,通常由用户控制。

SMS_RECEIVED_ACTION, WAP_PUSH_RECEIVED_ACTION

短信收件人应用程序依赖这些广播

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容