处理Lollipop中的隐含意图未来弃用

时间:2015-01-16 09:52:33

标签: java android android-intent service intentfilter

要将数据传输到其他应用程序,我一直在使用隐式意图,如下例所示:

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" class:

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));
            }
        }
    }

我该如何处理?

3 个答案:

答案 0 :(得分:19)

是的,在使用Android 5.0的设备中运行时,此代码会显示警告(如果您的应用targetSdkVersion <21)或直接崩溃(如果定位Lollipop本身)。查看source code for validateServiceIntent() in ContextImpl.java

此代码很危险,因为它可能允许恶意接收者自行注册并拦截(或更改)这些调用。

最合理的替代方法可能是使用Intent.setPackage()指定要呼叫的应用的包名称。当这样做时,意图不再是隐含的,它将起作用。例如:

   intent.setPackage("com.example.app");

当然,如果接收器位于您的应用内部,则有更简单的替代方案(但从您对问题的描述来看似乎并非如此)。

答案 1 :(得分:2)

正如其他评论和答案所述,此更改仅与bindService相关,而非sendBroadcast,因此,如果情况如此(如您的示例代码中所示) - 您不要# 39;不需要改变任何东西。

如果您使用bindService,那么对显式Intent进行隐式Intent的方法是使用ComponentName并使用setComponent或{{在服务Intent上设置它3}}方法。

来自Intent课程文档:

  

意图解决方案

     

您将使用两种主要形式的意图。

     

Explicit Intents指定了一个组件(通过   setComponent(ComponentName)或setClass(Context,Class)),其中   提供要运行的确切类。通常这些不包括任何   其他信息,只是一种应用程序启动的方式   当用户与之交互时,它具有的各种内部活动   应用。隐含意图没有指定组件;代替,   他们必须包含足够的信息供系统确定   可用组件最好为该意图运行。

答案 2 :(得分:1)

正如@Commonsware在blog中所指出的,解决这个问题的3种方法是:

1。 resolveService()

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())