mendapatkan pengecualian "IllegalStateException: Tidak dapat melakukan tindakan ini setelah onSaveInstanceState"

355

Saya memiliki aplikasi Android Live, dan dari pasar saya telah menerima jejak stack berikut dan saya tidak tahu mengapa hal ini terjadi karena tidak terjadi dalam kode aplikasi tetapi disebabkan oleh beberapa atau peristiwa lain dari aplikasi (asumsi)

Saya tidak menggunakan Fragmen, masih ada referensi FragmentManager. Jika ada orang yang dapat menyoroti fakta-fakta tersembunyi untuk menghindari masalah seperti ini:

java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
at android.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1109)
at android.app.FragmentManagerImpl.popBackStackImmediate(FragmentManager.java:399)
at android.app.Activity.onBackPressed(Activity.java:2066)
at android.app.Activity.onKeyDown(Activity.java:1962)
at android.view.KeyEvent.dispatch(KeyEvent.java:2482)
at android.app.Activity.dispatchKeyEvent(Activity.java:2274)
at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1668)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchKeyEvent(PhoneWindow.java:1720)
at com.android.internal.policy.impl.PhoneWindow.superDispatchKeyEvent(PhoneWindow.java:1258)
at android.app.Activity.dispatchKeyEvent(Activity.java:2269)
at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1668)
at android.view.ViewRoot.deliverKeyEventPostIme(ViewRoot.java:2851)
at android.view.ViewRoot.handleFinishedEvent(ViewRoot.java:2824)
at android.view.ViewRoot.handleMessage(ViewRoot.java:2011)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:132)
at android.app.ActivityThread.main(ActivityThread.java:4025)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:491)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:841)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:599)
at dalvik.system.NativeStart.main(Native Method)  
dcool
sumber
Apakah Anda sudah menemukan solusi? Memiliki masalah yang sama di sini: stackoverflow.com/questions/7575921/…
nhaarman
1
Saya memiliki masalah yang sama dan menemukan solusi sederhana yang bekerja untuk saya
phlebas
2
@ phlebas Tidak, kamu tidak. Milik Anda menyangkut dialog, dan ini tidak. Baris teratas pencocokan jejak tumpukan Anda tidak cukup. Sisanya sangat berbeda. Saya mengatakan ini karena saya baru saja melihat masalah Anda dan sayangnya tidak ada gunanya bagi saya.
themightyjon
Apakah Anda menggunakan Thread atau AsynTask dalam aktivitas itu?
José Castro
21
Saya membahas kesalahan ini di posting blog saya ... Anda harus membacanya. :)
Alex Lockwood

Jawaban:

455

Ini adalah bug paling bodoh yang saya temui sejauh ini. Saya memiliki Fragmentaplikasi yang berfungsi dengan baik untuk API <11 , dan Force Closingpada API> 11 .

Saya benar-benar tidak tahu apa yang mereka ubah di dalam Activitysiklus hidup dalam panggilan untuk saveInstance, tetapi saya di sini adalah bagaimana saya memecahkan ini:

@Override
protected void onSaveInstanceState(Bundle outState) {
    //No call for super(). Bug on API Level > 11.
}

Saya hanya tidak menelepon .super()dan semuanya bekerja dengan baik. Saya harap ini akan menghemat waktu Anda.

EDIT: setelah beberapa penelitian lebih lanjut, ini adalah bug yang dikenal dalam paket dukungan.

Jika Anda perlu menyimpan instance, dan menambahkan sesuatu ke Anda, outState BundleAnda dapat menggunakan yang berikut:

@Override
protected void onSaveInstanceState(Bundle outState) {
    outState.putString("WORKAROUND_FOR_BUG_19917_KEY", "WORKAROUND_FOR_BUG_19917_VALUE");
    super.onSaveInstanceState(outState);
}

EDIT2: ini juga dapat terjadi jika Anda mencoba melakukan transaksi setelah Activitylatar belakang Anda hilang. Untuk menghindari ini, Anda harus menggunakancommitAllowingStateLoss()

EDIT3: Solusi di atas memperbaiki masalah di perpustakaan support.v4 awal dari apa yang saya ingat. Tetapi jika Anda masih memiliki masalah dengan ini, Anda HARUS juga membaca blog @AlexLockwood : Transaksi Fragmen & Aktivitas Kehilangan Negara

Ringkasan dari posting blog (tapi saya sangat menyarankan Anda untuk membacanya):

  • TIDAK PERNAH commit() bertransaksi setelah onPause()pada pra-Honeycomb, dan onStop()pada pasca-Honeycomb
  • Hati-hati saat melakukan transaksi di dalam Activitymetode siklus hidup. Gunakan onCreate() , onResumeFragments()danonPostResume()
  • Hindari melakukan transaksi di dalam metode panggilan balik asinkron
  • Gunakan commitAllowingStateLoss()hanya sebagai pilihan terakhir
