“Помилка надання результату” - onActivityForResult


74

У мене є LoginActivity(Вхід користувача). В основному він власний Activity, тематизований як діалогове вікно (виглядає так, ніби діалогове вікно). Він з'являється над SherlockFragmentActivity. Що я хочу, так це: якщо успішний вхід в систему, 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)

як ти зателефонував startActivityForResult ()
Рохіт,

Відповіді:


293

Перш за все, ви повинні прочитати мою публікацію в блозі для отримання додаткової інформації (там розповідається про те, чому трапляється цей виняток і що ви можете зробити, щоб запобігти цьому).

Виклик 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 завжди відбуваються після того, як в Activity«-стан був відновлений в початковий стан ( onPostResume()гарантовано буде називатися після того , як Activity" стан s було відновлено) .


4
У фрагментах я використовую onResume за відсутності onPostResume. Я більше не бачив проблеми, але, можливо, ви хочете прокоментувати це. PS. Дякуємо за ваші проникливі публікації!
Maragues

27
Так, робити це в Fragment#onResume()нормально. Це тому, що FragmentActivity#onPostResume()дзвінки FragmentActivity#onResumeFragments(), які дзвонять FragmentManager#dispatchResume(), що викликає Fragment#onResume()кожен із фрагментів діяльності. Отже, Fragment#onResume()викликається після, FragmentActivity#onPostResume()щоб не виникало проблем (ви можете перевірити [вихідний код] ( goo.gl/Lo1Z1T ) для кожного відповідного класу, щоб перевірити це самі ... або ви можете просто мене: P ). І спасибі! Радий, що ти подумав, що вони проникливі. :)
Алекс Локвуд,

2
@crazyhorse У цьому випадку може бути непоганою ідеєю скасувати асинхронне завдання та / або підняти прапорець, щоб переконатися, що dismiss()він не викликається всередині AsyncTask#onPostExecute()(діалогове вікно автоматично відхилятиметься FragmentManagerу тому випадку, якщо діяльність перейде у фоновому режимі , тому вам не потрібно самостійно відхиляти діалогове вікно після того, як діяльність все одно зникне).
Алекс Локвуд,

1
Не знав про onPostResume(). Виправлено мою проблему (я створював фрагмент діалогового вікна onResume()після скасування системного діалогового вікна із очікуваного наміру, що призвело до збою мого додатка).
EpicPandaForce

1
@AlexLockwood дякую за цей допис. У своєму блозі ви говорите, що транзакції повинні здійснюватися після дії # onPostResume. Але в той же час ви говорите, що їх можна увімкнути в Activity # onCreate (який виконується ще до onResume). Я впевнений, що я щось неправильно зрозумів, тож ви можете пояснити.
nutella_eater

1

Це схоже на відповідь @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.0 і новіші версії з лямбда , використовуйте це:

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

Я волів би піти з runnable, оскільки це дозволяє писати логіку близько того, де це відбувається. У моєму випадку це надходить від абонента rx.
Фабіо

-2

Ви можете використовувати ft.commitAllowingStateLoss()для вирішення цієї проблеми.

Причина: ваш ft.commit()метод було переключено після onSaveInstanceState.


-2

у вашому logcat чітко сказано: "Не вдається виконати цю дію після onSaveInstanceState" - ви Activityвже померли на той момент і не змогли повернути жодних результатів.

просто рухайся:

Intent in = new Intent();
setResult(1, in);

до того місця, Activityде воно все ще живе, і все буде добре. і не забудьте finish()ваш Activityдоставити результат.


Дякую. finish()це останнє, що я роблю. У мене був код наміру безпосередньо над ним., Я перемістив його вгору та та сама помилка. Я додаю код для відповіді ...
TheLettuceMaster

Ще один момент до останнього коментаря. Я насправді не бачу тостів. Але коли я знову
ввожу

-2

У моєму випадку я стикався з тими ж проблемами через наступне

public void onBackPressed() {
    super.onBackPressed();
    setIntents();

}


private void setIntents(){
    Intent searchConstaints=new Intent();
    searchConstaints.putExtra("min",20);
    searchConstaints.putExtra("max",80);
    setResult(101,searchConstaints);
    finish();
}

Вирішено шляхом перестановки функції Calls усередині onBackPress ()

public void onBackPressed() {

    setIntents();
    super.onBackPressed();

}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.