Appel de startActivity () depuis l'extérieur d'un contexte d'activité

368

J'ai implémenté un ListViewdans mon application Android. Je me lie à cela en ListViewutilisant une sous-classe personnalisée de la ArrayAdapterclasse. Dans la ArrayAdapter.getView(...)méthode redéfinie , j'attribue un OnClickListener. Dans la onClickméthode du OnClickListener, je veux lancer une nouvelle activité. Je reçois l'exception:

Calling startActivity() from outside of an Activity  context requires the  
FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?

Comment puis-je obtenir le sous Contextlequel le ListView(le courant Activity) fonctionne?

Sako73
la source
1
Je pense que la réponse d'Alex devrait être la solution «acceptée» à votre problème, car elle corrige l'erreur que vous avez mentionnée de manière plus générique
devanshu_kaushik
10
J'adore ça "Est-ce vraiment ce que tu veux?" ... J'ai déjà eu un message qui disait "Etes-vous sûr que vous n'avez pas oublié de désenregistrer un récepteur de diffusion quelque part?" IMPRESSIONNANT! Chapeau à tous ceux qui ont mis tous ces petits messages pour nous aider.
Nerdy Bunz
1
J'ai rencontré ce problème. quand j'ai mis à jour targetSdkVersion à 28.
illusionJJ

Réponses:

575

Soit

  • mettre en cache l'objet Context via le constructeur dans votre adaptateur, ou
  • obtenez-le de votre vue.

Ou en dernier recours,

  • ajoutez - FLAG_ACTIVITY_NEW_TASK à votre intention:

_

myIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

Modifier - j'éviterais de définir des indicateurs car cela interférerait avec le flux normal des événements et de la pile d'historique.

Alex Volovoy
la source
6
Qu'en est-il de la fonction autoLink de TextView où je ne peux pas contrôler l'intention (et donc les indicateurs) créés par le système?
Alex Semeniuk
75
Je recevais cette exception quand je faisais quelque chose comme ça , context.startActivity(intent);je viens de changer contextde ApplicationContextpour le Activitytype. Cela a résolu le problème.
Sufian
@AlexSemeniuk a-t-il déjà trouvé une solution?
@AlexSemeniuk - autoLink fonctionnera tant que vous passerez l'activité comme contexte à l'adaptateur
Georges
J'ai passé l'objet Context via le constructeur mais cela ne fonctionne pas. mais FLAG_ACTIVITY_NEW_TASK fonctionne très bien pour moi merci.
Hiren
100

Vous pouvez y parvenir avec addFlags au lieu desetFlags

myIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

Selon la documentation, il fait:

Ajoutez des indicateurs supplémentaires à l'intention (ou avec la valeur d'indicateurs existante).


ÉDITER

Soyez conscient si vous utilisez des indicateurs que vous modifiez la pile d'historique comme le dit la réponse d'Alex Volovoy :

... évitez de définir des indicateurs car cela interfère avec le flux normal des événements et la pile d'historique.

Bruno Bieri
la source
1
J'ai un problème très similaire. Avez-vous rencontré des problèmes avec la pile d'historique ou autre chose comme les réponses ci-dessus le suggèrent?
Einar Sundgren
1
Je ne sais pas exactement ce que vous recherchez, mais vous pouvez démarrer une activité sans historique comme celui-ci: Intention intention = nouvelle intention (Intent.ACTION_VIEW, "http: \\ www.google.com")); intention. addFlags (Intent.FLAG_ACTIVITY_NO_HISTORY); startActivity (intention);
Bruno Bieri
Pourquoi n'est-il pas recommandé d'ajouter des drapeaux ici? Dans quelle mesure est-il essentiel d'interférer avec le flux normal d'événements et la pile d'historique?
Jason Krs
@JasonKrs, vous pouvez utiliser addFlags. Sachez simplement que vous pouvez modifier la pile d'historique en fonction de l'indicateur que vous ajoutez. Le FLAG_ACTIVITY_NEW_TASK peut être utilisé dans cette situation. Pour plus de détails, lisez: developer.android.com/reference/android/content/…
Bruno Bieri
64

