文档章节

关于静态注册BroadcastReceiver接收不到广播的问题

o
 osc_a22drz29
发布于 2019/03/26 17:18
字数 984
阅读 8
收藏 0

精选30+云产品,助力企业轻松上云!>>>

1、背景&解决方法

最近碰到一个需求,app监听特定的广播,接收到广播后启动自己再进行处理。需求很简单,静态注册就好,不过,在自测的时候遇到一个问题,app安装后没启动过的状态下,什么广播都收不到!なにもない!

后来,网上各种查,找到了“罪魁祸首”:Android 3.1以后新增的stopped机制。

解决方法是,发送广播时添加flag:FLAG_INCLUDE_STOPPED_PACKAGES

是的,没错,这个解决方法对系统广播无效,如果要处理的是系统广播,本文对你无效。

2、stopped机制是什么?

我们都知道流氓软件非常影响用户体验,而且经常在用户不知不觉的情况下就被安装到手机中,如果这个软件再监听了一些通用的系统广播,就可以消无声息地在你手机后台干各种事情。

为了避免这种情况,android 3.1以后加入了stopped机制。系统会在遍历所有app后,讲过app信息记录到一个xml文件中,路径为:

data/system/users/0/package-restrictions.xml

这个路径有部分系统会不同,有兴趣可以看看系统源码:frameworks\base\services\core\java\com\android\server\pm\Setting.java

每个app的信息会被记录到一个pkg标签中,而这个标签有个属性stopped,顾名思义,当这个属性被置为true时,这个app就是停止状态。例:

<pkg name="com.example.test" stopped="true" />

让我们再看到Intent的两个Flag:

/**
 * If set, this intent will not match any components in packages that
 * are currently stopped.  If this is not set, then the default behavior
 * is to include such applications in the result.
 */
public static final int FLAG_EXCLUDE_STOPPED_PACKAGES = 0x00000010;
/**
 * If set, this intent will always match any components in packages that
 * are currently stopped.  This is the default behavior when
 * {@link #FLAG_EXCLUDE_STOPPED_PACKAGES} is not set.  If both of these
 * flags are set, this one wins (it allows overriding of exclude for
 * places where the framework may automatically set the exclude flag).
 */
public static final int FLAG_INCLUDE_STOPPED_PACKAGES = 0x00000020;

简单地说,带有FLAG_EXCLUDE_STOPPED_PACKAGES的广播不会被发送给stopped状态的app,而两者都有的情况下以FLAG_INCLUDE_STOPPED_PACKAGES为准。这就是stopped机制,能在一定程度上防范流氓软件恶意监听系统广播,而FLAG_INCLUDE_STOPPED_PACKAGES则相当于留给开发者用于自定义广播的后门了。

3、那么,为啥我没加那个啥flag,也收不到?

我们可以看看广播被传到sendBroadcase方法后都被干了什么。

找到ContextImpl的sendBroadcast方法:

public void sendBroadcast(Intent intent) {
    warnIfCallingFromSystemProcess();
    String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
    try {
        intent.prepareToLeaveProcess(this);
        ActivityManager.getService().broadcastIntent(
                mMainThread.getApplicationThread(), intent, resolvedType, null,
                Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, false,
                getUserId());
    } catch (RemoteException e) {
        throw e.rethrowFromSystemServer();
    }
}

我们看到里面调用了一个Service的broadcastIntent方法,这个Service实际为ActivityManagerService(binder原理这里就不写了),我们直接找到ActivityManagerService类的broadcastIntent方法:

public final int broadcastIntent(IApplicationThread caller,
        Intent intent, String resolvedType, IIntentReceiver resultTo,
        int resultCode, String resultData, Bundle resultExtras,
        String[] requiredPermissions, int appOp, Bundle bOptions,
        boolean serialized, boolean sticky, int userId) {
    enforceNotIsolatedCaller("broadcastIntent");
    synchronized(this) {
        intent = verifyBroadcastLocked(intent);
        final ProcessRecord callerApp = getRecordForAppLocked(caller);
        final int callingPid = Binder.getCallingPid();
        final int callingUid = Binder.getCallingUid();
        final long origId = Binder.clearCallingIdentity();
        // 这里再传进去
        int res = broadcastIntentLocked(callerApp,
                callerApp != null ? callerApp.info.packageName : null,
                intent, resolvedType, resultTo, resultCode, resultData, resultExtras,
                requiredPermissions, appOp, bOptions, serialized, sticky,
                callingPid, callingUid, userId);
        Binder.restoreCallingIdentity(origId);
        return res;
    }
}

