
问题描述
最近的工作中,涉及到在Android的BroadcastReceiver的使用,这本是Android开发中常见的场景。但是当我用来隐式启动BroadcastReceiver的Intent中通过setData方法携带了一个Uri的时候,BroadcastReceiver却无法被Intent唤醒了。
1 | /** |
经过测试之后,发现如果Intent中同时设置了Action和Uri的时候,Action相当于是失效状态,这个不光是涉及到
BroadcastReceiver,连Activity的表现也是如此。
发现了问题,当然要解决呀,下面是经过复盘后精简出的两个可以验证失效的最小场景:
BroadcastReceiver 的隐式启动
首先,在AndroidManifest中注册我们的BroadcastReceiver及其intent-filter:
1 | <receiver |
然后,在Kotlin中尝试用如下的代码来启动EmptyReceiver:
1 | sendBroadcast(Intent("com.test.ACTION_RECEIVER").apply { |
之后就会发现这个BroadcastReceiver并没有被启动。
Activity 的隐式启动
首先,在AndroidManifest中注册我们的Activity及其intent-filter:
1 | <activity |
然后,在Kotlin中尝试用如下的代码来启动EmptyActivity:
1 | startActivity(Intent("com.test.ACTION_ACTIVITY").apply { |
之后就会发现这个EmptyActivity并没有被启动。
原理探究
既然问题已经暴露,我们就要尽量查出问题产生的根本原因,而不是仅仅找个规避方案了事。经过一番代码跟踪之后,我发现这个问题在BroadcastReceiver和Activity中的表现是一致的,连原因也是同一个,那么我下面就用BroadcastReceiver举例,说一下这个问题的根本原因。
1. BroadcastReceiver 的分发
众所周知,BroadcastReceiver的分发也是由framework中的AMS来处理的,就是它:frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java。经过一番调查之后,发现AMS收到一个BroadcastReceiver请求的时候,会通过它的成员变量mReceiverResolver来查找哪些BroadcastReceiver可以处理这个Intent。
1 | /** |
下面我们就要分析这个mReceiverResolver是如何工作的。
2. IntentResolver 查找对应的 BroadcastReceiver
AMS通过IntentResolver的queryIntent来查找 Intent 对应的那些BroadcastReceiver。这个方法的定义大致如下:
1 | public List<R> queryIntent(Intent intent, String resolvedType, boolean defaultOnly, |
经过我的梳理,这个方法的工作流程大致如下:
1 | public List<R> queryIntent(Intent intent, String resolvedType, boolean defaultOnly, |
由此可见,当Intent中既有Action,又有Uri的时候,Action就会被忽略。
总结
当我们使用的Intent去隐式启动BroadcastReceiver或者 Activity,如果Intent里既有Action,又有Uri。我们需要在组件的intent-filter显式声明我们能够捕获该Uri的scheme。类似https://m.lstec.org的 uri,需要在AndroidManifest.xml 中用如下的方式声明:
1 | <intent-filter> |