Ovidiu Latcu
sumber
97
Anda harus menggunakan commitAllowingStateLoss () alih-alih komit ()
meh
7
Jadi, tidak memanggil super di onSaveInstanceState akan menghentikan FragmentManager untuk dapat menyimpan status semua fragmen dan mengembalikannya. Itu dapat menyebabkan masalah dengan rotasi. Juga, saya baru saja mencoba hal lain tentang memasukkan sampah ke dalam bungkusan dan itu tidak ada bedanya bagi saya. Tidak yakin bagaimana itu akan terjadi - bug yang Anda rujuk dalam paket dukungan adalah NullPointerException, dan sepertinya tidak seperti IllegalStateException ini ...
themightyjon
56
@ meh commitAllowingStateLoss()hanya menghindari pengecualian. Itu tidak melindungi aplikasi Anda dari kehilangan kondisi tidak disengaja. Lihat posting blog ini .
Alex Lockwood
2
@AlexLockwood sehingga dari posting blog itu kita dapat belajar bahwa kita harus melakukan semua panggilan jaringan di dalam fragmen (dan menampilkan beberapa kemajuan sementara ui jika diperlukan) dan itulah satu-satunya cara kita dapat menghindari pengecualian ini ketika itu mungkin terjadi karena komit dipanggil setelah beberapa pemanggilan metode asinkron.
meh
1
Saya tidak mendapatkan poin pertama: "TIDAK PERNAH melakukan transaksi () setelah ... onStop () pada post-Honeycomb". Bagaimana jika saya membutuhkan tombol untuk memicu fragmen untuk diganti dengan yang lain? Haruskah saya meletakkan boolean yang memeriksa apakah aktivitas telah selesai di Stop, dan jika ya, panggil commitAllowingStateLoss sebagai gantinya? Juga, bagaimana jika saya memiliki fragmen di dalam fragmen, yang harus saya ganti setelah mengklik tombol?
pengembang android
76

Mencari di kode sumber Android tentang apa yang menyebabkan masalah ini memberikan bahwa flag mStateSaved di FragmentManagerImplkelas (contoh tersedia di Activity) memiliki nilai true. Ini disetel ke true ketika tumpukan belakang disimpan (saveAllState) saat panggilan dari Activity#onSaveInstanceState. Setelah itu panggilan dari ActivityThread tidak mengatur ulang flag ini menggunakan metode reset yang tersedia dari FragmentManagerImpl#noteStateNotSaved()dan dispatch().

Cara saya melihatnya ada beberapa perbaikan yang tersedia, tergantung pada apa yang dilakukan dan digunakan aplikasi Anda:

Cara yang baik

