为啥没有找到处理 Intent 的 Activity?

Posted

技术标签:

【中文标题】为啥没有找到处理 Intent 的 Activity?【英文标题】:Why is no Activity found to handle Intent?为什么没有找到处理 Intent 的 Activity? 【发布时间】:2019-06-26 19:26:59 【问题描述】:

我不想采用常规的getPackageManager().getLaunchIntentForPackage("com.example.app") 方式,而是想自己创建启动意图。

Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
intent.setPackage("com.example.app");
startActivity(intent);

如果 com.example.app 已安装、启用且清单正确,为什么 android 找不到 Activity? (它与getLaunchIntentForPackage 完美配合。)

【问题讨论】:

com.example.app 用清单文件中的 package="com.example...." 检查这个。 欢迎访问:***.com/a/30617251/6672577 @Opriday com.example.app 的清单文件是正确的,它包含正确的包名称 (com.example.app)。这与我尝试与intent.setPackage("com.example.app"); 一起使用的包名称相同。没有错字。 @Opriday 我已经访问了您的链接,但在那里找不到任何相关内容。我应该查找哪些信息? +1 。这实际上是一个很好的问题。让我们想知道您的意图和 getLaunchIntentForPackage() 创建的意图之间有什么区别。试试 Log.d(TAG, intent.toString() + " vs " + intent2.toString())。 (我添加了我的解决方法作为答案。) 【参考方案1】:

'要接收隐式意图,您必须在意图过滤器中包含 CATEGORY_DEFAULT 类别。' - 你的接收应用有这个吗?

例子:

<activity android:name="ShareActivity">
     <intent-filter>
         <action android:name="android.intent.action.SEND"/>
         <category android:name="android.intent.category.DEFAULT"/>
         <data android:mimeType="text/plain"/>
     </intent-filter>
</activity>

摘自: https://developer.android.com/guide/components/intents-filters#Receiving

您还可以检查以确保有一个活动可以接收您的广播:

 PackageManager packageManager = getPackageManager();
 List<ResolveInfo> activities = packageManager.queryIntentActivities(intent,PackageManager.MATCH_DEFAULT_ONLY);
 boolean isIntentSafe = activities.size() > 0;

摘自: https://developer.android.com/training/basics/intents/sending#java

【讨论】:

这不是一个隐含的意图。这是明确的,因为包名是使用intent.setPackage("com.example.app") 指定的。 "通过提供目标应用的包名或完全限定的组件类名,显式意图指定哪个应用程序将满足该意图。" - source 接收应用在其LAUNCHER 意图过滤器中没有DEFAULT 类别,只有其他地方。 如果我尝试使用其他具有DEFAULT 类别的接收器应用程序,那将是完美的开始。但是DEFAULT 不应该是必需的,因为我的意图是明确的。如果我可以通过查看接收器应用程序的清单找到正确的活动,为什么 Android 不能为我做呢?它还知道类别、操作和包名称。如果getLaunchIntentForPackage 在没有DEFAULT 的情况下也能完美运行,那么我的方法也应该可以。【参考方案2】:

startActivity所有意图视为声明CATEGORY_DEFAULT

即使您的代码中没有intent.addCategory(Intent.CATEGORY_DEFAULT);

即使你加了intent.removeCategory(Intent.CATEGORY_DEFAULT);

即使您的意图很明确*:intent.setPackage("com.example.app");. * 提供“目标应用的包名或完全限定的组件类名”。

...除非没有

如果你设置了目标activity的类名,系统将不会去寻找CATEGORY_DEFAULT

Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
intent.setClassName("com.example.app", "com.example.app.NameOfTheActivityToBeStarted");
startActivity(intent);

标题来源:the <category> element's page 上的蓝色注释。 显式意图的定义来源:developer.android.com。

【讨论】:

即使intent没有action或category,系统也会找到activity,比如:Intent intent = new Intent(); intent.setClassName("com.example.app", "com.example.app.NameOfTheActivityToBeStarted"); startActivity(intent);【参考方案3】:

