Au lieu de suivre la méthode habituelle getPackageManager (). getLaunchIntentForPackage ("com.example.app")
, je souhaite créer moi-même l'intention de lancement.
Intent intent = new Intent(Intent.ACTION_MAIN); intent.addCategory(Intent.CATEGORY_LAUNCHER); intent.setPackage("com.example.app"); startActivity(intent);
Pourquoi Android ne trouve-t-il pas l'activité, si com.example.app
est installé, activé et a un manifeste correct? (Cela fonctionne parfaitement avec getLaunchIntentForPackage
.)
7 Réponses :
"Pour recevoir des intentions implicites, vous devez inclure la catégorie CATEGORY_DEFAULT dans le filtre d'intention." - Votre application de réception l'a-t-elle?
Exemple:
PackageManager packageManager = getPackageManager(); List<ResolveInfo> activities = packageManager.queryIntentActivities(intent,PackageManager.MATCH_DEFAULT_ONLY); boolean isIntentSafe = activities.size() > 0;
Extrait de: https://developer.android.com/guide/components/intents-filters# Réception
Vous pouvez également vérifier qu'une activité peut recevoir votre diffusion:
<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>
Extrait de: https://developer.android.com/training/basics/intents/sending# java
Ce n'est pas une intention implicite. Il est explicite, car le nom du package est spécifié à l'aide de intent.setPackage ("com.example.app")
. " Les intentions explicites spécifient quelle application satisfera l'intention, en fournissant soit le nom du package de l'application cible, soit un nom de classe de composant complet. " - source
L'application réceptrice n'a pas la catégorie DEFAULT
dans son filtre d'intention LAUNCHER
, mais ailleurs.
Si j'essaie une autre application de récepteur qui a la catégorie DEFAULT
, cela commence parfaitement. Mais DEFAULT
ne devrait pas être nécessaire, car mon intention est explicite. Si je peux trouver la bonne activité en consultant le manifeste de l'application de réception, pourquoi Android ne peut-il pas le faire pour moi? Il connaît également la catégorie, l'action et le nom du package. Si getLaunchIntentForPackage
fonctionne parfaitement sans DEFAULT
, mon approche devrait également fonctionner.
startActivity
traite tous les intentions comme si elles déclaraient CATEGORY_DEFAULT
Même si vous n'avez pas intent.addCategory (Intent.CATEGORY_DEFAULT);
dans votre code.
Même si vous ajoutez intent.removeCategory(Intent.CATEGORY_DEFAULT);
.
Même si votre intention est explicite *: intent.setPackage ("com.example.app");
.
* Fournit " soit le nom du package de l'application cible, soit un nom de classe de composant complet ".
Le système ne recherchera pas CATEGORY_DEFAULT
si vous définissez le nom de classe de l'activité cible: p>
Intent intent = new Intent(Intent.ACTION_MAIN); intent.addCategory(Intent.CATEGORY_LAUNCHER); intent.setClassName("com.example.app", "com.example.app.NameOfTheActivityToBeStarted"); startActivity(intent);
Source de l'en-tête: la note bleue sur la page de l'élément
Source de la définition d'un intent explicite: developer.android.com .
Le système trouvera l'activité même si l'intention n'a pas d'action ou de catégorie, comme: Intent intent = new Intent (); intent.setClassName ("com.example.app", "com.example.app.NameOfTheActivityToBeStarted"); startActivity (intention);
Je comprends que vous essayez de démarrer l'activité de lancement d'une application connue avec un nom de package connu ( com.example.app
). Je suppose que vous avez des informations sur l'application. Ainsi, vous pouvez le démarrer via une intention explicite comme ceci:
Intent intent = new Intent(); intent.setComponent(new ComponentName("com.example.app", "com.example.app.MainActivity")); if(intent.resolveActivity(getPackageManager()) != null) { startActivity(intent); }
EDIT: Faire une analyse des deux objets d'intention ( intent1
== votre propre intention VS intent2
== intent créé à partir de getLaunchIntentForPackage ()
), la différence est
intent1:
{act = android.intent.action.MAIN cat = [android.intent.category.LAUNCHER] pkg = com.example.app}
intent2:
{act = android.intent.action.MAIN cat = [android.intent.category.LAUNCHER] flg = 0x10000000 pkg = com.example.app cmp = com.example.app / .MainActivity}
Je vais devoir croire que ce que vous avez fait pour créer votre propre objet d'intention n'est pas suffisant pour que l'intention explicite fonctionne. Vous devrez fournir à Android plus d'informations sur votre intention, par exemple en précisant le nom du composant (comme indiqué dans ma réponse ci-dessus).
+1 pour votre aide, vous comprenez l ' objectif de mon exemple de code , et resolutionActivity
me conduit à sa documentation pleine de détails utiles. Mais votre réponse ne concerne pas l ' objectif de ma question : mieux comprendre pourquoi l'activité est introuvable. Ma propre réponse a déjà eu une solution de contournement en utilisant setClassName
(identique à votre setComponent < / code>, juste plus pratique). Vous avez eu une bonne idée dans votre commentaire (en comparant les intentions), mais votre réponse actuellement ne contient pas de nouveaux détails, juste un sous-ensemble des informations connues de ma réponse.
(Au fait, il y a un commentaire juste au-dessus du vôtre commençant par a> " J'ai envisagé d'utiliser intent.setComponent (...), mais ", qui est pertinent pour cette réponse.)
Merci. Je reconnais que ce n'est pas la réponse à votre objectif. Je n'ai pas assez de temps pour le moment alors je vous ai donné ce qui me passait par la tête. Je vous répondrai plus tard aujourd'hui. À votre santé!
J'ai mis à jour ma réponse. En regardant votre intention explicite VS intent créée par getLaunchIntentForPackage (), il semble que vous manquiez des valeurs pour que votre intention fonctionne. setClassName () ou setComponent () doit être utilisé pour compléter les informations sur votre intention.
Utile pour voir les différences: l'indicateur 0x10000000 ( Intent.FLAG_ACTIVITY_NEW_TASK
) et le nom du composant. Cela prouve que le système doit trouver la bonne activité dans les deux cas. Mais ce n'est pas le cas, donc la question demeure: pourquoi. L'activité existe, même un enfant pourrait la trouver (connaissant l'action, la catégorie et le package). Le système cherche-t-il ce que je lui dis de rechercher? Nous avons déjà la réponse: non, il recherche également la catégorie par défaut . Mais on ne sait toujours pas pourquoi. Pourquoi rechercher la catégorie par défaut en cas d'intention explicite?
en utilisant setComponent () comme ci-dessus, il crée un intent avec les informations suivantes uniquement: {cmp = com.example.app / .MainActivity} et il trouve l'activité correcte et il n'utilise pas CATEGORY_DEFAULT
setComponent
fonctionne. C'est clair, je suis d'accord avec vous. ( setClassName
fonctionne aussi.) Mais au lieu de trouver une méthode de travail, mon objectif dans un premier temps est de découvrir pourquoi l'exemple de code de ma question ne fonctionne pas. Pourquoi un nom de composant est-il requis? Le nom du package devrait suffire, car il rend l'intention explicite. J'ai une réponse à ce sujet a > , mais plus de détails sont nécessaires.
Ceci est la fonction où android.content.Intent # CATEGORY_DEFAULT est ajouté à tout le code 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;
C'est le code à partir duquel tout commence http: // androidxref. com / 7.1.2_r36 / xref / frameworks / base / core / java / android / app / ContextImpl.java # 766
+1. Ce serait formidable de voir le code qui (s'exécute après l'appel de startActivity
et) ignore les indicateurs lorsque le composant n'est pas nul (ce qui rend le setComponent
et setClassName < / code> fonctionnent au lieu de l'approche insuffisante de
setPackage
). Je suppose que ce sera la même logique que décrit ici , mais je ne suis pas sûr et je ne trouve pas le code. Nous sommes vraiment proches maintenant de bien comprendre le comportement de la plateforme, au niveau du code.
Vous avez demandé à voir le code exécuté après startActivity
et le voici.
Dans votre application:
Activity.startActivity (Intent)
appels
Activity.startActivity (Intent, Bundle)
, qui appelle
Activity.startActivityForResult (Intent, int)
, qui appelle
FragmentActivity.startActivityForResult (Intent, int)
, qui appelle
Activity.startActivityForResult (Intent, int)
, qui appelle
Activity.startActivityForResult (Intent, int, Bundle)
, qui appelle
Instrumentation.execStartActivity (Context, IBinder, IBinder, Activity, Intent, int, Bundle)
, qui appelle
IActivityManager.startActivity (IApplicationThread, String, Intent, String, IBinder, String, int, int, ProfilerInfo, Bundle)
L'appel sur la dernière ligne est un appel de processus distant, ce qui signifie que dans votre processus d'application, une méthode est appelée sur une instance proxy IActivityManager
qui la transmet à un autre processus, dans ce cas un système processus.
Jusqu'à présent, aucun filtrage d'intention n'a eu lieu.
Dans le processus système d'Android, IActivityManager
a été résolu en ActivityManagerService
et:
ActivityManagerService.startivity (IApplicationThread, String, Intent, String, IBinder, String, int, int, ProfilerInfo, Bundle)
appelle
ActivityManagerService.startActivityAsUser (IApplicationThread, String, Intent, String, IBinder, String, int, int, ProfilerInfo, Bundle, int)
, qui appelle
ActivityStackSupervisor.startActivityMayWait (IApplicationThread, int, String, Intent, String, IVoiceInteractionSession, IVoiceInteractor, IBinder, String, int, int, ProfilerInfo, WaitResult, Configuration, Bundle, boolean, int, IActivityContainer, TaskRecord)
qui appelle
ActivityStackSupervisor.resolveActivity (Intent, String, int, ProfilerInfo, int)
, qui appelle
IPackageManager.resolveIntent (Intent, String, int, int)
C'est ici que MATCH_DEFAULT_ONLY est ajouté , comme l'a dit nkalra0123.
De plus, c'est une autre invocation de méthode distante. IPackageManager
est résolu en PackageManagerService
, et à partir de là, cela se passe comme suit:
PackageManagerService.resolveIntent (Intent, String, int, int)
appelle
PackageManagerService.queryIntentActivities (Intent, String, int, int)
, qui tente d'obtenir toutes les activités pour le package Intent. Cette action obtient les Activités de votre forfait, puis appelle
PackageService.ActivityIntentResolver.queryIntentForPackage (Intent, String, int, ArrayList
, qui obtient les IntentFilters dans votre package, puis appelle
PackageService.ActivityIntentResolver.queryIntentFromList (Intent, String, boolean, ArrayList
, qui appelle
IntentResolver.buildResolveList (...)
, qui exécute tous les IntentFilters trouvés par rapport aux données de votre Intent, en prenant en compte si nous avons besoin ou non de CATEGORY_DEFAULT
, et en ajoutant le faire correspondre les IntentFilters à une liste en conséquence.
Tous ces appels de méthode d'appel puis retournent et finalement un objet quelque part découvrira qu'il n'y avait aucun IntentFilters correspondant. J'omets cela ici car c'est la partie pertinente de la réponse.
Vous devez créer un nom de composant pour l'application requise, tel que:
Intent intent = new Intent(Intent.ACTION_MAIN); intent.addCategory(Intent.CATEGORY_LAUNCHER); intent.setComponent(ComponentName.createRelative("com.whatsapp",".Main")); intent.setPackage("com.whatsapp");
Le nom du composant représente l'activité que vous devez ouvrir, le nom complet du package et le deuxième paramètre est la classe nom de ce package.
Permettez-moi d'ajouter quelques informations supplémentaires à ce sujet. comme décrit ici , sans nom de composant, l'intention est implicite.
Le nom du composant est facultatif, mais il s'agit de l'information essentielle qui rend une intention explicite, ce qui signifie que l'intention doit être livré uniquement au composant d'application défini par le nom du composant. Sans nom de composant, l'intention est implicite et le système décide quel composant doit recevoir l'intention en fonction de l'autre informations d'intention (telles que l'action, les données et la catégorie - décrites au dessous de). Si vous devez démarrer un composant spécifique dans votre application, vous doit spécifier le nom du composant.
La catégorie DEFAULT
est requise pour Context.startActivity pour résoudre votre activité lorsque le nom de son composant n'est pas spécifié explicitement. Vérifiez le premier exemple dans ce lien . P >
J'espère que cela vous aidera.
com.example.app vérifiez cela avec package = "com.example ...." dans le fichier manifeste.
veuillez visiter: stackoverflow.com/a/30617251/6672577
@Opriday Le fichier manifeste de
com.example.app
est correct, il contient le bon nom de package (com.example.app
). C'est le même nom de package que j'essaie d'utiliser avecintent.setPackage ("com.example.app");
. Pas de faute de frappe.@Opriday J'ai visité votre lien, mais je n'y trouve rien de pertinent. Quelle information dois-je rechercher?
J'ai envisagé d'utiliser
intent.setComponent (
...)
, mais cela ne devrait pas être nécessaire selon la documentation: " (généralement facultatif) Défini explicitement le composant pour gérer l'intention. S'il reste avec la valeur par défaut null, le système déterminera la classe appropriée à utiliser en fonction des autres champs (action, données, type, catégories) de l'intention. (.. .) Vous ne devez définir cette valeur que lorsque vous savez que vous voulez absolument qu'une classe spécifique soit utilisée; sinon, il est préférable de laisser le système trouver la classe appropriée afin que vous respectiez les applications installées et les préférences de l'utilisateur. i> "+1. C'est une bonne question en fait. Nous nous demandons quelle est la différence entre votre intention et l'intention créée par getLaunchIntentForPackage (). Essayez Log.d (TAG, intent.toString () + "vs" + intent2.toString ()). (J'ai ajouté ma solution de contournement comme réponse.)
supprimez cette ligne: intent.addCategory (Intent.CATEGORY_LAUNCHER);