处理 Lollipop 中隐含的意图未来弃用
Dealing with implicit intent future deprecation in Lollipop
为了将数据传输到其他应用程序,我一直在使用隐式意图,如下例所示:
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
Binding to a Service
The Context.bindService() method now requires an explicit Intent, and throws
an exception if given an implicit intent. To ensure your app is secure, use an
explicit intent when starting or binding your Service, and do not declare intent
filters for the service.
来自 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));
}
}
}
我该如何处理?
是的,当 运行 在具有 Android 5.0 的设备中时,此代码将显示警告(如果您的应用程序的 targetSdkVersion
小于 21)或彻底崩溃(如果针对 Lollipop本身)。检查 source code for validateServiceIntent()
in ContextImpl.java
:
此代码很危险,因为它可能允许恶意接收者自行注册并拦截(或更改)这些调用。
最合理的选择可能是使用 Intent.setPackage()
指定要调用的应用程序的包名称。以这种方式完成后,意图不再是隐含的,而且它会起作用。例如:
intent.setPackage("com.example.app");
当然,如果接收器在您的应用程序中,则有更简单的选择(但从您对问题的描述来看,情况似乎并非如此)。
如其他评论和答案中所述,更改仅与 bindService
相关,与 sendBroadcast
无关,因此如果是这种情况(如示例代码中所示)- 您不需要改变任何东西。
如果您使用 bindService
,将隐式 Intent 转换为显式 Intent 的方法是使用 ComponentName
并使用 setComponent
or setClass
方法将其设置在服务 Intent 上.
来自 Intent
class 文档:
Intent Resolution
There are two primary forms of intents you will use.
Explicit Intents have specified a component (via
setComponent(ComponentName) or setClass(Context, Class)), which
provides the exact class to be run. Often these will not include any
other information, simply being a way for an application to launch
various internal activities it has as the user interacts with the
application. Implicit Intents have not specified a component; instead,
they must include enough information for the system to determine which
of the available components is best to run for that intent.
正如@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));
如果查询 returns 多个信息,这可能意味着恶意服务正在侦听。
3。 setPackage()
如果你有包名,你可以像@matiash在他的post中所说的那样设置包名:
Intent i = new Intent(MyClass.class.getName());
i.setPackage(MyClass.class.getPackage().getName())
为了将数据传输到其他应用程序,我一直在使用隐式意图,如下例所示:
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
Binding to a Service
The Context.bindService() method now requires an explicit Intent, and throws an exception if given an implicit intent. To ensure your app is secure, use an explicit intent when starting or binding your Service, and do not declare intent filters for the service.
来自 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));
}
}
}
我该如何处理?
是的,当 运行 在具有 Android 5.0 的设备中时,此代码将显示警告(如果您的应用程序的 targetSdkVersion
小于 21)或彻底崩溃(如果针对 Lollipop本身)。检查 source code for validateServiceIntent()
in ContextImpl.java
:
此代码很危险,因为它可能允许恶意接收者自行注册并拦截(或更改)这些调用。
最合理的选择可能是使用 Intent.setPackage()
指定要调用的应用程序的包名称。以这种方式完成后,意图不再是隐含的,而且它会起作用。例如:
intent.setPackage("com.example.app");
当然,如果接收器在您的应用程序中,则有更简单的选择(但从您对问题的描述来看,情况似乎并非如此)。
如其他评论和答案中所述,更改仅与 bindService
相关,与 sendBroadcast
无关,因此如果是这种情况(如示例代码中所示)- 您不需要改变任何东西。
如果您使用 bindService
,将隐式 Intent 转换为显式 Intent 的方法是使用 ComponentName
并使用 setComponent
or setClass
方法将其设置在服务 Intent 上.
来自 Intent
class 文档:
Intent Resolution
There are two primary forms of intents you will use.
Explicit Intents have specified a component (via setComponent(ComponentName) or setClass(Context, Class)), which provides the exact class to be run. Often these will not include any other information, simply being a way for an application to launch various internal activities it has as the user interacts with the application. Implicit Intents have not specified a component; instead, they must include enough information for the system to determine which of the available components is best to run for that intent.
正如@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));
如果查询 returns 多个信息,这可能意味着恶意服务正在侦听。
3。 setPackage()
如果你有包名,你可以像@matiash在他的post中所说的那样设置包名:
Intent i = new Intent(MyClass.class.getName());
i.setPackage(MyClass.class.getPackage().getName())