我有一个应用程序,它使用应用程序包中包含的服务。
我现在正在制作一个新应用程序,它使用更新版本的相同服务, bundle 在自己的应用程序包中。
如果设备上只安装了一个应用程序,则效果很好。但是,如果两个应用程序都安装在设备上,则它们都会使用最近安装的软件包(即最旧的软件包)中的服务。这是有问题的,原因有两个: 1)旧服务不处理新调用(显然) 2) 当使用与其他应用程序 bundle 的服务时,该服务最终会使用其他应用程序保存的数据,而不是自己的数据。
短期内,我真的只是希望每个应用程序都能与自己的服务版本对话。是否可以在不让每个 bundle 服务响应独特 Intent 并让每个应用程序使用该独特 Intent 的情况下做到这一点?
从长远来看,最好选择让两个应用程序都与最新版本的服务通信,并让它们共享数据,并在任一应用程序被卸载时都能正常运行。正确的做法是什么?数据主要存储在 SQLite 数据库中,存储在 Context.getDatabasePath()
指定的位置,这当然是特定于应用程序的,如果卸载该应用程序,则会被删除。
这是我迄今为止尝试过的:
我可以使用PackageManager.queryIntentServices()
来获取包含两个版本服务的ResolveInfo列表。通过查看 ResolveInfo.serviceInfo.applicationInfo.packageName
,我可以分辨出哪个是哪个。但是,似乎没有任何方法可以使用 ResolveInfo 来指定我想要处理 Intent 的两个服务中的哪一个。
这似乎应该做正确的事情:
serviceIntent.setPackage(context.getApplicationContext().getApplicationInfo().packageName);
因为该 packageName 与我要匹配的 packageName 相同。事实上,当我使用修改后的 Intent 调用 queryIntentServices
时,我会得到一个仅包含包中服务的列表。但是,当我尝试以该 Intent 绑定(bind)Service 时,它会引发安全异常。
java.lang.RuntimeException: Unable to bind to service my.service.package.name.MyServiceClass@40531558 with Intent { act=my.intent.string pkg=my.app.package.name }: java.lang.SecurityException: Not allowed to start service Intent { act=RuntimeException: Unable to bind to service my.service.package.name.MyServiceClass } without permission private to package
根据一些谷歌搜索,我还尝试通过以下方式创建 Intent :
Intent serviceIntent = new Intent().setClassName(
"my.service.package.name",
"my.service.package.name.ServiceClassName");
这根本无法解析任何服务。
我还尝试在两个应用程序中以各种组合将 AndroidManifest.xml 中的服务条目中的 android:exported=
更改为 true、false 或未指定,但均无济于事。 (我希望如果我将它们都设置为 false,那么每个应用程序只能看到自己的服务副本。)
最佳答案
这是一个有效的解决方案:
在 list 中添加服务的元数据。名称应该类似于“服务启动器”,并且值必须与 Intent 过滤器中的操作名称相同。
<meta-data android:name="Service Launcher"
android:value="your.service.action.string.here" />
<intent-filter>
<action android:name="your.service.action.string.here" />
</intent-filter>
然后在您的代码中添加一个访问方法来获取元数据并使用它来构建 Intent。
public static String getServiceIntentAction(Context context) {
String action = "";
try {
PackageManager pm = context.getPackageManager();
android.content.ComponentName cn = new android.content.ComponentName(context.getPackageName(), "com.your.ServiceClass");
android.content.pm.ServiceInfo si = pm.getServiceInfo(cn, PackageManager.GET_META_DATA);
action = si.metaData.getString("Service Launcher");
} catch(android.content.pm.PackageManager.NameNotFoundException nnfe) {}
return action;
}
然后,任何构建 Intent 来启动服务的内容都会调用此方法。应用中该服务的每个用户都可以执行不同的操作,仅使用一个版本的库和 list 更改来与其应用进行通信。
要让两个应用程序使用相同的服务,该服务可能应该作为其自己的实体安装。
关于android - 如何选择哪个服务来处理 Intent ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8997258/