Au lieu d'utiliser (getApplicationContext)useYourActivity.this

Jeffrey Nyauke
la source
Cela a fonctionné pour moi, c'est-à-dire en utilisant activity.startActivityau lieu decontext.startActivity
Phani Rithvij
LA MEILLEURE RÉPONSE!
amirhossein
Cela fonctionne pour moi.
user5722540
40

Si vous avez une erreur en raison de l'utilisation de l'outil de création de sélecteur comme ci-dessous:

Intent sharingIntent = new Intent(Intent.ACTION_VIEW);
sharingIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
sharingIntent.setData(Uri.parse("http://google.com"));
startActivity(Intent.createChooser(sharingIntent, "Open With"));

Réglez le drapeau pour créer un sélecteur comme celui-ci:

Intent sharingIntent = new Intent(Intent.ACTION_VIEW);
sharingIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
sharingIntent.setData(Uri.parse("http://google.com"));

Intent chooserIntent = Intent.createChooser(sharingIntent, "Open With");
chooserIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

startActivity(chooserIntent);
sanath_p
la source
4
C'était très utile. L'intention de choisir exactement devrait avoir ce drapeau!
Mahdi
2
C'est la bonne solution, et exactement ce qu'il faut faire, new_task dans intent.chooser
Rafael Guimarães
15

De plus: si vous affichez des liens dans listview en fragment , ne le créez pas comme ceci

adapter = new ListAdapter(getActivity().getApplicationContext(),mStrings);

appeler à la place

adapter = new ListAdapter(getActivity(),mStrings);

l'adaptateur fonctionne bien dans les deux cas, mais les liens ne fonctionnent que dans le dernier.

djdance
la source
@ user2676468: cela a résolu le problème de liaison automatique pour moi.
Head Geek
Cela devrait être une réponse acceptée, au lieu d'utiliser des drapeaux, c'est mieux !!
Gastón Saillén
@ GastónSaillén, je n'utilise pas getApplicationContext()(sauf l'initialisation de l'application), mais j'ai pris cette exception. Les situations peuvent donc être différentes.
CoolMind
C'était mon problème, j'ai utilisé getApplicationContext () pour le contexte. La définition du thiscontexte fonctionne en ce qui concerne l'activité actuelle.
Brlja
14

Je pense que vous implémentez peut-être OnClickListener au mauvais endroit - généralement, vous devriez certainement implémenter un OnItemClickListener dans votre activité et le placer à la place sur ListView, ou vous aurez des problèmes avec vos événements ...

mreichelt
la source
2
Vous m'amenez à la solution. J'avais besoin d'utiliser un OnItemClickListener, affecté à ListView. Voici quelques liens pour quiconque: developer.android.com/reference/android/widget/… androidpeople.com/… Merci pour l'aide.
Sako73
Veuillez fournir des réponses génériques. La réponse d'Alex Volovoy ci-dessous résout le problème de manière générique.
devanshu_kaushik
Pour la postérité: si vous le définissez directement comme setListener (nouveau Listener) sur un composant nécessite un contexte, vous créez une référence implicite à l'activité entière qui fuira de la mémoire comme vous ne le croiriez pas. Cela peut être contourné en créant un écouteur de classe interne statique ou en déplaçant l'écouteur vers une classe distincte s'il doit pouvoir gérer les entrées de plusieurs origines.
G_V
9
CustomAdapter mAdapter = new CustomAdapter( getApplicationContext(), yourlist);

ou

Context mContext = getAppliactionContext();
CustomAdapter mAdapter = new CustomAdapter( mContext, yourlist);

changer en dessous

CustomAdapter mAdapter = new CustomAdapter( this, yourlist);
Murtaza Ashraf
la source
8

Au Android 28(Android P)départActivité

