6
votes

Pourquoi aucune activité n'est-elle trouvée pour gérer l'intention?

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 commentaires

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 avec intent.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. "


+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);


7 Réponses :


1
votes

"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


3 commentaires

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.



0
votes

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 ".

... sauf si ce n'est pas le cas

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 .


1 commentaires

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);



2
votes

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).


7 commentaires

+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 " 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 , mais plus de détails sont nécessaires.




1
votes

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 , int) , qui obtient les IntentFilters dans votre package, puis appelle
PackageService.ActivityIntentResolver.queryIntentFromList (Intent, String, boolean, ArrayList , int) , 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.


0 commentaires

0
votes

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.


0 commentaires

0
votes

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 .

J'espère que cela vous aidera.


0 commentaires