背景
应用程序使用 Intents 打开其他应用程序,有时使用专门的 Intents。
一个例子是这个 Intent,从 WhatsApp 中选择一个联系人:
val WHATSAPP_PACKAGE_NAME = "com.whatsapp"
val whatsAppPickIntent = Intent(Intent.ACTION_PICK).setPackage(WHATSAPP_PACKAGE_NAME)
这通常工作正常。当您希望启动应用程序时也是如此:
val launchIntent=packageManager.getLaunchIntentForPackage(WHATSAPP_PACKAGE_NAME)
问题
最近我得知一个相对较新的功能,允许用户拥有同一个应用程序的多个实例。它可能在其他设备上可用,但在 OnePlus 设备上它被称为“并行应用程序”。这是 2 个 WhatsApp 实例的示例,每个实例都分配给不同的电话号码:
问题是,这可能会破坏 Intent 与应用程序的单个实例一起工作的方式。现在 Intent 不知道该去哪个应用程序。启动器现在为 WhatsApp 显示 2 个图标:
如果您选择通过普通启动器图标(左侧)启动 WhatsApp,它会显示以下对话框:
工作正常,但如果您选择使用选择器意图,您仍然会看到此对话框,但是当您从对话框中选择一个项目时,它不会让您真正对它做任何事情(打开和关闭应用程序),而显示吐司“不支持文件格式”。
我试过的
由于我没有该设备,我试图通过互联网阅读有关它的信息,但我只找到了与用户相关的信息,例如:
我决定尝试进一步调查它,通过向告诉我的人发送一个 APK,尝试查看下一个代码是否会起作用:
val whatsAppPickIntent = Intent(Intent.ACTION_PICK).setPackage(WHATSAPP_PACKAGE_NAME)
val queryIntentActivities: List<ResolveInfo> = packageManager.queryIntentActivities(whatsAppPickIntent, 0)
button2.setOnClickListener {
intent = Intent(Intent.ACTION_PICK)
val resolveInfo = queryIntentActivities[0]
toast("number of possible choices:" + queryIntentActivities.size)
intent.component = ComponentName(resolveInfo.activityInfo.packageName, resolveInfo.activityInfo.name)
startActivity(intent)
}
将显示的 toast 告诉我只有一个可以处理意图的东西,实际上,当我使用它时,我会得到相同的对话框来选择要使用的实例。就像在最初的意图中一样,它失败了,同样的吐司。
编辑:后来我尝试了下一件事:我要求在启用该功能之前和之后显示什么是 ResolveInfo 属性,方法是使用以下代码:
val launchIntent = packageManager.getLaunchIntentForPackage(WHATSAPP_PACKAGE_NAME)
val whatsAppPickIntent = Intent(Intent.ACTION_PICK).setPackage(WHATSAPP_PACKAGE_NAME)
var queryIntentActivities: List<ResolveInfo> = packageManager.queryIntentActivities(whatsAppPickIntent, 0)
var sb = StringBuilder()
queryIntentActivities[0].dump(object : Printer {
override fun println(x: String?) {
if (x != null)
sb.append(x)
}
}, "")
val pickResult = "pick result:packageName:\"" + queryIntentActivities[0].activityInfo.packageName + "\" name:\"" + queryIntentActivities[0].activityInfo.name + "\"\n\n" + "extended:" + sb.toString()
sb = StringBuilder()
queryIntentActivities = packageManager.queryIntentActivities(launchIntent, 0)
queryIntentActivities[0].dump(object : Printer {
override fun println(x: String?) {
if (x != null)
sb.append(x)
}
}, "")
val launchResult = "launch result:packageName:\"" + queryIntentActivities[0].activityInfo.packageName + "\" name:\"" + queryIntentActivities[0].activityInfo.name + "\"\n\n" + "extended:" + sb.toString()
val body = pickResult + "\n\n" + launchResult
val emailIntent = Intent(Intent.ACTION_SENDTO, Uri.fromParts("mailto", "", null))
emailIntent.putExtra(Intent.EXTRA_SUBJECT, "whatsApp investigation")
emailIntent.putExtra(Intent.EXTRA_TEXT, body)
startActivity(Intent.createChooser(emailIntent, "Send email..."))
结果是两者都是一样的,好像一切都很好。这是打开/关闭时的结果(完全相同):
pick result:packageName:"com.whatsapp" name:"com.whatsapp.ContactPicker"
extended:priority=0 preferredOrder=0 match=0x108000 specificIndex=-1 isDefault=falseActivityInfo: name=com.whatsapp.ContactPicker packageName=com.whatsapp enabled=true exported=true directBootAware=false taskAffinity=com.whatsapp targetActivity=null persistableMode=PERSIST_ROOT_ONLY launchMode=0 flags=0x3 theme=0x7f110173 screenOrientation=-1 configChanges=0xfb3 softInputMode=0x0 lockTaskLaunchMode=LOCK_TASK_LAUNCH_MODE_DEFAULT resizeMode=RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION ApplicationInfo: name=com.whatsapp.AppShell packageName=com.whatsapp labelRes=0x7f100473 nonLocalizedLabel=null icon=0x7f080c15 banner=0x0 className=com.whatsapp.AppShell processName=com.whatsapp taskAffinity=com.whatsapp uid=10099 flags=0x3 privateFlags=0x1010 theme=0x7f110164 requiresSmallestWidthDp=0 compatibleWidthLimitDp=0 largestWidthLimitDp=0 sourceDir=/data/app/com.whatsapp-NaKTLVhiNTh4zEGhFdkxrg==/base.apk seinfo=default:targetSdkVersion=26 seinfoUser=:complete dataDir=/data/user/0/com.whatsapp deviceProtectedDataDir=/data/user_de/0/com.whatsapp credentialProtectedDataDir=/data/user/0/com.whatsapp enabled=true minSdkVersion=15 targetSdkVersion=26 versionCode=452238 targetSandboxVersion=1 supportsRtl=true fullBackupContent=true category=4
launch result:packageName:"com.whatsapp" name:"com.whatsapp.Main"
extended:priority=0 preferredOrder=0 match=0x0 specificIndex=-1 isDefault=falseActivityInfo: name=com.whatsapp.Main packageName=com.whatsapp labelRes=0x7f10044c nonLocalizedLabel=null icon=0x0 banner=0x0 enabled=true exported=true directBootAware=false taskAffinity=com.whatsapp targetActivity=null persistableMode=PERSIST_ROOT_ONLY launchMode=0 flags=0x3 theme=0x0 screenOrientation=-1 configChanges=0xfb3 softInputMode=0x0 lockTaskLaunchMode=LOCK_TASK_LAUNCH_MODE_DEFAULT resizeMode=RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION ApplicationInfo: name=com.whatsapp.AppShell packageName=com.whatsapp labelRes=0x7f100473 nonLocalizedLabel=null icon=0x7f080c15 banner=0x0 className=com.whatsapp.AppShell processName=com.whatsapp taskAffinity=com.whatsapp uid=10099 flags=0x3 privateFlags=0x1010 theme=0x7f110164 requiresSmallestWidthDp=0 compatibleWidthLimitDp=0 largestWidthLimitDp=0 sourceDir=/data/app/com.whatsapp-NaKTLVhiNTh4zEGhFdkxrg==/base.apk seinfo=default:targetSdkVersion=26 seinfoUser=:complete dataDir=/data/user/0/com.whatsapp deviceProtectedDataDir=/data/user_de/0/com.whatsapp credentialProtectedDataDir=/data/user/0/com.whatsapp enabled=true minSdkVersion=15 targetSdkVersion=26 versionCode=452238 targetSandboxVersion=1 supportsRtl=true fullBackupContent=true category=4
所以我想检查一下其他内容:尝试放置 WhatsApp 的小部件快捷方式(称为“whatsApp 聊天”),当此功能打开时,需要您选择联系人。事实证明,它不能很好地处理它。它询问将小部件创建到哪个应用程序:原始或克隆。如果你选择原版,一切都很好。如果您选择克隆,它会很好地添加小部件,但是当单击它时,它会转到应用程序的主窗口,而不是转到人。
问题
如何区分主实例和“克隆”实例?我的意思是,如何将 Intent 定向到目标应用程序的单个实例(主要实例)?我问这个关于我提出的两个意图(启动和选择器)。
这个功能是如何工作的?现在每个实例的私有数据保存在哪里?他们每个人都有一个进程,但名称不同吗?
其他 OEM 的其他设备是否有此功能?它在那里的工作方式和这里一样吗?
如果用户选择了要定位的应用程序,为什么我们会看到 toast 消息?它是否可能是一个错误的功能,仅适用于启动意图?
至少有一种方法可以知道给定的应用程序(给定它的包名称)是否启用了此功能?