if ((intent.getFlags() & Intent.FLAG_ACTIVITY_NEW_TASK) == 0
        && (targetSdkVersion < Build.VERSION_CODES.N
                || targetSdkVersion >= Build.VERSION_CODES.P)
        && (options == null
                || ActivityOptions.fromBundle(options).getLaunchTaskId() == -1)) {
    throw new AndroidRuntimeException(
            "Calling startActivity() from outside of an Activity "
                    + " context requires the FLAG_ACTIVITY_NEW_TASK flag."
                    + " Is this really what you want?");
}

Donc, la meilleure façon est d'ajouter FLAG_ACTIVITY_NEW_TASK

Intent intent = new Intent(context, XXXActivity.class);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
context.startActivity(intent);
Alen Lee
la source
Ceci est requis pour les appareils 28 et supérieurs.
Md Mohsin du
7

Voir, si vous créez une intention dans un listiner dans une méthode

override onClick (View v).

puis appelez également le contexte à travers cette vue:

v.getContext ()

Il n'y aura même pas besoin de SetFlags ...

Paulo Linhares - Packapps
la source
Et quelle était la mauvaise situation? v.getApplicationContext ()?
CoolMind
3

Pour toute personne obtenant cela sur Xamarin.Android (MonoDroid) même lorsque StartActivity est appelé à partir d'une activité - il s'agit en fait d'un bogue Xamarin avec un nouveau runtime ART, voir https://bugzilla.xamarin.com/show_bug.cgi?id=17630

rouen
la source
Oui, il suffit de faire ce qui a été décrit ci-dessus, mais le libellé a changé ... intent.SetFlags (ActivityFlags.NewTask);
Luke Alderton
3

Élaborer un peu plus la réponse d'Alex Volovoy -

au cas où vous auriez ce problème avec des fragments, getActivity () fonctionne bien pour obtenir le contexte

Dans d'autres cas:

Si vous ne voulez pas utiliser-

myIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);//not recommend

puis faites une fonction comme celle-ci dans votre OutsideClass -

public void gettingContext(Context context){
    real_context = context;//where real_context is a global variable of type Context
}

Maintenant, dans votre activité principale, lorsque vous créez un nouvel OutsideClass, appelez la méthode ci-dessus immédiatement après avoir défini le OutsideClass en donnant le contexte de l'activité comme argument. Toujours dans votre activité principale, faites un

public void startNewActivity(final String activity_to_start) {
    if(activity_to_start.equals("ACTIVITY_KEY"));
    //ACTIVITY_KEY-is a custom key,just to
    //differentiate different activities
    Intent i = new Intent(MainActivity.this, ActivityToStartName.class);
    activity_context.startActivity(i);      
}//you can make a if-else ladder or use switch-case

maintenant revenez à votre OutsideClass, et pour commencer une nouvelle activité, faites quelque chose comme ça-

@Override
public void onClick(View v) {
........
case R.id.any_button:

            MainActivity mainAct = (MainActivity) real_context;             
            mainAct.startNewActivity("ACTIVITY_KEY");                   

        break;
    }
........
}

De cette façon, vous pourrez démarrer différentes activités appelées à partir de différents OutsideClass sans gâcher les drapeaux.

Remarque - Essayez de ne pas mettre en cache l'objet contextuel via le constructeur pour le fragment (avec l'adaptateur, c'est bien). Un fragment doit avoir un constructeur vide sinon l'application se bloque dans certains scénarios.

n'oubliez pas d'appeler

OutsideClass.gettingContext(Context context);

dans la fonction onResume () également.

Flying Monkey
la source
3

Cette erreur survient lorsque startactivity ne sait pas quelle est son activité. Vous devez donc ajouter une activité avant startActivity ()

vous devez définir

context.startActivity(yourIntent);
Cabezas
la source
Si vous appelez startActivitydepuis Fragment, un appelant peut souvent être un fragment, pas une activité.
CoolMind
2

