web-dev-qa-db-ja.com

「失敗した配信結果」-onActivityResult

LoginActivity(ユーザーログイン)があります。基本的に、ダイアログのようにテーマが設定されているのは独自のActivityです(ダイアログのように表示されます)。 SherlockFragmentActivityの上に表示されます。私が欲しいのは:ログインに成功した場合、ビューを更新するために2つのFragmentTransactionが必要です。コードは次のとおりです。

LoginActivityで、ログインに成功した場合、

setResult(1, new Intent());

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();
    }
}

この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

まず、詳細については、私の blog post を読む必要があります(この例外が発生する理由とできることについて説明しています)それを防ぐために行う)。

commitAllowingStateLoss()を呼び出すことは、修正というよりもハッキングです。州の損失はひどいものであり、どんな犠牲を払っても避けるべきです。 onActivityResult()が呼び出された時点では、アクティビティ/フラグメントの状態はまだ復元されていない可能性があるため、この間に発生したトランザクションはすべて失われます。これは非常に重要なバグであり、対処する必要があります! (このバグは、システムによって殺された後にActivityが戻ってきたときにのみ発生することに注意してください...デバイスのメモリ量によっては、まれに発生することがあります...バグはテスト中に見つけるのが非常に簡単なものではありません)。

代わりにトランザクションをonPostResume()に移動してみてください(onPostResume()の後にonResume()が常に呼び出され、onResume()の後に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;
}

これは少し奇妙に思えるかもしれませんが、FragmentTransactionsが常にコミットされるようにするには、この種のことを行う必要がありますafterActivityの状態が元の状態に復元されました状態(onPostResume()Activityの状態が復元された後に呼び出されることが保証されています)。

286
Alex Lockwood

これは@Alex Lockwoodの回答に似ていますが、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;
    }
}

Android 3.以上でlambdasを指定して実行している場合、これを使用します:

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