330 votes

La méthode OnActivityResult est dépréciée, quelle est l'alternative ?

J'ai récemment découvert que onActivityResult est déprécié. Que devons-nous faire pour le gérer ?

Une alternative introduite pour cela ?

Image showing code with onActivityResult striked out, indicating deprecation

1 votes

Si je l'enlève une erreur lint est apparue pour ajouter le super appel !

26 votes

Je ne sais pas s'il y a déjà eu une dépréciation qui n'a pas été dépréciée, mais j'ai bon espoir que startActivityForResult . Cette nouvelle façon de faire complique excessivement le code et en réduit la lisibilité.

57 votes

Google est le patron. Mais la façon dont ils continuent à changer les choses en peu de temps est frustrante.

493voto

Muntashir Akon Points 2339

Une formation de base est disponible sur développeur.Android.com .

Voici un exemple de la façon de convertir le code existant avec le nouveau :

L'ancienne méthode :

public void openSomeActivityForResult() {
    Intent intent = new Intent(this, SomeActivity.class);
    startActivityForResult(intent, 123);
}

@Override
protected void onActivityResult (int requestCode, int resultCode, Intent data) {
    if (resultCode == Activity.RESULT_OK && requestCode == 123) {
        doSomeOperations();
    }
}

La nouvelle voie (Java) :

// You can do the assignment inside onAttach or onCreate, i.e, before the activity is displayed
ActivityResultLauncher<Intent> someActivityResultLauncher = registerForActivityResult(
        new ActivityResultContracts.StartActivityForResult(),
        new ActivityResultCallback<ActivityResult>() {
            @Override
            public void onActivityResult(ActivityResult result) {
                if (result.getResultCode() == Activity.RESULT_OK) {
                    // There are no request codes
                    Intent data = result.getData();
                    doSomeOperations();
                }
            }
        });

public void openSomeActivityForResult() {
    Intent intent = new Intent(this, SomeActivity.class);
    someActivityResultLauncher.launch(intent);
}

La nouvelle méthode (Kotlin) :

var resultLauncher = registerForActivityResult(StartActivityForResult()) { result ->
    if (result.resultCode == Activity.RESULT_OK) {
        // There are no request codes
        val data: Intent? = result.data
        doSomeOperations()
    }
}

fun openSomeActivityForResult() {
    val intent = Intent(this, SomeActivity::class.java)
    resultLauncher.launch(intent)
}

EDIT. Une meilleure approche serait de le rendre plus généralisé afin que nous puissions le réutiliser. Le snippet ci-dessous est utilisé dans un de mes projets mais attention, il n'est pas bien testé et peut ne pas couvrir tous les cas.

MeilleureActivitéRésultat.java

import android.content.Intent;
import androidx.activity.result.ActivityResult;
import androidx.activity.result.ActivityResultCaller;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContract;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

public class BetterActivityResult<Input, Result> {
    /**
     * Register activity result using a {@link ActivityResultContract} and an in-place activity result callback like
     * the default approach. You can still customise callback using {@link #launch(Object, OnActivityResult)}.
     */
    @NonNull
    public static <Input, Result> BetterActivityResult<Input, Result> registerForActivityResult(
            @NonNull ActivityResultCaller caller,
            @NonNull ActivityResultContract<Input, Result> contract,
            @Nullable OnActivityResult<Result> onActivityResult) {
        return new BetterActivityResult<>(caller, contract, onActivityResult);
    }

    /**
     * Same as {@link #registerForActivityResult(ActivityResultCaller, ActivityResultContract, OnActivityResult)} except
     * the last argument is set to {@code null}.
     */
    @NonNull
    public static <Input, Result> BetterActivityResult<Input, Result> registerForActivityResult(
            @NonNull ActivityResultCaller caller,
            @NonNull ActivityResultContract<Input, Result> contract) {
        return registerForActivityResult(caller, contract, null);
    }

    /**
     * Specialised method for launching new activities.
     */
    @NonNull
    public static BetterActivityResult<Intent, ActivityResult> registerActivityForResult(
            @NonNull ActivityResultCaller caller) {
        return registerForActivityResult(caller, new ActivityResultContracts.StartActivityForResult());
    }

    /**
     * Callback interface
     */
    public interface OnActivityResult<O> {
        /**
         * Called after receiving a result from the target activity
         */
        void onActivityResult(O result);
    }