可以看到intent被传给了broadcastIntentLocked方法,继续进去,这个方法就是对intent进行一系列处理的地方,不难看到有一句:

// By default broadcasts do not go to stopped apps.
intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);

一切真相大白,android默认设置自定义广播也不发送给stopped状态的app。所以需要做到我们上面的需求,必须添加FLAG_INCLUDE_STOPPED_PACKAGES标志位。

4、什么情况下会被置为stopped state?

主要有3种情况:

(1)安装后未被启动过;

(2)被用户强制停止(应用管理-->应用详情-->强制停止);

(3)被调用forceStopPackage(pkg)杀死。

另外,系统应用不受此限制(还没看这里的源码,以后看了再补充,或者有大神指点一下吗?)。

o
粉丝 0
博文 500
码字总数 0
作品 0
私信 提问
加载中
请先登录后再评论。
关于Android8.0 静态注册广播 行为变更的说明。

Andorid 8.0 对广播的使用做了变更。 当广播接收器使用静态注册方式使用时,除了一些例外,这个接收器接收不到隐式广播。 注意这个“隐式”是重点。 看了网上几篇文章,对这个变更理解有误。...

osc_f1tgjw6d
2019/07/24
8
0
静态注册接收不到非系统广播原因探究

学习郭老师的《第一行代码》进行到广播章节,在5.3中发送自定义广播 5.3.1发送标准广播,按照教材内容敲完,点击按钮后始终看不到吐司的提示,但是动态注册的接收器是可以接到到的。 网上搜了...

osc_8vnewt33
2019/05/25
5
0
四大组件---广播机制

BroadcastReceiver 一、概述 广播(Broadcast)机制用于进程/线程间通信,因此在我们应用程序内发出的广播,其他的应用程序应该也是可以收到的。广播分为广播发送和广播接收两个过程,其中广播...

android-key
2016/11/21
29
0
Android插件化原理解析——广播的管理

在Activity生命周期管理 以及 插件加载机制 中我们详细讲述了插件化过程中对于Activity组件的处理方式,为了实现Activity的插件化我们付出了相当多的努力;那么Android系统的其他组件,比如B...

weishu
2016/05/13
0
0
Android广播那些事—开发者必须要知道的面试技巧

1、请描述一下 BroadcastReceiver BroadCastReceiver 是 Android 四大组件之一,主要用于接收系统或者 app 发送的广播事件。 广播分两种: 有序广播 无序广播。 内部通信实现机制: 无序广播...

codeGoogle
2017/11/27
0
0

没有更多内容

加载失败,请刷新页面

加载更多

asp.net core之NLog

NuGet添加 NLog.Web.AspNetCore。 <PackageReference Include="Microsoft.AspNetCore.App" /> 添加配置文件 新建一个文件nlog.config(建议全部小写,linux系统中要注意), 并右键点击其属性......

一介草民Coder
42分钟前
23
0
.NET中的struct和class有什么区别? - What's the difference between struct and class in .NET?

问题: .NET中的struct和class有什么区别? 解决方案: 参考一: https://stackoom.com/question/3OT/NET中的struct和class有什么区别 参考二: https://oldbug.net/q/3OT/What-s-the-differ...

富含淀粉
今天
23
0
android:layout_weight是什么意思? - What does android:layout_weight mean?

问题: I don't understand how to use this attribute. 我不明白如何使用这个属性。 Can anyone tell me more about it? 谁能告诉我更多关于它的事情? 解决方案: 参考一: https://stacko...

javail
今天
17
0
CSS背景不透明度[重复] - CSS Background Opacity [duplicate]

问题: This question already has an answer here: 这个问题已经在这里有了答案: How do I give text or an image a transparent background using CSS? 如何使用CSS为文本或图像提供透明背...

fyin1314
今天
31
0
node http 获取gb2312网页如何转为utf8

最初,我想当然认为是下述做法,但被证明是错误的 const http = require('http'), iconv = require('iconv-lite');const url = 'http://xxx';http.get(url, function(res) { var bo......

高延
今天
24
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部