web-dev-qa-db-fra.com

"Echec du résultat" - onActivityResult

J'ai un LoginActivity (utilisateur ouvre une session). Il s'agit essentiellement de son propre Activity dont le thème est celui d'un dialogue (apparaître comme un dialogue). Il apparaît sur un SherlockFragmentActivity. Ce que je veux, c'est: Si la connexion est réussie, il devrait y avoir deux FragmentTransaction 'pour mettre à jour la vue. Voici le code:

Dans LoginActivity, en cas de connexion réussie,

setResult(1, new Intent());

Dans SherlockFragmentActivity:

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

    if (resultCode == 1) {
        LoggedStatus = PrefActivity.getUserLoggedInStatus(this);
        FragmentTransaction t = MainFragmentActivity.this.getSupportFragmentManager().beginTransaction();
        SherlockListFragment mFrag = new MasterFragment();
        t.replace(R.id.menu_frame, mFrag);
        t.commit();

        // Set up Main Screen
        FragmentTransaction t2 = MainFragmentActivity.this.getSupportFragmentManager().beginTransaction();
        SherlockListFragment mainFrag = new FeaturedFragment();
        t2.replace(R.id.main_frag, mainFrag);
        t2.commit();
    }
}

Il se bloque sur le premier commit, avec ce LogCat:

E/AndroidRuntime(32072): Caused by: Java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
E/AndroidRuntime(32072):    at Android.support.v4.app.FragmentManagerImpl.checkStateLoss(FragmentManager.Java:1299)
E/AndroidRuntime(32072):    at Android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.Java:1310)
E/AndroidRuntime(32072):    at Android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.Java:541)
E/AndroidRuntime(32072):    at Android.support.v4.app.BackStackRecord.commit(BackStackRecord.Java:525)
E/AndroidRuntime(32072):    at com.kickinglettuce.rate_this.MainFragmentActivity.onActivityResult(MainFragmentActivity.Java:243)
E/AndroidRuntime(32072):    at Android.app.Activity.dispatchActivityResult(Activity.Java:5293)
E/AndroidRuntime(32072):    at Android.app.ActivityThread.deliverResults(ActivityThread.Java:3315)
72
KickingLettuce

Tout d’abord, vous devriez lire mon article de blog pour plus d’informations (il explique pourquoi cette exception se produit et ce que vous pouvez faire pour l'empêcher).

L'appel de commitAllowingStateLoss() est plus un piratage qu'un correctif. Les pertes d’État sont graves et doivent être évitées à tout prix. Au moment où la fonction onActivityResult() est appelée, l'état de l'activité/du fragment n'a peut-être pas encore été restauré. Par conséquent, toutes les transactions effectuées pendant cette période seront perdues. C'est un bug très important qui doit être traité! (Notez que le bogue ne survient que lorsque votre Activity revient après avoir été tué par le système ... ce qui, en fonction de la quantité de mémoire dont dispose le périphérique, peut parfois être rare ... bug n'est pas quelque chose qui est très facile à attraper lors des tests).

Essayez de déplacer vos transactions dans onPostResume() à la place (notez que onPostResume() est toujours appelé après onResume() et onResume() est toujours appelé après onActivityResult()):

private boolean mReturningWithResult = false;

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

@Override
protected void onPostResume() {
    super.onPostResume();
    if (mReturningWithResult) {
        // Commit your transactions here.
    }
    // Reset the boolean flag back to false for next time.
    mReturningWithResult = false;
}

Cela peut sembler un peu étrange, mais faire ce genre de chose est nécessaire pour s'assurer que vos FragmentTransactions sont toujours validés après l'état de Activity a été restauré son état d'origine (il est garanti que onPostResume() sera appelé après que l'état de Activity aura été restauré).

286
Alex Lockwood

Ceci est similaire à la réponse @Alex Lockwood mais en utilisant un Runnable:

private Runnable mOnActivityResultTask;

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    mOnActivityResultTask = new Runnable() {
        @Override
        public void run() {
            // Your code here
        }
    }
}

@Override
protected void onPostResume() {
    super.onPostResume();
    if (mOnActivityResultTask != null) {
        mOnActivityResultTask.run();
        mOnActivityResultTask = null;
    }
}

Si vous utilisez Android 3. et au-dessus avec lambdas, utilisez ceci:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    mOnActivityResultTask = () -> {
        // Your code here
    }
}
1
vovahost