问题描述
最近的工作中,涉及到在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> |