我了解到您正在尝试启动具有已知包名称 (com.example.app) 的已知应用程序的启动器活动。我假设您有有关该应用程序的信息。因此,您可以像这样通过显式意图启动它:

Intent intent = new Intent();
intent.setComponent(new ComponentName("com.example.app", "com.example.app.MainActivity"));
if(intent.resolveActivity(getPackageManager()) != null) 
    startActivity(intent);

编辑:对两个意图对象进行分析(intent1 == 你自己的意图 VS intent2 == 从getLaunchIntentForPackage() 创建的意图),区别是

意图1:

act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] pkg=com.example.app

意图2:

act=android.intent.action.MAIN 猫=[android.intent.category.LAUNCHER] flg=0x10000000 pkg=com.example.app cmp=com.example.app/.MainActivity

我将不得不相信,您为创建自己的意图对象所做的一切不足以使显式意图起作用。您必须向 Android 提供有关您的意图的更多信息,例如具体的组件名称(如我上面的回答所示)。

【讨论】:

+1 为您提供帮助,您了解 我的示例代码的目标resolveActivity 将我引导至其文档,其中包含有用的详细信息。但您的回答不是关于我的问题的目标:更深入地了解为什么找不到活动。 My own answer 已经有了使用setClassName 的解决方法(与您的setComponent 相同,只是更方便)。您的评论中有一个好主意(比较意图),但您的回答目前不包含新的细节,只是我回答的已知信息的一个子集。 (顺便说一句,你上面有一条评论starting with“我考虑过使用intent.setComponent(...),但是”,这与这个答案。) 谢谢。我确实认识到这不是您目标的答案。我现在没有足够的时间,所以我把我脑子里想的都给了你。我会在今天晚些时候回复你。干杯! 我更新了我的答案。查看由 getLaunchIntentForPackage() 创建的显式意图 VS 意图,看起来您缺少工作意图的值。应使用 setClassName() 或 setComponent() 来完成有关您的意图的信息。 有助于查看差异:0x10000000 标志 (Intent.FLAG_ACTIVITY_NEW_TASK) 和组件名称。这证明系统应该在这两种情况下都找到正确的活动。但事实并非如此,所以问题仍然存在:为什么。活动存在,即使是孩子也能找到(知道动作、类别和包)。系统是否在寻找我告诉它寻找的东西?我们已经有了答案:不,it's looking for the default category too。但目前还不清楚为什么。如果有明确的意图,为什么要查找默认类别?【参考方案4】:

这是将 android.content.Intent#CATEGORY_DEFAULT 添加到所有 startActivity 代码的函数。

ResolveInfo resolveIntent(Intent intent, String resolvedType, int userId, int flags) 
        try 
            return AppGlobals.getPackageManager().resolveIntent(intent, resolvedType,
                    PackageManager.MATCH_DEFAULT_ONLY | flags
                    | ActivityManagerService.STOCK_PM_FLAGS, userId);
         catch (RemoteException e) 
        
        return null;
    

/**
     * Resolution and querying flag: if set, only filters that support the
     * @link android.content.Intent#CATEGORY_DEFAULT will be considered for
     * matching.  This is a synonym for including the CATEGORY_DEFAULT in your
     * supplied Intent.
     */
    public static final int MATCH_DEFAULT_ONLY  = 0x00010000;

这是一切开始的代码 http://androidxref.com/7.1.2_r36/xref/frameworks/base/core/java/android/app/ContextImpl.java#766

【讨论】:

+1。很高兴看到代码(在调用startActivity 之后运行并且)在组件不为空时忽略标志(使setComponentsetClassName 方法工作而不是不足的setPackage 方法)。我想这与described here 的逻辑相同,但我不确定也找不到代码。我们现在非常接近在代码级别上完全了解平台的行为。【参考方案5】:

您要求查看在startActivity 之后执行的代码,就在这里。

在您的应用中:Activity.startActivity(Intent) 调用Activity.startActivity(Intent, Bundle),调用Activity.startActivityForResult(Intent, int),调用FragmentActivity.startActivityForResult(Intent, int),调用Activity.startActivityForResult(Intent, int),调用Activity.startActivityForResult(Intent, int, Bundle),调用Instrumentation.execStartActivity(Context, IBinder, IBinder, Activity, Intent, int, Bundle),调用 IActivityManager.startActivity(IApplicationThread, String, Intent, String, IBinder, String, int, int, ProfilerInfo, Bundle)