    private final ActivityResultLauncher<Input> launcher;
    @Nullable
    private OnActivityResult<Result> onActivityResult;

    private BetterActivityResult(@NonNull ActivityResultCaller caller,
                                 @NonNull ActivityResultContract<Input, Result> contract,
                                 @Nullable OnActivityResult<Result> onActivityResult) {
        this.onActivityResult = onActivityResult;
        this.launcher = caller.registerForActivityResult(contract, this::callOnActivityResult);
    }

    public void setOnActivityResult(@Nullable OnActivityResult<Result> onActivityResult) {
        this.onActivityResult = onActivityResult;
    }

    /**
     * Launch activity, same as {@link ActivityResultLauncher#launch(Object)} except that it allows a callback
     * executed after receiving a result from the target activity.
     */
    public void launch(Input input, @Nullable OnActivityResult<Result> onActivityResult) {
        if (onActivityResult != null) {
            this.onActivityResult = onActivityResult;
        }
        launcher.launch(input);
    }

    /**
     * Same as {@link #launch(Object, OnActivityResult)} with last parameter set to {@code null}.
     */
    public void launch(Input input) {
        launch(input, this.onActivityResult);
    }

    private void callOnActivityResult(Result result) {
        if (onActivityResult != null) onActivityResult.onActivityResult(result);
    }
}

Avec l'approche ci-dessus, vous devez toujours l'enregistrer avant ou pendant le lancement de l'activité ou du fragment attaché. Une fois défini, il peut être réutilisé au sein de l'activité ou du fragment. Par exemple, si vous devez lancer de nouvelles activités dans la majeure partie de l'activité, vous pouvez définir un fichier BaseActivity et enregistrer un nouveau BetterActivityResult comme ça :

BaseActivity.java

public class BaseActivity extends AppCompatActivity {
    protected final BetterActivityResult<Intent, ActivityResult> activityLauncher = BetterActivityResult.registerActivityForResult(this);
}

Après cela, vous pouvez simplement lancer une activité à partir de n'importe quelle activité enfant comme ceci :

public void openSomeActivityForResult() {
    Intent intent = new Intent(this, SomeActivity.class);
    activityLauncher.launch(intent, result -> {
        if (result.getResultCode() == Activity.RESULT_OK) {
            // There are no request codes
            Intent data = result.getData();
            doSomeOperations();
        }
    })
}

Puisque vous pouvez définir la fonction de rappel en même temps que l'option Intent vous pouvez le réutiliser pour n'importe quelle activité.

De même, vous pouvez utiliser d'autres contrats d'activité à l'aide des deux autres constructeurs.

420 votes

La nouvelle méthode semble inutilement compliquée par rapport à l'ancienne...

9 votes

@drmrbrewer il supprime le tracas de la maintenance des codes de demande et ajoute la possibilité d'exécuter des tests. Mais je pense que son principal problème est la réutilisation. A mon avis ActivityResultCallback devrait faire partie de ActivityResultLauncher#launch() afin de pouvoir la réutiliser à d'autres fins. Par exemple, si j'avais eu besoin de vérifier les autorisations de stockage pour plusieurs actions, j'aurais pu utiliser un seul fichier ActivityResultContracts.RequestPermission() pour tous. Vous pourriez mettre en œuvre ActivityResultCallback pour tenter de résoudre ce problème mais cela sera pire que l'ancienne méthode puisque vous avez besoin de quelque chose de similaire pour demander des codes.

2 votes

Si mon activité démarre 2 nouvelles activités pour le résultat, comment puis-je le différencier sur onActivityResult ?

35voto

Hardik Hirpara Points 677

A partir de maintenant, startActivityForResult() a été déprécié, utilisez donc la nouvelle méthode à la place.

Exemple Kotlin

    fun openActivityForResult() {
        startForResult.launch(Intent(this, AnotherActivity::class.java))
    }

    val startForResult = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { 
    result: ActivityResult ->
        if (result.resultCode == Activity.RESULT_OK) {
            val intent = result.data
            // Handle the Intent
            //do stuff here
        }
    }

21voto

Abhijeet Points 101

