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

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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,744评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,505评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,105评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,242评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,269评论 6 389
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,215评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,096评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,939评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,354评论 1 311
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,573评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,745评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,448评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,048评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,683评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,838评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,776评论 2 369
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,652评论 2 354

推荐阅读更多精彩内容