Comment gérer startActivityForResult sur Android?

969

Dans mon activité, j'appelle une deuxième activité de l'activité principale par startActivityForResult. Dans ma deuxième activité, il existe certaines méthodes qui terminent cette activité (peut-être sans résultat), mais une seule d'entre elles renvoie un résultat.

Par exemple, à partir de l'activité principale, j'appelle une deuxième. Dans cette activité, je vérifie certaines fonctionnalités du combiné, telles que l'appareil photo. Si ce n'est pas le cas, je ferme cette activité. De plus, lors de la préparation MediaRecorderou en MediaPlayercas de problème, je terminerai cette activité.

Si son appareil dispose d'une caméra et que l'enregistrement est terminé, après l'enregistrement d'une vidéo, si un utilisateur clique sur le bouton Terminé, je renvoie le résultat (adresse de la vidéo enregistrée) à l'activité principale.

Comment vérifier le résultat de l'activité principale?

Hesam
la source

Réponses:

2447

Depuis votre FirstActivityappel, la méthode SecondActivityusing startActivityForResult()

Par exemple:

int LAUNCH_SECOND_ACTIVITY = 1
Intent i = new Intent(this, SecondActivity.class);
startActivityForResult(i, LAUNCH_SECOND_ACTIVITY);

Dans votre SecondActivityensemble, les données auxquelles vous souhaitez revenir FirstActivity. Si vous ne voulez pas revenir en arrière, n'en définissez pas.

Par exemple: SecondActivitySi vous souhaitez renvoyer des données:

Intent returnIntent = new Intent();
returnIntent.putExtra("result",result);
setResult(Activity.RESULT_OK,returnIntent);
finish();

Si vous ne souhaitez pas renvoyer de données:

Intent returnIntent = new Intent();
setResult(Activity.RESULT_CANCELED, returnIntent);
finish();

Maintenant, dans votre FirstActivityclasse, écrivez le code suivant pour la onActivityResult()méthode.

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    if (requestCode == LAUNCH_SECOND_ACTIVITY) {
        if(resultCode == Activity.RESULT_OK){
            String result=data.getStringExtra("result");
        }
        if (resultCode == Activity.RESULT_CANCELED) {
            //Write your code if there's no result
        }
    }
}//onActivityResult

Pour mettre en œuvre le transfert de données entre deux activités de manière bien meilleure dans Kotlin, veuillez consulter ce lien « Une meilleure façon de transmettre des données entre les activités »

Nishant
la source
1
Quel est le but de mettre une intention lorsque RESUT_CANCELLED dans setResult (RESULT_CANCELED, returnIntent);
Ismail Sahin
4
@ismail Supposons que, dans SecondActivityune exception, une exception se soit produite, dans ce cas, vous devez également renvoyer le résultat à la FirstActivity, afin que vous puissiez définir le résultat comme "RESULT_CANCELLED"dans le bloc catch et revenir à FirstActivtyet dans et FirstActivity's' 'onActivityResult()vous pouvez vérifier si vous avez obtenu le résultat de réussite ou d'échec.
Nishant
10
C'est donc à vous, si vous n'avez pas besoin de connaître la raison de l'annulation, vous pouvez utiliser simplement setResult (RESULT_CANCELED); sans aucune intention
Ismail Sahin
2
@Lei Leyba Aucune finition () n'est appelée après l'appel de startActivityForResult (). La première Actvity passe à l'état de pause.
Nishant
6
Pour moi ça ne marche pas - c'est ce que je déteste tellement sur Android - ce système est si peu fiable: - /
Martin Pfeffer
50

Comment vérifier le résultat de l'activité principale?

Vous devez remplacer Activity.onActivityResult()puis vérifier ses paramètres:

  • requestCodeidentifie l'application qui a renvoyé ces résultats. Ceci est défini par vous lorsque vous appelez startActivityForResult().
  • resultCode vous informe si cette application a réussi, échoué ou quelque chose de différent
  • datadétient toutes les informations retournées par cette application. C'est possible null.
Sam
la source
Cela signifie que le requestCode n'est utilisé que dans la première activité, et il n'est jamais utilisé pour la 2e activité? Si la 2e activité a des approches différentes, cela changerait, mais en fonction des extras d'intention et non pas du requestCode, non? Edit: Oui, stackoverflow.com/questions/5104269/…
JCarlosR
44

En complément de la réponse de @ Nishant, la meilleure façon de renvoyer le résultat de l'activité est:

Intent returnIntent = getIntent();
returnIntent.putExtra("result",result);
setResult(RESULT_OK,returnIntent);
finish();

J'avais des problèmes avec

new Intent();

Ensuite, j'ai découvert que la bonne façon utilise

getIntent();

pour obtenir l'intention actuelle