Il y a 4 étapes simples à suivre lors du remplacement de la méthode dépréciée startActivityForResult(...) .

  1. En lieu et place de la méthode surchargée onActivityResult(..) -

     ActivityResultLauncher<Intent> activityResultLaunch = registerForActivityResult(
             new ActivityResultContracts.StartActivityForResult(),
             new ActivityResultCallback<ActivityResult>() {
                 @Override
                 public void onActivityResult(ActivityResult result) {
                     if (result.getResultCode() == 123) {
                         // ToDo : Do your stuff...
                     } else if(result.getResultCode() == 321) {
                         // ToDo : Do your stuff...
                     }
                 }
    });

Pour les demandes personnalisées multiples, ajoutez la condition comme

if (result.getResultCode() == 123) {
..
} else if(result.getResultCode() == 131){
..
} // so on..
  1. Importations :

     import androidx.activity.result.ActivityResult;
     import androidx.activity.result.ActivityResultCallback;
     import androidx.activity.result.ActivityResultLauncher;
     import androidx.activity.result.contract.ActivityResultContracts;
  2. A la place de startActivityForResult(intent, 123), utilisez

     Intent intent = new Intent(this, SampleActivity.class);
     activityResultLaunch.launch(intent);
  3. Dans la classe SampleActivity.java, en retournant à l'activité source, le code restera le même, comme -

    Intent intent = new Intent();
    setResult(123, intent);
    finish();

Bon codage ! :)

0 votes

Merci beaucoup, votre guide est très détaillé.

20voto

A KOTLIN J'ai modifié mon code

startActivityForResult(intent, Constants.MY_CODE_REQUEST)

y

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) {
    super.onActivityResult(requestCode, resultCode, data)
    if (resultCode == Activity.RESULT_OK) {
        when (requestCode) {
            Constants.MY_CODE_REQUEST -> {
            ...
}

à

registerForActivityResult(StartActivityForResult()) { result ->
    onActivityResult(Constants.MY_CODE_REQUEST, result)
}.launch(intent)

y

private fun onActivityResult(requestCode: Int, result: ActivityResult) {
    if(result.resultCode == Activity.RESULT_OK) {
        val intent = result.data
        when (requestCode) {
            Constants.MY_CODE_REQUEST -> {
            ...

J'espère que cela fonctionnera pour vous. :D

3 votes

onActivityResult de votre troisième extrait de code sur registerForActivityResult est déprécié.

2 votes

@FilipeBrito onActivityResult n'est pas une méthode écrasée, c'est ma propre méthode, le nom peut être n'importe quoi ;)

7 votes

Le requestCode de la nouvelle manière semble pratiquement inutile.

19voto

Sanjayrajsinh Points 1098

La nouvelle voie est : registerForActivityResult


Avantage :

  1. Cette nouvelle méthode permet de réduire la complexité à laquelle nous sommes confrontés lorsque nous appelons une activité à partir d'un fragment ou d'une autre activité.
  2. Demandez facilement n'importe quelle permission et obtenez un rappel.

Dans Kotlin :

var launchSomeActivity = registerForActivityResult(StartActivityForResult()) { result ->
    if (result.resultCode == Activity.RESULT_OK) {
        val data: Intent? = result.data
        // your operation...
    }
}

fun openYourActivity() {
    val intent = Intent(this, SomeActivity::class.java)
    launchSomeActivity.launch(intent)
}

En Java :

 // Create lanucher variable inside onAttach or onCreate or global
 ActivityResultLauncher<Intent> launchSomeActivity = registerForActivityResult(
     new ActivityResultContracts.StartActivityForResult(),
     new ActivityResultCallback<ActivityResult>() {
              @Override
              public void onActivityResult(ActivityResult result) {
                   if (result.getResultCode() == Activity.RESULT_OK) {
                         Intent data = result.getData();
                         // your operation....
                    }
               }
      });

      public void openYourActivity() {
            Intent intent = new Intent(this, SomeActivity.class);
            launchSomeActivity.launch(intent);
      }

0 votes

Et si quelqu'un veut passer android.content.Intent intent, int requestCode comme paramètres comme sur une méthode dépréciée pour une raison quelconque ?

0 votes

Sans vouloir trop changer la logique antérieure, jusqu'à ce que l'on s'adapte à la nouvelle façon de faire ?

1 votes

@AtomX pas besoin de passer le code de la demande parce que vous avez besoin de créer cet objet de lancement pour l'appel individuel, donc si vous voulez appeler deux activités différentes, vous devez créer un objet de rappel.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X