Sebelum hal lain: Saya akan mengiklankan artikel Alex Lockwood . Lalu, dari apa yang telah saya lakukan sejauh ini:

  1. Untuk fragmen dan aktivitas yang tidak perlu menyimpan informasi status apa pun, hubungi commitAllowStateLoss . Diambil dari dokumentasi:

    Mengizinkan komit dieksekusi setelah status aktivitas disimpan. Ini berbahaya karena komit dapat hilang jika aktivitas tersebut nanti harus dipulihkan dari kondisinya, jadi ini hanya boleh digunakan untuk kasus-kasus di mana tidak apa-apa bagi negara UI untuk mengubah secara tak terduga pada pengguna`. Saya kira ini tidak masalah untuk digunakan jika fragmen menampilkan informasi hanya baca. Atau bahkan jika mereka menampilkan info yang dapat diedit, gunakan metode panggilan balik untuk mempertahankan info yang diedit.

  2. Tepat setelah transaksi dilakukan (Anda baru saja menelepon commit()), lakukan panggilan ke FragmentManager.executePendingTransactions().

Cara yang tidak disarankan:

  1. Seperti Ovidiu Latcu yang disebutkan di atas, jangan menelepon super.onSaveInstanceState(). Tetapi ini berarti Anda akan kehilangan seluruh status aktivitas Anda bersama dengan fragmen.

  2. Timpa onBackPresseddan hanya ada panggilan finish(). Ini seharusnya OK jika aplikasi Anda tidak menggunakan Fragmen API; seperti di super.onBackPressedsana ada panggilan untuk FragmentManager#popBackStackImmediate().

  3. Jika Anda menggunakan kedua API Fragmen dan status aktivitas Anda penting / vital, maka Anda dapat mencoba menelepon menggunakan API refleksi FragmentManagerImpl#noteStateNotSaved(). Tapi ini peretasan, atau bisa dikatakan itu solusi. Saya tidak suka, tetapi dalam kasus saya ini cukup dapat diterima karena saya memiliki kode dari aplikasi lawas yang menggunakan kode usang ( TabActivitydan secara implisit LocalActivityManager).

Di bawah ini adalah kode yang menggunakan refleksi:

@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    invokeFragmentManagerNoteStateNotSaved();
}

@SuppressWarnings({ "rawtypes", "unchecked" })
private void invokeFragmentManagerNoteStateNotSaved() {
    /**
     * For post-Honeycomb devices
     */
    if (Build.VERSION.SDK_INT < 11) {
        return;
    }
    try {
        Class cls = getClass();
        do {
            cls = cls.getSuperclass();
        } while (!"Activity".equals(cls.getSimpleName()));
        Field fragmentMgrField = cls.getDeclaredField("mFragments");
        fragmentMgrField.setAccessible(true);

        Object fragmentMgr = fragmentMgrField.get(this);
        cls = fragmentMgr.getClass();

        Method noteStateNotSavedMethod = cls.getDeclaredMethod("noteStateNotSaved", new Class[] {});
        noteStateNotSavedMethod.invoke(fragmentMgr, new Object[] {});
        Log.d("DLOutState", "Successful call for noteStateNotSaved!!!");
    } catch (Exception ex) {
        Log.e("DLOutState", "Exception on worka FM.noteStateNotSaved", ex);
    }
}

Bersulang!

gunar
sumber
Ini tampaknya terjadi dengan ActionBarSherlock juga, di bawah Gingerbread, sehingga kasus memeriksa Build Id tampaknya diperdebatkan ... :(
t0mm13b
Juga, Anda harus menunjukkan - yang juga tidak cocok untuk penggunaan ABS :)
t0mm13b
@ t0mm13b: Kode di atas berarti proyek saya karena tidak menggunakan fragmen, tidak juga support.FragmentActivity. Ini berjalan di android.app. Aktivitas dan karena ketidakkonsistenan yang memicu Pengecualian disebabkan oleh FragmentManager (API level 11 ke atas), itu sebabnya cek ... Jika Anda yakin sumber kejahatannya juga sama untuk Anda, jangan ragu untuk menghapus cek. ABS adalah cerita yang berbeda karena berjalan di atas paket kompatibilitas dan implementasi dukungan. FragmentActivity dapat menggunakan implementasi yang sama dari FragmentManager dan voila: masalah yang sama.
gunar
@ t0mm13b: untuk menambahkan lebih dari 600 karakter tidak cukup, Anda harus menyelidiki terlebih dahulu apa yang sebenarnya menyebabkan ini untuk Anda. Anda juga harus menyadari bahwa peretasan di atas adalah jelek dan saya tidak bertanggung jawab jika itu berjalan atau tidak (bagi saya adalah solusi terbaik mengingat keadaan). Jika Anda harus menggunakannya, periksa dua kali dalam kode sumber compat untuk penamaan variabel karena mungkin berbeda dari paket standar. Saya berharap masalah ini akan ditangani oleh versi paket kompatibilitas berikutnya, tetapi dari pengalaman Android, ada beberapa peluang untuk terjadi ...
gunar
Uhhh ... Persisnya sama dengan laporan bug ini yang merupakan inti dari pertanyaan OP ini. Saya mendukung komentar saya - Anda seharusnya secara eksplisit memasukkan penafian dan mengatakan itu tidak dijamin dan juga harus menyatakan bahwa Anda tidak menggunakan fragmen - jika tidak, mengapa repot-repot memposting jawaban itu juga! :) Hanya mengatakan ...
t0mm13b
35

Pengecualian seperti itu akan terjadi jika Anda mencoba melakukan transisi fragmen setelah aktivitas fragmen Anda onSaveInstanceState()dipanggil.

Salah satu alasan ini bisa terjadi, adalah jika Anda membiarkan AsyncTask(atau Thread) menjalankan ketika suatu aktivitas dihentikan.

Setiap transisi setelah onSaveInstanceState()dipanggil dapat berpotensi hilang jika sistem mengklaim kembali aktivitas untuk sumber daya dan membuatnya kembali nanti.

FunkTheMonk
sumber
2
Hei Funk, saya punya pertanyaan di sini, bagaimana bisa onBackPressed dapat dipanggil pada aktivitas atau fragmen itu, jika aktivitas atau fragmen itu telah dihentikan. Pengecualian di atas tampaknya dihasilkan dari beberapa acara UI (yaitu menekan tombol BACK), saya tidak dapat mengetahui hubungan antara Tugas Async dan tombol Kembali sekalipun.
dcool
Karena Anda dapat menyimpan transisi fragmen ke kondisi kembali, menekan tombol kembali dapat menyebabkan kebalikan dari transisi yang Anda simpan (sehingga fragmen lama kembali). onSaveInstanceState dipanggil sebelum aktivitas Anda dihancurkan untuk mengembalikan sumber daya ke sistem, tidak selalu setelah onStop dipanggil. Maaf, jawaban saya tidak begitu jelas.
FunkTheMonk
Funk, tetapi saya tidak menggunakan Fragmen apa pun dalam aplikasi saya. Mungkin fragmen yang digunakan dalam kode asli. sebelumnya saya pikir Anda berbicara tentang hal yang sama.
dcool
1
Saya punya AsyncTask dengan referensi ke sebuah fragmen. Masalah terpecahkan setelah menghapus panggilan super () dari onSaveInstanceState dan mengganti referensi dari AsyncTask saya dengan WeakReference <Fragment>.
Buffalo
2
@Buffalo Itu jelas bukan solusi untuk masalah ini. Anda harus selalu menelepon super.onSaveInstanceState().
Alex Lockwood
27

Cukup panggil super.onPostResume () sebelum menampilkan fragmen Anda atau pindahkan kode Anda di metode onPostResume () setelah memanggil super.onPostResume (). Ini menyelesaikan masalah!

MJ.Ahmadi
sumber
6
Memanggil onPostResume () memastikan bahwa onResumeFragments () dipanggil dan bagi saya ini adalah solusi yang ideal.
j2emanue
20

Ini juga dapat terjadi ketika memanggil dismiss()fragmen dialog setelah layar dikunci \ blanked dan keadaan instance dialog + Aktivitas telah disimpan. Untuk menyiasati panggilan ini:

dismissAllowingStateLoss()

Secara harfiah setiap kali saya menolak dialog saya tidak peduli tentang keadaan itu lagi, jadi ini tidak masalah untuk dilakukan - Anda sebenarnya tidak kehilangan status apa pun.

Brian Dilley
sumber
2
Ini adalah masalah saya! Tuan yang brilian!
Tash Pemhiwa
17

Solusi singkat dan bekerja:

Ikuti Langkah Sederhana:

Langkah 1 : Ganti status onSaveInstanceState di masing-masing fragmen. Dan hapus metode super dari itu.

@Override
public void onSaveInstanceState(Bundle outState) {
};

Langkah 2 : Gunakan CommitAllowingStateLoss (); alih-alih komit (); sementara operasi fragmen.

fragmentTransaction.commitAllowingStateLoss();
Basbous
sumber
1
Menghapus metode super berhasil, bisakah Anda menjelaskan mengapa? Apakah aman untuk menghapus itu?
Bruce
7
itu tidak aman untuk menghapus super (), Anda akan kehilangan conf data lain setelah itu!
deadfish
7

ini bekerja untuk saya ... menemukan ini sendiri ... semoga membantu Anda!

1) JANGAN memiliki global "statis" FragmentManager / FragmentTransaction.

2) onCreate, SELALU menginisialisasi FragmentManager lagi!

contoh di bawah ini: -

public abstract class FragmentController extends AnotherActivity{
protected FragmentManager fragmentManager;
protected FragmentTransaction fragmentTransaction;
protected Bundle mSavedInstanceState;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    mSavedInstanceState = savedInstanceState;
    setDefaultFragments();
}

protected void setDefaultFragments() {
    fragmentManager = getSupportFragmentManager();
    //check if on orientation change.. do not re-add fragments!
    if(mSavedInstanceState == null) {
        //instantiate the fragment manager

        fragmentTransaction = fragmentManager.beginTransaction();

        //the navigation fragments
        NavigationFragment navFrag = new NavigationFragment();
        ToolbarFragment toolFrag = new ToolbarFragment();

        fragmentTransaction.add(R.id.NavLayout, navFrag, "NavFrag");
        fragmentTransaction.add(R.id.ToolbarLayout, toolFrag, "ToolFrag");
        fragmentTransaction.commitAllowingStateLoss();

        //add own fragment to the nav (abstract method)
        setOwnFragment();
    }
}
kurayami88
sumber
6

Saya selalu mendapatkan ini ketika saya mencoba untuk menunjukkan fragmen dalam metode onActivityForResult (), jadi masalahnya adalah berikutnya:

  1. Aktivitas saya dijeda dan dihentikan, yang berarti onSaveInstanceState () sudah dipanggil (untuk perangkat pra-Honeycomb dan pasca-Honeycomb).
  2. Jika ada hasil, saya melakukan transaksi untuk menampilkan / menyembunyikan fragmen, yang menyebabkan IllegalStateException ini.

Apa yang saya buat selanjutnya:

  1. Nilai tambah untuk menentukan apakah tindakan yang saya inginkan telah dilakukan (mis. Mengambil foto dari camere - isPhotoTaken) - itu bisa berupa nilai boolean atau bilangan bulat tergantung berapa banyak transaksi yang Anda butuhkan.
  2. Di overriden metode onResumeFragments () saya memeriksa nilai saya dan setelah melakukan transaksi fragmen yang saya butuhkan. Dalam hal ini commit () tidak dilakukan setelah onSaveInstanceState, karena keadaan dikembalikan dalam metode onResumeFragments ().
Himpunan
sumber
5

Saya memecahkan masalah dengan perubahan konfigurasi. Caranya adalah bahwa menurut siklus hidup aktivitas android, ketika Anda secara eksplisit disebut niat (niat kamera, atau yang lainnya); aktivitas dijeda dan onsavedInstance dipanggil dalam kasus itu. Saat memutar perangkat ke posisi lain selain dari yang selama aktivitas itu aktif; melakukan operasi fragmen seperti fragmen komit menyebabkan pengecualian keadaan ilegal. Ada banyak keluhan tentang hal itu. Ini adalah sesuatu tentang manajemen siklus aktivitas Android dan pemanggilan metode yang tepat. Untuk mengatasinya saya lakukan ini: 1-Override metode onsavedInstance dari aktivitas Anda, dan tentukan orientasi layar saat ini (potret atau lanskap) kemudian atur orientasi layar Anda ke sana sebelum aktivitas Anda dijeda. dengan cara itu aktivitas Anda mengunci rotasi layar untuk aktivitas Anda seandainya telah diputar oleh yang lain. 2-kemudian, ganti metode aktivitas onresume, dan atur mode orientasi Anda sekarang ke sensor sehingga setelah metode yang dipanggil dipanggil akan memanggil sekali lagi konfigurasi untuk menangani rotasi dengan benar.

Anda dapat menyalin / menempelkan kode ini ke aktivitas Anda untuk menghadapinya:

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

    Toast.makeText(this, "Activity OnResume(): Lock Screen Orientation ", Toast.LENGTH_LONG).show();
    int orientation =this.getDisplayOrientation();
    //Lock the screen orientation to the current display orientation : Landscape or Potrait
    this.setRequestedOrientation(orientation);
}

//A method found in stackOverflow, don't remember the author, to determine the right screen orientation independently of the phone or tablet device 
public int getDisplayOrientation() {
    Display getOrient = getWindowManager().getDefaultDisplay();

    int orientation = getOrient.getOrientation();

    // Sometimes you may get undefined orientation Value is 0
    // simple logic solves the problem compare the screen
    // X,Y Co-ordinates and determine the Orientation in such cases
    if (orientation == Configuration.ORIENTATION_UNDEFINED) {
        Configuration config = getResources().getConfiguration();
        orientation = config.orientation;

        if (orientation == Configuration.ORIENTATION_UNDEFINED) {
        // if height and widht of screen are equal then
        // it is square orientation
            if (getOrient.getWidth() == getOrient.getHeight()) {
                orientation = Configuration.ORIENTATION_SQUARE;
            } else { //if widht is less than height than it is portrait
                if (getOrient.getWidth() < getOrient.getHeight()) {
                    orientation = Configuration.ORIENTATION_PORTRAIT;
                } else { // if it is not any of the above it will defineitly be landscape
                    orientation = Configuration.ORIENTATION_LANDSCAPE;
                }
            }
        }
    }
    return orientation; // return value 1 is portrait and 2 is Landscape Mode
}

@Override
public void onResume() {
    super.onResume();
    Toast.makeText(this, "Activity OnResume(): Unlock Screen Orientation ", Toast.LENGTH_LONG).show();
    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);
} 
douggynix
sumber
4

Saya memiliki masalah yang sama, mendapatkan IllegalStateException, tetapi mengganti semua panggilan saya untuk melakukan () dengan commitAllowingStateLoss () tidak membantu.

Pelakunya adalah panggilan ke DialogFragment.show ().

Saya mengelilinginya

try {
    dialog.show(transaction, "blah blah");
}
catch(IllegalStateException e) {
    return;
}

dan itu berhasil. OK, saya tidak bisa menampilkan dialog, tetapi dalam hal ini baik-baik saja.

Itu adalah satu-satunya tempat di aplikasi saya di mana saya pertama kali memanggil FragmentManager.beginTransaction () tetapi tidak pernah disebut commit () jadi saya tidak menemukannya ketika saya mencari "commit ()".

Lucunya, pengguna tidak pernah meninggalkan aplikasi. Sebaliknya si pembunuh adalah iklan pengantara AdMob yang muncul.

Roger CS Wernersson
sumber
3
Sama disini. Saya telah memecahkan Overriding metode 'show (FragmentManager manager, Tag tag)', mengganti 'commit' dengan 'commitAllowingStateLoss'; kehilangan sesuatu karena saya tidak dapat menetapkan dua atribut pribadi dari Dialog: mDismissed dan mShownByMe. Tetapi tampaknya bekerja setiap waktu :)
Francesco Ditrani
Saya telah membuat solusi alternatif untuk DialogFragment, yang dapat menghindari pengecualian ini: github.com/AndroidDeveloperLB/DialogShard
pengembang android
4

Solusi saya untuk masalah itu adalah

Dalam metode tambahkan fragmen:

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    ...
    guideMapFragment = (SupportMapFragment)a.getSupportFragmentManager().findFragmentById(R.id.guideMap);
    guideMap = guideMapFragment.getMap();
    ...
}

@Override
public void onDestroyView() {
    SherlockFragmentActivity a = getSherlockActivity();
    if (a != null && guideMapFragment != null) {
        try {
            Log.i(LOGTAG, "Removing map fragment");
            a.getSupportFragmentManager().beginTransaction().remove(guideMapFragment).commit();
            guideMapFragment = null;
        } catch(IllegalStateException e) {
            Log.i(LOGTAG, "IllegalStateException on exit");
        }
    }
    super.onDestroyView();
}

Mungkin buruk, tetapi tidak dapat menemukan yang lebih baik.

mc.dev
sumber
Trues .. menangkap pengecualian dapat menghindari crash aplikasi, tetapi masalah perilaku fragmen tersebut dibiarkan di layar atau tidak bisa ditambahkan.
Marcos Vasconcelos
4
terus bergulir. kebenaran ada di luar sana
dan
4

Saya mendapatkan masalah ini. Namun saya pikir masalah ini tidak terkait dengan commit dan commitAllowStateLoss.

Tumpukan jejak jejak dan pesan pengecualian berikut adalah tentang commit ().

java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
at android.support.v4.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1341)
at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1352)
at android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:595)
at android.support.v4.app.BackStackRecord.commit(BackStackRecord.java:574)

Tetapi pengecualian ini disebabkan oleh onBackPressed ()

java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
at android.support.v4.app.FragmentManagerImpl.checkStateLoss(Unknown Source)
at android.support.v4.app.FragmentManagerImpl.popBackStackImmediate(Unknown Source)
at android.support.v4.app.FragmentActivity.onBackPressed(Unknown Source)

Mereka semua disebabkan oleh checkStateLoss ()

private void checkStateLoss() {
    if (mStateSaved) {
        throw new IllegalStateException(
                "Can not perform this action after onSaveInstanceState");
    }
    if (mNoTransactionsBecause != null) {
        throw new IllegalStateException(
                "Can not perform this action inside of " + mNoTransactionsBecause);
    }

mStateSaved akan benar setelah onSaveInstanceState.

Masalah ini jarang terjadi. Saya belum pernah mengalami masalah ini. Saya tidak dapat mengulangi masalah ini.

Saya menemukan masalah 25517

Itu mungkin terjadi dalam keadaan berikut

  1. Tombol kembali dipanggil setelah onSaveInstanceState, tetapi sebelum aktivitas baru dimulai.

  2. gunakan onStop () dalam kode

Saya tidak yakin apa akar masalahnya. Jadi saya menggunakan cara yang jelek.

@Override
public void onBackPressed() {

    try{
        super.onBackPressed();
    }catch (IllegalStateException e){
        // can output some information here
        finish();
    }
}
oO_ox
sumber
Saya tidak benar-benar menyelesaikan masalah, tetapi masalah ini tidak terkait dengan commit dan commitAllowStateLoss.
oO_ox
4

Saya punya masalah yang sama di Aplikasi saya. Saya telah memecahkan masalah ini dengan memanggil super.onBackPressed();kelas sebelumnya dan memanggil commitAllowingStateLoss()kelas saat ini dengan fragmen itu.

Peter
sumber
2
Terima kasih. Solusi ini menyelesaikan masalah uisng commitAllowingStateLoss()alih-alihcommit()
Chintak Patel
Hindari penggunaan commitAllowingStateLoss () medium.com/@elye.project/…
swooby
3

onSaveInstance akan dipanggil jika pengguna memutar layar sehingga dapat memuat sumber daya yang terkait dengan orientasi baru.

Ada kemungkinan bahwa pengguna ini memutar layar diikuti dengan menekan tombol kembali (karena itu juga mungkin bahwa pengguna ini meraba-raba ponsel mereka saat menggunakan aplikasi Anda)

dols
sumber
2
Meskipun perubahan konfigurasi (seperti perubahan orientasi) dapat menghasilkan pengecualian ini, mereka bukan penyebab utama.
Alex Lockwood
2

Masalah yang sama dari saya dan setelah sehari analisis semua artikel, blog dan stackoverflow saya telah menemukan solusi sederhana. Jangan gunakan saveInstanceState sama sekali, ini adalah kondisi dengan satu baris kode. Pada kode fragmen:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(null);
    .....
Tobia Caneschi
sumber
2

Ini terjadi setiap kali Anda mencoba memuat fragmen tetapi aktivitas telah mengubah statusnya menjadi onPause (). Ini terjadi misalnya ketika Anda mencoba mengambil data dan memuatnya ke aktivitas tetapi pada saat pengguna telah mengklik beberapa tombol dan telah dipindahkan ke aktivitas berikutnya.

Anda dapat menyelesaikan ini dengan dua cara

Anda dapat menggunakan transaction.commitAllowingStateLoss () alih-alih transaction.commit () untuk memuat fragmen tetapi Anda akhirnya bisa kehilangan operasi komit yang dilakukan.

atau

Pastikan aktivitas sedang dalam resume dan tidak akan berhenti saat memuat fragmen. Buat boolean dan periksa apakah aktivitas tidak akan menjadi status onPause ().

@Override
public void onResume() {
    super.onResume();
    mIsResumed = true;
}

@Override
public void onPause() {
    mIsResumed = false;
    super.onPause();
}

lalu sambil memuat fragmen, periksa apakah aktivitas ada dan muat hanya saat aktivitas dilatar depan.

if(mIsResumed){
 //load the fragment
}
Sharath kumar
sumber
1

Terima kasih @gunar, tapi saya rasa ada cara yang lebih baik.

Menurut dokumen:

 * If you are committing a single transaction that does not modify the
 * fragment back stack, strongly consider using
 * {@link FragmentTransaction#commitNow()} instead. This can help avoid
 * unwanted side effects when other code in your app has pending committed
 * transactions that expect different timing.
 *
 * @return Returns true if there were any pending transactions to be
 * executed.
 */
public abstract boolean executePendingTransactions();

Jadi gunakan commitNowuntuk mengganti:

fragmentTransaction.commit();
FragmentManager.executePendingTransactions()
JianxinLi
sumber
0

Nah, setelah mencoba semua solusi di atas tidak berhasil (karena pada dasarnya saya tidak punya transaksi).

Dalam kasus saya, saya menggunakan AlertDialogs dan ProgressDialog sebagai fragmen yang, kadang-kadang, pada rotasi, ketika meminta FragmentManager, kesalahan muncul.

Saya menemukan pemecahan masalah menggabungkan beberapa posting serupa:

Ini adalah solusi 3 langkah, semua dilakukan pada FragmentActivity Anda (dalam hal ini, yang disebut GenericActivity):

private static WeakReference<GenericActivity> activity = null; //To avoid bug for fragments: Step 1 of 3

@Override
protected void onCreate(Bundle savedInstanceState){
    super.onCreate(savedInstanceState);
    //To avoid bug for fragments: Step 2 of 3
    activity = new WeakReference<GenericActivity>(this);
}

@Override
public FragmentManager getSupportFragmentManager(){
    //To avoid bug for fragments: Step 3 of 3
    if (this == activity.get()) {
        return super.getSupportFragmentManager();
    }
    return activity.get().getSupportFragmentManager();
}
Antilope
sumber
0

Ketika saya menggunakan startactivity dalam satu fragmen, saya akan mendapatkan pengecualian ini;

Ketika saya mengubah untuk menggunakan startactivityforresult, pengecualian hilang :)

Jadi cara mudah untuk memperbaikinya adalah menggunakan api startActivityForResult :)

Pertanyaan
sumber
0

Saya mendapatkan pengecualian ini ketika saya menekan tombol kembali untuk membatalkan pemilih pemilih pada aktivitas fragmen peta saya. Saya menyelesaikan ini dengan mengganti kode onResume () (di mana saya menginisialisasi fragmen dan melakukan transaksi) ke onStart () dan aplikasi bekerja dengan baik sekarang. Semoga ini bisa membantu.

DCS
sumber
0

Ini diperbaiki di Android 4.2 dan juga di sumber perpustakaan dukungan. [*]

Untuk detail penyebabnya (dan penyelesaiannya) lihat laporan bug Google: http://code.google.com/p/android/issues/detail?id=19917

Jika Anda menggunakan perpustakaan dukungan maka Anda tidak perlu khawatir tentang bug ini (lama) [*]. Namun, jika Anda menggunakan API secara langsung (mis. Tidak menggunakan FragmentManager perpustakaan dukungan) dan menargetkan API di bawah Android 4.2 maka Anda harus mencoba salah satu solusi.

[*] Pada saat penulisan, Android SDK Manager masih mendistribusikan versi lama yang menunjukkan bug ini.

Edit Saya akan menambahkan beberapa klarifikasi di sini karena saya jelas-jelas bingung siapa pun yang memilih jawaban ini.

Ada beberapa keadaan yang berbeda (namun terkait) yang dapat menyebabkan pengecualian ini dilemparkan . Jawaban saya di atas mengacu pada contoh spesifik yang dibahas dalam pertanyaan yaitu bug di Android yang kemudian diperbaiki. Jika Anda mendapatkan pengecualian ini karena alasan lain, itu karena Anda menambahkan / menghapus fragmen ketika seharusnya tidak (setelah status fragmen disimpan). Jika Anda berada dalam situasi seperti itu, maka mungkin " Nested Fragments - IllegalStateException" Tidak dapat melakukan tindakan ini setelah onSaveInstanceState " " dapat bermanfaat bagi Anda.

Benjamin Dobell
sumber
0

Kasus penggunaan saya: Saya telah menggunakan pendengar secara terpisah untuk memberi tahu aktivitas bahwa sesuatu telah terjadi. Saya melakukan fragmen baru pada metode panggilan balik. Ini bekerja dengan sangat baik pada kali pertama. Tetapi pada perubahan orientasi aktivitas diciptakan kembali dengan keadaan instance disimpan. Dalam hal itu fragmen tidak dibuat lagi menyiratkan bahwa fragmen tersebut memiliki pendengar yang sudah lama dihancurkan aktivitasnya. Bagaimana pun metode panggilan balik akan terpicu saat beraksi. Pergi ke aktivitas yang hancur yang menyebabkan masalah. Solusinya adalah mengatur ulang pendengar secara terpisah dengan aktivitas langsung saat ini. Ini menyelesaikan masalah.

Ganesh Kanna
sumber
0

Apa yang saya temukan adalah bahwa jika aplikasi lain adalah tipe dialog dan memungkinkan sentuhan untuk dikirim ke aplikasi latar belakang maka hampir semua aplikasi latar belakang akan crash dengan kesalahan ini. Saya pikir kita perlu memeriksa setiap kali transaksi dilakukan jika instance disimpan atau dikembalikan.

chris
sumber
0

Dalam kasus saya, dengan pengecualian kesalahan yang sama, saya meletakkan "onBackPressed ()" di runnable (Anda dapat menggunakan salah satu dari tampilan Anda):

myView.post(new Runnable() {
                    @Override
                    public void run() {
                        onBackPressed()
                    }
                });

Saya tidak mengerti mengapa, tetapi itu berhasil!

Alecs
sumber
Posting pada tampilan hanya akan menjalankan Runnable setelah tampilan telah ditata dan ditarik ke layar; ini sering berarti Kegiatan itu sendiri telah sepenuhnya dilanjutkan, karenanya tidak ada masalah
Mercato
0

Anda mungkin memanggil fragmentManager.popBackStackImmediate (); ketika aktivitas dijeda. Aktivitas belum selesai tetapi dijeda dan tidak di latar depan. Anda perlu memeriksa apakah aktivitas dijeda atau tidak sebelum popBackStackImmediate ().

Murat
sumber
0

Saya perhatikan sesuatu yang sangat menarik. Di aplikasi saya ada opsi untuk membuka galeri ponsel dan perangkat menanyakan aplikasi apa yang harus digunakan, di sana saya mengklik area abu-abu menjauh dari dialog dan melihat masalah ini. Saya perhatikan bagaimana aktivitas saya berjalan dari onPause, onSaveInstanceState kembali ke onResume, itu tidak terjadi untuk mengunjungi onCreateView. Saya melakukan transaksi di onResume. Jadi apa yang akhirnya saya lakukan adalah mengatur bendera yang dinegasikan pada jeda, tetapi menjadi benar onCreateView. jika flag benar padaResume maka lakukan onCommit, jika tidak, commitAllowingStateLoss. Saya bisa terus dan menghabiskan begitu banyak waktu tetapi saya ingin memeriksa siklus hidupnya. Saya memiliki perangkat yang merupakan sdkversion 23, dan saya tidak mendapatkan masalah ini, tetapi saya memiliki satu lagi yang 21, dan di sana saya melihatnya.

Juan Mendez
sumber
-1

Anda dapat menggunakan FragmentActivity.onStart sebelum popBackStackImmediate

seperti ini:

public void backStackFragment() {
    this.start();
    getFragmentManager().popBackStackImmediate();
}

public void start(){
    FragmentActivity a = getActivity();
    if(a instanceof DepositPlanPadActivity){
      ((DepositPlanPadActivity)a).onStart();
    }
    if(a instanceof SmallChangePlanPad){
            ((SmallChangePlanPad)a).onStart();
        }
        if(a instanceof UserCenterActivity){
            ((UserCenterActivity)a).onStart();
        }
    }

http://jorryliu.blogspot.com/2014/09/illegalstateexception-can-not-perform.html

pengguna3327339
sumber