Julian Alberto
la source
Il semble un peu étrange de créer un nouveau Intentqui n'existe que pour contenir un Bundleet qui n'a pas les valeurs normales comme une action ou un composant. Mais il semble également un peu étrange (et potentiellement dangereux?) De modifier celui Intentqui a été utilisé pour lancer l'activité en cours. J'ai donc recherché la source pour Android lui-même et j'ai constaté qu'ils en créaient toujours un nouveau Intentà utiliser. Par exemple, github.com/aosp-mirror/platform_frameworks_base/blob/…
spaaarky21
Bonjour spaaarky21, merci pour ton commentaire. Je suis désolé de ne pas avoir été aussi clair en expliquant exactement comment je me suis retrouvé avec cette solution. C'était il y a trois ans, et je me souviens seulement que mon application plantait à cause de "nouvelle intention", c'est ce que je voulais dire quand j'ai dit "j'avais un problème avec". En fait, j'ai juste essayé avec "getIntent", car cela avait du sens à l'époque, et cela a fonctionné! À cause de cela, j'ai décidé de partager ma solution. Ce n'est peut-être pas le meilleur choix de mots pour dire «meilleure façon» ou «bonne façon», mais je maintiens ma solution. C'est ce qui a résolu mon problème et aussi vis-à-vis des autres. Merci
Julian Alberto
1
Hou la la! fonctionne très bien. getIntent()semblent être un moyen idéal pour renvoyer des données à une activité inconnue, d'où l'activité a été appelée. Merci!
sam
43

Exemple

Pour voir l'ensemble du processus dans son contexte, voici une réponse supplémentaire. Voir ma réponse plus complète pour plus d'explications.

entrez la description de l'image ici

MainActivity.java

public class MainActivity extends AppCompatActivity {

    // Add a different request code for every activity you are starting from here
    private static final int SECOND_ACTIVITY_REQUEST_CODE = 0;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    // "Go to Second Activity" button click
    public void onButtonClick(View view) {

        // Start the SecondActivity
        Intent intent = new Intent(this, SecondActivity.class);
        startActivityForResult(intent, SECOND_ACTIVITY_REQUEST_CODE);
    }

    // This method is called when the second activity finishes
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        // check that it is the SecondActivity with an OK result
        if (requestCode == SECOND_ACTIVITY_REQUEST_CODE) {
            if (resultCode == RESULT_OK) { // Activity.RESULT_OK

                // get String data from Intent
                String returnString = data.getStringExtra("keyName");

                // set text view with string
                TextView textView = (TextView) findViewById(R.id.textView);
                textView.setText(returnString);
            }
        }
    }
}

SecondActivity.java

public class SecondActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
    }

    // "Send text back" button click
    public void onButtonClick(View view) {

        // get the text from the EditText
        EditText editText = (EditText) findViewById(R.id.editText);
        String stringToPassBack = editText.getText().toString();

        // put the String to pass back into an Intent and close this activity
        Intent intent = new Intent();
        intent.putExtra("keyName", stringToPassBack);
        setResult(RESULT_OK, intent);
        finish();
    }
}
Suragch
la source
Cela peut-il être fait par deux applications A et B différentes? stackoverflow.com/questions/52975645/…
Jerry Abraham
12

Pour ceux qui ont un problème avec un mauvais requestCode dans onActivityResult

Si vous appelez startActivityForResult()depuis votre Fragment, le requestCode est modifié par l'activité qui possède le fragment.

Si vous voulez obtenir le bon code de résultat dans votre activité, essayez ceci:

Changement:

startActivityForResult(intent, 1); À:

getActivity().startActivityForResult(intent, 1);

Tomasz Mularczyk
la source
10

Si vous souhaitez mettre à jour l'interface utilisateur avec le résultat de l'activité, vous ne pouvez pas utiliser. En this.runOnUiThread(new Runnable() {} procédant ainsi, l'interface utilisateur ne sera pas actualisée avec une nouvelle valeur. Au lieu de cela, vous pouvez le faire:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    if (resultCode == RESULT_CANCELED) {
        return;
    }

    global_lat = data.getDoubleExtra("LATITUDE", 0);
    global_lng = data.getDoubleExtra("LONGITUDE", 0);
    new_latlng = true;
}

@Override
protected void onResume() {
    super.onResume();

    if(new_latlng)
    {
        PhysicalTagProperties.this.setLocation(global_lat, global_lng);
        new_latlng=false;
    }
}

Cela semble idiot mais fonctionne plutôt bien.

DaviF
la source
2

D'abord, vous utilisez startActivityForResult()avec les paramètres en premier Activityet si vous souhaitez envoyer des données du deuxième Activityau premier, Activitypassez la valeur à l'aide Intentde la setResult()méthode et récupérez ces données à l'intérieur de la onActivityResult()méthode en premier Activity.

Dharmendra Pratap
la source
1

Problème très courant dans Android
Il peut être décomposé en 3 morceaux
1) démarrer l'activité B (se produit dans l'activité A)
2) Définir les données demandées (se produit dans l'activité B)
3) Recevoir les données demandées (se produit dans l'activité A)

1) startActivity B

