处理 Lollipop 中的隐含意图未来弃用
Posted
技术标签:
【中文标题】处理 Lollipop 中的隐含意图未来弃用【英文标题】:Dealing with implicit intent future deprecation in Lollipop 【发布时间】:2015-03-14 21:32:42 【问题描述】:为了将数据传输到其他应用程序,我一直在使用隐式意图,如下例所示:
Intent intent = new Intent();
intent.setAction("com.example.OpenURL");
intent.putExtra("URL_TO_OPEN", url_string);
sendOrderedBroadcastAsUser(intent);
Intent intent = new Intent();
intent.setAction("com.example.CreateUser");
intent.putExtra("Username", uname_string);
intent.putExtra("Password", pw_string);
sendBroadcast(intent);
Intent intent = new Intent();
intent.setAction("com.example.BackupUserData");
intent.setData(file_uri);
intent.addFlags(FLAG_GRANT_READ_URI_PERMISSION);
sendBroadcast(intent);
但在 android 5.0 中不再推荐这种行为
http://developer.android.com/about/versions/android-5.0-changes.html
绑定到服务
Context.bindService() 方法现在需要显式 Intent,并抛出 如果给出隐含意图,则例外。为确保您的应用安全,请使用 启动或绑定服务时的明确意图,并且不声明意图 服务的过滤器。
来自android源代码更准确地说是“ContextImpl”类:
private void validateServiceIntent(Intent service)
if (service.getComponent() == null && service.getPackage() == null)
if (getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP)
IllegalArgumentException ex = new IllegalArgumentException(
"Service Intent must be explicit: " + service);
throw ex;
else
Log.w(TAG, "Implicit intents with startService are not safe: " + service
+ " " + Debug.getCallers(2, 3));
我该如何处理?
【问题讨论】:
更改只影响 bindService,从您发布的代码来看,您正在执行 sendBroadcast,AFAIK 它不应该影响您。 它说“在启动或绑定您的服务时” 根据您问题中的代码,您没有服务。 我明白了这里的重点,这些只是您可以用 startservice() 替换 sendbroadcast() 的示例。重要的部分是意图的构建。 【参考方案1】:是的,当在装有 Android 5.0 的设备上运行时,此代码将显示警告(如果您的应用的 targetSdkVersion
小于 21)或直接崩溃(如果针对 Lollipop 本身)。检查source code for validateServiceIntent()
in ContextImpl.java
:
此代码很危险,因为它可能允许恶意接收者注册自己并拦截(或更改)这些调用。
最合理的选择可能是使用Intent.setPackage()
指定您要调用的应用程序的包名。以这种方式完成后,意图不再是隐含的,它将起作用。例如:
intent.setPackage("com.example.app");
当然,如果接收器在您的应用内,还有更简单的替代方案(但从您对问题的描述来看,情况似乎并非如此)。
【讨论】:
你能提供一个完整的例子吗? @An-droid 实际上,它只是在调用startService()
(或类似方法)之前添加该行。您的问题是“通用的”还是您手头有更具体的问题?
@CommonsWare 我的意思是,对于这次检查来说,这已经足够明确了。问题中的 Intent 都提供了一个动作,如果它在另一个应用程序中,您很少会知道处理它们的类。
@matiash 设置包不足以使意图明确。请参阅我的答案以获取更多详细信息***.com/a/28150873/624109
@Muzikant 恐怕不得不反对。我的观点与我对 CommonsWare 的观点相同,尽管他迟到了删除他的评论:任何适用于 KITKAT 的“正确”服务 Intent 都将通过添加包名称在 Lollipop 上运行——它不一定是“完全明确的”,但为此新检查定义的足够明确。我已经对此进行了测试,例如使用 GCM 调用,该调用曾经有效,但在针对 API 21 时被破坏。【参考方案2】:
正如在其他 cmets 和答案中所说,更改仅与 bindService
相关,与 sendBroadcast
无关,因此如果是这种情况(如您的示例代码中所示) - 您无需更改任何内容。
如果您确实使用bindService
,将隐式 Intent 转换为显式 Intent 的方法是使用 ComponentName
并使用 setComponent
或 setClass
方法将其设置在服务 Intent 上。
来自Intent
类文档:
意图解析
您将使用两种主要形式的意图。
Explicit Intents 已经指定了一个组件(通过 setComponent(ComponentName) 或 setClass(Context, Class)),其中 提供要运行的确切类。通常这些不包括任何 其他信息,只是作为应用程序启动的一种方式 当用户与 应用。隐式意图没有指定组件;反而, 它们必须包含足够的信息,以便系统确定哪个 可用组件中的一个最好是针对该意图运行。
【讨论】:
这是正确答案。绑定服务更改对用于活动和广播的隐式 Intent 没有影响。【参考方案3】:正如@Commonsware 在他的blog 中指出的那样,解决此问题的三种方法是:
1。解决服务()
Intent i = new Intent("serviceName");
ResolveInfo info = ctx.getPackageManager().resolveService(i, Context.BIND_AUTO_CREATE);
i.setComponent(new ComponentName(info.serviceInfo.packageName,info.serviceInfo.name));
并使用意图绑定服务。
2。 queryIntentServices()
Intent i = new Intent("serviceName");
List<ResolveInfo> infos = ctx.getPackageManager().queryIntentServices(i,Context.BIND_AUTO_CREATE);
if (infos.isEmpty())
throw new IllegalStateException("no service found");
if (infos.size() > 1)
throw new SecurityException("multiple services found, could be a security issue");
i.setComponent(new ComponentName(infos.get(0).serviceInfo.packageName, infos.get(0).serviceInfo.name));
如果查询返回多个信息,这可能意味着恶意服务正在监听。
3。 setPackage()
如果你有包名,你可以像@matiash在他的帖子中所说的那样设置包名:
Intent i = new Intent(MyClass.class.getName());
i.setPackage(MyClass.class.getPackage().getName())
【讨论】:
以上是关于处理 Lollipop 中的隐含意图未来弃用的主要内容,如果未能解决你的问题,请参考以下文章