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)杀死。

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

关于静态注册BroadcastReceiver接收不到广播的问题的更多相关文章

  1. 关于Android8.0 静态注册广播 行为变更的说明。

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

  2. BroadcastReceiver(广播)的静态注册和动态注册 --Android开发

    BroadcastReceiver是安卓四大组件之一,本例通过代码的方式演示静态注册和动态注册. 1.静态注册 静态注册只需要AndroidManifest.xml中进行配置: AndroidMani ...

  3. Android静态注册广播无法接收的问题(8.0+版本)

    如果你静态注册的广播无法接收到消息,请先检查下:你的安卓版本是不是8.0+ * 前言** Google官方声明:Beginning with Android 8.0 (API level 26), t ...

  4. Android(java)学习笔记173:BroadcastReceiver之 静态注册 和 动态注册

    1. 广播接受者>什么是广播.收音机.电台:对外发送信号.收音机:接收电台的信号. >在android系统里面,系统有很多重要的事件: 电池电量低,插入充电器,sd卡被移除,有电话打出去, ...

  5. BroadcastReceiver的两种注册方式之------静态注册

    activity_main.xml <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android&qu ...

  6. Android BroadcastReceiver 接收收到短信的广播

    一.知识介绍 1.broadcastReceiver是广播接受者,四大组件之一. 2.Android中内置了很多系统级别的广播,可以在应用程序中得到各种系统的状态信息. 3.使用场景: ①当手机没有电 ...

  7. BroadcastReceiver 接收系统短信广播

    BroadcastReceiver 接收系统短信广播 /* 注册广播:    * |--静态注册  (在AndroidManifest .xml 中注册)  * <receiver androi ...

  8. Android 8.0新特性-取消大部分静态注册广播

    今天楼主在写一个广播的demo,功能非常的简单,就是一个应用发送一个自定义的广播,同时在这个应用里面定义了一个广播接受者,并且在AndroidManifest文件中进行静态的注册.Demo看上去非常的 ...

  9. Android(java)学习笔记116:BroadcastReceiver之 静态注册 和 动态注册

    1. 广播接受者>什么是广播.收音机.电台:对外发送信号.收音机:接收电台的信号. >在android系统里面,系统有很多重要的事件: 电池电量低,插入充电器,sd卡被移除,有电话打出去, ...

随机推荐

  1. IT连创业系列:近期功能调整(小魔术功能从二级目录调整到一级栏目)

    最近花了点时间,折腾了一下.NET Core,因此有几篇 Taurus.MVC + CYQ.Data 的文章出来. 这两天也顺带把 ASP.NET Aries 升级了一下功能, 也计划支持.NET C ...

  2. python 模块与包

    一.模块 1.1 什么是模块 # 什么是模块 # 一组功能的集合,就是模块,在python中一个py文件就一个模块 1.2 为什么要使用模块 # 1.可使代码的结构功能分区更清晰 # 2.可重复使用模 ...

  3. 在基于Windows系统的PHP虚拟主机上实现域名的301永久重定向

    作者:荒原之梦 原文链接:http://zhaokaifeng.com/?p=581 操作背景: 当网站在更换或添加域名.进行网址规范化或删除旧页面时,出于对用户使用体验和搜索引擎优化方面的考虑就需要 ...

  4. sklearn了解一下

    sklearn是机器学习中一个常用的python第三方模块,网址:http://scikit-learn.org/stable/index.html ,里面对一些常用的机器学习方法进行了封装,在进行机 ...

  5. 阿里云和腾讯云免费SSL证书 专题

    阿里云部署SSL证书 http://www.cnblogs.com/sslwork/p/5984167.html 查找中间证书 为了确保兼容到所有浏览器,我们必须在阿里云上部署中间证书,如果不部署证书 ...

  6. c# Socket通信异步TCP

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.N ...

  7. 你不知道的JavaScript--Item16 for 循环和for...in 循环的那点事儿

    大家都知道在JavaScript中提供了两种方式迭代对象: for 循环: for..in循环: 1.for循环 不足: 在于每次循环的时候数组的长度都要去获取: 终止条件要明确: 在for循环中,你 ...

  8. memcache 和 redis 之间的区别

    结论 应该说Memcached和Redis都能很好的满足解决我们的问题,它们性能都很高,总的来说,可以把Redis理解为是对Memcached的拓展,是更加重量级的实现,提供了更多更强大的功能.具体来 ...

  9. Elasticsearch笔记九之优化

    Elasticsearch笔记九之优化 ).get(); } curl命令可以在linux中建立一个定时任务每天执行一次,同样java代码也可以建立一个定时器来执行. 2:内存设置之前介绍过es集群有 ...

  10. bzoj4067 [Ctsc2015]gender

    好神的一道题啊! 我们发现题目中的ln的贡献非常傻逼,但是我们可以发现这个东西的取值只有40个左右,于是我们可以枚举他! 枚举完了对于题里的贡献就是一个普通的最小割,采用的是文理分科的思想,与S连代表 ...