自 Android 13 起无法启动任意 Activity

Jas*_*tes 7 android android-intent android-activity

我构建了一个小型个人应用程序,它允许我为不同的 URL 指定不同的浏览器。在 Android 13 之前,它运行良好,但在 Android 13 之后的某个时候,它开始出现故障。我怀疑这与应用程序启动任意活动的权限(或缺乏权限)有关,但仔细阅读文档却没有得到任何结果。

该过程的工作原理如下:

我查询所有活动以查找Intent具有 URI 作为其数据属性的活动

Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(uri); // uri is some location like 'https://www.google.com'
PackageManager pm = context.getPackageManager();
List<ResolveInfo> allTargets = pm.queryIntentActivities(intent, PackageManager.MATCH_ALL);
Run Code Online (Sandbox Code Playgroud)

allTargets我根据浏览器的名称循环查找我想要的浏览器:

ResolveInfo target = null;
for (ResolveInfo b : allTargets) {
    String appName = b.loadLabel(pm).toString();

    // targetBrowserName will be something like "Chrome"
    if(appName.equalsIgnoreCase(targetBrowserName)) {
        target = b;
        break;
    }
}
Run Code Online (Sandbox Code Playgroud)

然后我尝试使用 url 启动此浏览器

ActivityInfo activity = target.activityInfo;

ComponentName name = new ComponentName(activity.applicationInfo.packageName, activity.name);
targetIntent = new Intent(Intent.ACTION_MAIN);
targetIntent.addCategory(Intent.CATEGORY_LAUNCHER);
targetIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
targetIntent.setComponent(name);
targetIntent.setData(uri);
startActivity(targetIntent);
Run Code Online (Sandbox Code Playgroud)

现在失败并出现如下错误:

android.content.ActivityNotFoundException: Unable to find explicit activity class {com.android.chrome/com.google.android.apps.chrome.IntentDispatcher}; have you declared this activity in your AndroidManifest.xml, or does your intent not match its declared <intent-filter>?
        at android.app.ActivityThread.performResumeActivity(ActivityThread.java:4803)
        at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:4836)
        at android.app.servertransaction.ResumeActivityItem.execute(ResumeActivityItem.java:54)
        at android.app.servertransaction.ActivityTransactionItem.execute(ActivityTransactionItem.java:45)
        at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:176)
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:97)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2308)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loopOnce(Looper.java:201)
        at android.os.Looper.loop(Looper.java:288)
        at android.app.ActivityThread.main(ActivityThread.java:7898)
        at java.lang.reflect.Method.invoke(Native Method)
Run Code Online (Sandbox Code Playgroud)

我尝试了启动代码的各种排列(提醒一下,它工作得很好)。例如

targetIntent = pm.getLaunchIntentForPackage(activity.applicationInfo.packageName);
targetIntent.setAction(Intent.ACTION_VIEW);
targetIntent.addCategory(Intent.CATEGORY_BROWSABLE);
targetIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
Run Code Online (Sandbox Code Playgroud)

但我仍然遇到同样的错误(尽管在上面的情况下仍然找不到不同的 Activity 类)

我知道应用程序可见性受到限制,但我认为我已被覆盖,因为我的应用程序中有此内容AndroidManifest.xml

    <!-- As per guidelines, QUERY_ALL_PACKAGES is required to list all browsers -->
    <uses-permission
        android:name="android.permission.QUERY_ALL_PACKAGES"
        tools:ignore="QueryAllPackagesPermission" />
Run Code Online (Sandbox Code Playgroud)

阅读文档时,我注意到清单中没有元素(这是新的吗?),所以我添加了以下内容<queries>

    <queries>
        <intent>
            <action android:name="android.intent.action.VIEW" />
            <category android:name="android.intent.category.BROWSABLE" />
            <data android:scheme="https" />
        </intent>
    </queries>
Run Code Online (Sandbox Code Playgroud)

没有喜悦。

有谁知道以编程方式启动已知/特定[浏览器]应用程序的正确方法?或者 Android 13 中发生了什么变化才能让这段代码再次工作?

谢谢!

编辑下面的正确答案

下面的答案中提供的指导有效。这是最终代码的摘要版本:

// Create an intent with the destination URL
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(uri);

// List all activities that support this intent, and choose one:
PackageManager pm = context.getPackageManager();
List<ResolveInfo> allTargets = pm.queryIntentActivities(intent, PackageManager.MATCH_ALL);
ResolveInfo target = null;
for (ResolveInfo b : allTargets) {
    String appName = b.loadLabel(pm).toString();

    // targetBrowserName is something like "Chrome"
    if(appName.equalsIgnoreCase(targetBrowserName)) {
        target = b;
        break;
    }
}

// Set the specific component to be launched
ActivityInfo activity = target.activityInfo;
ComponentName name = new ComponentName(activity.applicationInfo.packageName, activity.name);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
intent.setComponent(name);

// Start
startActivity(intent);
Run Code Online (Sandbox Code Playgroud)

小智 9

似乎与此行为更改有关https://developer.android.com/about/versions/13/behavior-changes-13#intent-filters

当您的应用向另一个面向 Android 13 或更高版本的应用的导出组件发送 Intent 时,当且仅当它与接收应用中的元素匹配时,才会传递该 Intent。不匹配的意图将被阻止。

为了确保这确实是您的问题,请检查 logcat 中的 tag PackageManager。类似的东西W/PackageManager( 1828): Access blocked: ComponentInfo{com.android.chrome/com.google.android.apps.chrome.Main}应该出现在那里

我还没有过多研究它是如何工作的,但似乎现在我们需要在使用外部活动之前查询它们,除非它们没有意图过滤器。这将“激活”组件,然后您使用的意图需要“匹配”其他应用程序的意图过滤器:

意图与意图过滤器相匹配,不仅可以发现要激活的目标组件,还可以发现有关设备上组件集的某些信息。例如,Home 应用程序通过查找具有指定 ACTION_MAIN 操作和 CATEGORY_LAUNCHER 类别的意图过滤器的所有活动来填充应用程序启动器。仅当 Intent 中的操作和类别与过滤器匹配时,匹配才会成功,如 IntentFilter 类的文档中所述。

https://developer.android.com/guide/components/intents-filters#imatch

我也有类似的问题。它不起作用的原因是我通过intent.setData在调用 后调用来修改 Uri queryIntentActivities。看起来这会使组件的激活无效。

intent.setData摘要:在queryIntentActivities使意图起作用后不调用