Intent i = new Intent(A.this, B.class);
startActivity(i);

2) Définir les données demandées

Dans cette partie, vous décidez si vous souhaitez renvoyer des données ou non lorsqu'un événement particulier se produit.
Par exemple: Dans l'activité B, il y a un EditText et deux boutons b1, b2.
Cliquer sur le bouton b1 renvoie les données à l'activité A
Cliquer sur le bouton b2 n'envoie aucune donnée.

Envoi de données

b1......clickListener
{
   Intent resultIntent = new Intent();
   resultIntent.putExtra("Your_key","Your_value");
   setResult(RES_CODE_A,resultIntent);
   finish();
}

Ne pas envoyer de données

b2......clickListener
    {
       setResult(RES_CODE_B,new Intent());
       finish();
    }

l'utilisateur clique sur le bouton de retour
Par défaut, le résultat est défini avec le code de réponse Activity.RESULT_CANCEL

3) Récupérer le résultat

Pour ce remplacement, méthode onActivityResult

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);

if (resultCode == RES_CODE_A) {

     // b1 was clicked 
   String x = data.getStringExtra("RES_CODE_A");

}
else if(resultCode == RES_CODE_B){

   // b2 was clicked

}
else{
   // back button clicked 
}
}
Rohit Singh
la source
1

ActivityResultRegistry est l'approche recommandée

ComponentActivityfournit maintenant un ActivityResultRegistryqui vous permet de gérer le startActivityForResult()+ onActivityResult()ainsi que requestPermissions()+ onRequestPermissionsResult()flux sans écraser les méthodes dans votre Activityou Fragment, apporte la sécurité de type augmenté parActivityResultContract et fournit des crochets pour tester ces flux.

Il est fortement recommandé d'utiliser les API Activity Result introduites dans AndroidX Activity 1.2.0-alpha02 et Fragment 1.3.0-alpha02.

Ajoutez ceci à votre build.gradle

def activity_version = "1.2.0-alpha03"

// Java language implementation
implementation "androidx.activity:activity:$activity_version"
// Kotlin
implementation "androidx.activity:activity-ktx:$activity_version"

Comment utiliser le contrat pré-construit?

Cette nouvelle API possède les fonctionnalités pré-construites suivantes

  1. Prendre une vidéo
  2. PickContact
  3. Obtenir du contenu
  4. GetContents
  5. OpenDocument
  6. OpenDocuments
  7. OpenDocumentTree
  8. CreateDocument
  9. Cadran
  10. TakePicture
  11. Demander la permission
  12. RequestPermissions

Un exemple qui utilise le contrat takePicture:

private val takePicture = prepareCall(ActivityResultContracts.TakePicture()) 
     { bitmap: Bitmap? ->
        // Do something with the Bitmap, if present
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        button.setOnClickListener { takePicture() }
       }

Alors qu'est-ce qui se passe ici? Décomposons-le légèrement. takePictureest juste un rappel qui retourne un bitmap nullable - que ce soit nul ou non dépend de la onActivityResultréussite ou non du processus. prepareCallpuis enregistre cet appel dans une nouvelle fonctionnalité ComponentActivityappelée ActivityResultRegistry- nous y reviendrons plus tard. ActivityResultContracts.TakePicture()est l'un des assistants intégrés que Google a créés pour nous, et enfin, l'invocation takePicturedéclenche réellement l'intention de la même manière que vous le feriez précédemment avecActivity.startActivityForResult(intent, REQUEST_CODE) .

Comment rédiger un contrat personnalisé?

Contrat simple qui prend un Int comme entrée et renvoie une chaîne qui a demandé que l'activité retourne dans le résultat Intent.

    class MyContract : ActivityResultContract<Int, String>() {

    companion object {
        const val ACTION = "com.myapp.action.MY_ACTION"
        const val INPUT_INT = "input_int"
        const val OUTPUT_STRING = "output_string"
    }

    override fun createIntent(input: Int): Intent {
        return Intent(ACTION)
            .apply { putExtra(INPUT_INT, input) }
    }

    override fun parseResult(resultCode: Int, intent: Intent?): String? {
        return when (resultCode) {
            Activity.RESULT_OK -> intent?.getStringExtra(OUTPUT_STRING)
            else -> null
        }
    }
}



    class MyActivity : AppCompatActivity() {

    private val myActionCall = prepareCall(MyContract()) { result ->
        Log.i("MyActivity", "Obtained result: $result")
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        ...
        button.setOnClickListener {
            myActionCall(500)
        }
    }
}

Consultez cette documentation officielle pour plus d'informations.

Darish
la source
0
You need to override Activity.onActivityResult()

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);

if (resultCode == RESULT_CODE_ONE) {


   String a = data.getStringExtra("RESULT_CODE_ONE");

}
else if(resultCode == RESULT_CODE_TWO){

   // b was clicked

}
else{

}
}
Nitesh Dev Kunwar
la source