À mon avis, il vaut mieux utiliser la méthode de startActivity()juste dans votre code du Activity.class. Si vous l'utilisez dans la Adapterou une autre classe, cela se traduira par cela.

kaosmys
la source
2

J'ai aussi eu le même problème. Vérifiez tout le contexte que vous avez passé. Pour les « liens », il faut un contexte d'activité et non un contexte d'application .

C'est l'endroit où vous devez vérifier:

1.) Si vous avez utilisé LayoutInflater, vérifiez quel contexte vous avez passé.

2.) Si vous utilisez un adaptateur, vérifiez quel contexte vous avez passé.

codemaniac
la source
2

J'ai eu le même problème. Le problème vient du contexte. Si vous souhaitez ouvrir des liens (par exemple partager n'importe quel lien via le sélecteur), passez le contexte d'activité, pas le contexte d'application.

N'oubliez pas d'ajouter myIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)si vous n'êtes pas dans votre activité.

Coder29
la source
2

Utilisez ce code dans votre Adapter_Activity et utilisez context.startActivity(intent_Object)etintent_Object.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

Comme ça:

Intent n_act = new Intent(context, N_Activity.class);
n_act.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(n_act);

Ça marche....

Gaurav Lambole
la source
1
Intent viewIntent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);    
viewIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);    
startActivity(viewIntent);   

j'espère que cela fonctionnera.

Chirag Patel
la source
1

Face au même problème, puis mis en œuvre

intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

et a résolu le problème.

Il peut y avoir une autre raison liée à l'adaptateur d'affichage de liste.
Vous pouvez voir ce blog , très bien décrit.

Mayank Sharma
la source
blog utile, merci. :)
Rucha Bhatt Joshi
1

Utilisez ce code. Fonctionne bien pour moi. Partager quelque chose de l'extérieur d'une activité:

Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType("text/plain");

// Append Text
String Text = "Your Text Here"

intent.putExtra(Intent.EXTRA_TEXT, Text);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

Intent shareIntent = Intent.createChooser(intent,"Share . . . ");
shareIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
G.context.getApplicationContext().startActivity(shareIntent);
Pooya Hayati
la source
La configuration des drapeaux gâche l'historique de stacktrace
Ezio
1

Depuis l'ajout de drapeaux event_flow, stack_historyil est préférable de passer le «contexte d'application» à la non-activité d'où vous devez appeler une classe d'activité de la manière suivante:

"ActivityClassName.this" (pendant que vous passez le contexte de cette manière, il contiendra tous les détails et informations dont vous avez besoin pour appeler une activité à partir d'un scénario de non-activité)

Il n'est donc pas nécessaire de définir ou d'ajouter des indicateurs, cela fonctionnera bien dans tous les cas.

Musthafa
la source
0
Intent i= new Intent(context, NextActivity.class);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
Chandan Lal
la source
0

Si vous invoquez le partage d'intention dans le plug-in Cordova, la définition du drapeau n'aidera pas. Utilisez plutôt ceci -

cordova.getActivity().startActivity(Intent.createChooser(shareIntent, "title"));
Sandeep Kumar
la source
0

Ma situation était un peu différente, je teste mon application en utilisant Espressoet j'ai dû lancer mon Activity avec ActivityTestRuledepuis l'instrumentation Context(qui n'est pas celle qui vient d'un Activity).

fun intent(context: Context) = 
    Intent(context, HomeActivity::class.java)
        .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)

J'ai dû changer les drapeaux et ajouter un orbit à bit ( |en Java) avecIntent.FLAG_ACTIVITY_NEW_TASK

Il en résulte donc:

fun intent(context: Context) = 
    Intent(context, HomeActivity::class.java)
        .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
Rafael Ruiz Muñoz
la source
0

Version Kotlin

val intent = Intent(Intent.ACTION_EDIT, ContactsContract.Profile.CONTENT_URI)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
this.startActivity(intent)
Sazzad Hissain Khan
la source