欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

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

程序员文章站 2022-03-11 23:54:01
1、背景&解决方法 最近碰到一个需求,app监听特定的广播,接收到广播后启动自己再进行处理。需求很简单,静态注册就好,不过,在自测的时候遇到一个问题,app安装后没启动过的状态下,什么广播都收不到!なにもない! 后来,网上各种查,找到了“罪魁祸首”:Android 3.1以后新增的stopped机制 ......

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

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