web-dev-qa-db-fra.com

Retour d'une activité à l'aide de browseUpFromSameTask ()

J'ai deux activités, A et B. Lorsque l'activité A est démarrée pour la première fois, elle accède à la Intent qui lui a été transmise (car la Bundle est null, comme il devrait être la première fois) et affiche les informations en conséquence:

CustInfo m_custInfo;
...
protected void onCreate(Bundle savedInstanceState)
{
    ...
    Bundle bundle = (savedInstanceState == null) ? getIntent().getExtras() : savedInstanceState;
    m_custInfo = (CustInfo) m_bundle.getSerializable("CustInfo");
    if (m_custInfo != null
        ...
}

Cela fonctionne bien la première fois. Les contrôles EditText et ListView sont remplis correctement.

Maintenant, quand un élément de la liste est cliqué, l'activité B est lancée pour afficher les détails:

m_custInfo = m_arrCustomers.get(pos);

Intent intent = new Intent(A.this, B.class);
intent.putExtra("CustInfo", m_custInfo); // CustInfo is serializable
// printing this intent, it shows to have extras and no flags

startActivityForResult(intent, 1);

Juste avant le démarrage de l'activité B, la structure appelle la fonction onSaveInstanceState() remplacée:

protected void onSaveInstanceState(Bundle outState)
{
    super.onSaveInstanceState(outState);

    outState.putSerializable("CustInfo", m_custInfo);
}

Dans l'activité B, lorsque vous appuyez sur le bouton Haut dans la barre d'actions, je souhaite revenir à l'activité A et la laisser dans le même état qu'auparavant:

public boolean onOptionsItemSelected(MenuItem item)
{
    if (item.getItemId() == Android.R.id.home)
    {
        Intent intent = NavUtils.getParentActivityIntent(this);
        // printing this intent, it shows to have flags but no extras

        NavUtils.navigateUpFromSameTask(this); // tried finish() here but that created an even bigger mess
        return true;
    }
    ...
}

C’est là que réside le problème, lorsque, pour la deuxième fois dans onCreate() de l’activité A, le paramètre Bundle est null et getExtras() renvoie null. Puisque onSaveInstanceState() a été appelé, j'aurais pu m'attendre à ce que le paramètre Bundle soit non -null.

J'ai lu des articles à ce sujet sur d'autres sites Web, j'ai essayé les suggestions, mais rien ne fonctionne.

68
DavidCrow

Si vous souhaitez que votre application réagisse de cette manière, vous devez déclarer le mode de lancement de votre activité A comme suit:

Android:launchMode="singleTop"

dans votre AndroidManifest.xml.

Sinon, Android utilise le mode de lancement standard, ce qui signifie 

"Le système crée toujours une nouvelle instance de l'activité dans la tâche cible "

et votre activité est recréée (voir documentation Android ).

Avec singleTop, le système revient à votre activité existante (avec le supplément d'origine), si elle se trouve en haut de la pile arrière de la tâche. Il n'est pas nécessaire d'implémenter onSaveInstanceState dans cette situation. 

savedInstanceState est null dans votre cas, car votre activité n’était pas précédemment arrêtée par le système d’exploitation.

Remarque (merci, développeur Android pour avoir pointé sur this ) :

Bien que ce soit une solution à la question, cela ne fonctionnera pas si l’activité vers laquelle on retourne est pas au sommet de la pile arrière . Considérons le cas de l’activité A commençant B, qui commence L'activité parent de C et C est configurée pour être A. Si vous appelez NavigateUpFromSameTask() depuis C, l'activité sera recréée, car A n'est pas au sommet de la pile.

Dans ce cas, vous pouvez utiliser ce morceau de code à la place:

Intent intent = NavUtils.getParentActivityIntent(this); 
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP|Intent.FLAG_ACTIVITY_SINGLE_TOP); 
NavUtils.navigateUpTo(this, intent);
129
yonojoy

Cela se produit parce que NavUtils.navigateUpFromSameTask() appelle simplement startActivity(intentForActivityA). Si l'activité A utilise le Android:launchMode="standard" par défaut, une nouvelle instance de l'activité est créée et l'état enregistré n'est pas utilisé. Il existe trois façons de résoudre ce problème, avec différents avantages et inconvénients.

Option 1

Remplacez getParentActivityIntent () dans l'activité B et spécifiez les suppléments appropriés nécessaires à l'activité A:

@Override
public Intent getParentActivityIntent() {
    Intent intent = super.getParentActivityIntent();
    intent.putSerializable("CustInfo", m_custInfo);
    return intent;
}

Remarque: Si vous utilisez ActionBarActivity et la barre d'outils de la bibliothèque de support, vous devrez alors remplacer getSupportParentActivityIntent () .

Je pense que c'est la solution la plus élégante car elle fonctionne quelle que soit la manière dont l'utilisateur a accédé à l'activité B. Les deux options répertoriées ci-dessous ne fonctionnent pas correctement si l'utilisateur accède directement à l'activité B sans passer par l'activité A, par exemple si activité B est lancé à partir d'un gestionnaire de notification ou d'URL. Je pense que ce scénario est la raison pour laquelle l'API de navigation "vers le haut" est compliquée et ne se comporte pas simplement comme un bouton muet.

L'un des inconvénients de cette solution est que l'animation standard de transition d'une nouvelle activité est utilisée plutôt que l'animation de retour à une activité antérieure.

Option 2

Interceptez le bouton haut et traitez-le comme un bouton muet en remplaçant onNavigateUp () :

@Override
public boolean onNavigateUp() {
    onBackPressed();
    return true;
}

Remarque: Si vous utilisez ActionBarActivity et la barre d'outils de la bibliothèque de support, vous devez remplacer le paramètre onSupportNavigateUp () .

Cette solution est un peu compliquée et ne fonctionne que pour les applications où "up" et "back" devraient se comporter de la même manière. Ceci est probablement rare, car si "up" et "back" devaient se comporter de la même manière, pourquoi se donner la peine d'avoir un bouton "up"? L'un des avantages de cette solution est que l'animation standard de retour à une activité antérieure est utilisée.

Option 3

Comme suggéré par yonojoy, définissez Android:launchMode="singleTop" sur l'activité A. Voir sa réponse pour plus de détails. Notez que singleTop ne convient pas à toutes les applications. Vous devrez l'essayer et/ou passer du temps à lire la documentation pour décider par vous-même.

16
Mark Doliner

Au lieu d'appeler NavUtils.navigateUpFromSameTask

Vous pouvez obtenir l'intention parent et la modifier avant de naviguer. Par exemple:

Intent intent = NavUtils.getParentActivityIntent(this);
intent.putExtra("CustInfo", m_custInfo); // CustInfo is serializable
NavUtils.navigateUpTo(this, intent);

Cela se comportera exactement comme NavUtils.navigateUpFromSameTask mais vous permettra d'ajouter des propriétés supplémentaires à l'intention. Vous pouvez consulter le code pour NavUtils.navigateUpFromSameTask c'est très simple.

public static void navigateUpFromSameTask(Activity sourceActivity) {
    Intent upIntent = getParentActivityIntent(sourceActivity);

    if (upIntent == null) {
        throw new IllegalArgumentException("Activity " +
                sourceActivity.getClass().getSimpleName() +
                " does not have a parent activity name specified." +
                " (Did you forget to add the Android.support.PARENT_ACTIVITY <meta-data> " +
                " element in your manifest?)");
    }

    navigateUpTo(sourceActivity, upIntent);
}

Faire de l'activité parent SINGLE_TOP peut fonctionner pour certaines applications simples, mais que se passe-t-il si cette activité n'a pas été lancée directement à partir de sa société mère? OR vous pouvez légitimement vouloir que le parent existe à plusieurs endroits dans la pile arrière.

4
Jared Kells

Je pense que c'est une solution plus générale, à ajouter à votre classe d'activité de base:

@Override
public boolean onOptionsItemSelected(final MenuItem item) {
    if (item.getItemId() == Android.R.id.home) {
        final Intent upIntent = NavUtils.getParentActivityIntent(this);
        if (upIntent == null)
            onBackPressed();
        else
            //optionally add this flag to the intent: Intent.FLAG_ACTIVITY_SINGLE_TOP
            NavUtils.navigateUpTo(this, upIntent);
        return true;
    }
    return super.onOptionsItemSelected(item);
}

Et pour chaque activité désirée menant à une activité parent spécifique, utilisez ceci sur le manifeste:

    <activity Android:parentActivityName="pathToParentActivity" ...>
        <meta-data
            Android:name="Android.support.PARENT_ACTIVITY"
            Android:value="pathToParentActivity"/>
    </activity>

Et, si votre minSdk provient de l'API 16, supprimez la balise "méta-données", afin que vous ayez ceci à la place dans le manifeste:

    <activity Android:parentActivityName="pathToParentActivity" ...>
    </activity>

Plus d'informations ici

0
android developer