最后一行的调用是远程进程调用,这意味着在您的应用进程中,该方法在代理 IActivityManager 实例上调用,该实例将其转发到另一个进程,在本例中为系统进程。

到目前为止,还没有进行 Intent 过滤。

在Android的系统进程中IActivityManager解析为ActivityManagerService并且:

ActivityManagerService.startivity(IApplicationThread, String, Intent, String, IBinder, String, int, int, ProfilerInfo, Bundle) 调用ActivityManagerService.startActivityAsUser(IApplicationThread, String, Intent, String, IBinder, String, int, int, ProfilerInfo, Bundle, int),调用ActivityStackSupervisor.startActivityMayWait(IApplicationThread, int, String, Intent, String, IVoiceInteractionSession, IVoiceInteractor, IBinder, String, int, int, ProfilerInfo, WaitResult, Configuration, Bundle, boolean, int, IActivityContainer, TaskRecord),调用ActivityStackSupervisor.resolveActivity(Intent, String, int, ProfilerInfo, int),调用IPackageManager.resolveIntent(Intent, String, int, int)

这是添加 MATCH_DEFAULT_ONLY 的地方,正如 nkalra0123 所说。

另外,这是另一个远程方法调用。 IPackageManager 被解析为 PackageManagerService,然后从那里变成这样:

PackageManagerService.resolveIntent(Intent, String, int, int) 调用PackageManagerService.queryIntentActivities(Intent, String, int, int),它尝试获取 Intent 包的所有活动。这会从你的包中获取 Activity,然后调用PackageService.ActivityIntentResolver.queryIntentForPackage(Intent, String, int, ArrayList&lt;PackageParser.Activity&gt;, int),它会获取你的包中的 IntentFilters,然后调用PackageService.ActivityIntentResolver.queryIntentFromList(Intent, String, boolean , ArrayList&lt;F[]&gt;, int),它调用IntentResolver.buildResolveList(...),它针对您的 Intent 中的数据运行它找到的所有 IntentFilter,考虑我们是否需要 CATEGORY_DEFAULT,并相应地将匹配的 IntentFilter 添加到列表中。

所有这些调用方法调用然后返回,最终某个对象会发现没有匹配的 IntentFilters。我在这里省略了,因为这是答案的相关部分。

【讨论】:

【参考方案6】:

您需要为所需的应用程序创建一个组件名称,例如:

Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
intent.setComponent(ComponentName.createRelative("com.whatsapp",".Main"));
intent.setPackage("com.whatsapp");

组件名代表你需要打开的activity,完整的包名,第二个参数是该包的类名。

【讨论】:

【参考方案7】:

让我在此添加一些其他信息。如here 所述,没有组件名称,意图是隐含的。

组件名称是可选的,但它是关键信息 这使意图明确,这意味着意图应该是 仅交付给组件名称定义的应用组件。 没有组件名称,意图是隐含的,系统 决定哪个组件应该根据另一个接收意图 意图信息(例如动作、数据和类别——描述 以下)。如果你需要在你的应用中启动一个特定的组件,你 应指定组件名称。

Context.startActivity 方法需要 DEFAULT 类别,以在未明确指定其组件名称时解析您的活动。查看this link 中的第一个示例。

希望这会有所帮助。

【讨论】:

以上是关于为啥没有找到处理 Intent 的 Activity?的主要内容,如果未能解决你的问题,请参考以下文章

为啥行为不同?- android:launchMode="singleTask" , android:taskAffinity="" 和 Intent.FLAG

没有找到处理 Intent 的活动:android.intent.action.EDIT

没有找到处理意图的活动:android.intent.action.VIEW

未找到Android活动异常:找不到处理Intent的活动

android activity之间传递对象 对象为啥要序列化

未找到处理 Intent 的活动 - android.intent.action.OPEN_DOCUMENT