IllegalStateException: ViewPager ile onSaveInstanceState sonrasında bu eylem gerçekleştirilemiyor


496

Piyasadaki uygulamamdan kullanıcı raporları alıyorum ve aşağıdaki istisnayı sağlıyorum:

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.onKeyUp(Activity.java:2044)
at android.view.KeyEvent.dispatch(KeyEvent.java:2529)
at android.app.Activity.dispatchKeyEvent(Activity.java:2274)
at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1803)
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:1855)
at com.android.internal.policy.impl.PhoneWindow.superDispatchKeyEvent(PhoneWindow.java:1277)
at android.app.Activity.dispatchKeyEvent(Activity.java:2269)
at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1803)
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.widget.TabHost.dispatchKeyEvent(TabHost.java:297)
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:1855)
at com.android.internal.policy.impl.PhoneWindow.superDispatchKeyEvent(PhoneWindow.java:1277)
at android.app.Activity.dispatchKeyEvent(Activity.java:2269)
at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1803)
at android.view.ViewRoot.deliverKeyEventPostIme(ViewRoot.java:2880)
at android.view.ViewRoot.handleFinishedEvent(ViewRoot.java:2853)
at android.view.ViewRoot.handleMessage(ViewRoot.java:2028)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:132)
at android.app.ActivityThread.main(ActivityThread.java:4028)
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:844)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:602)
at dalvik.system.NativeStart.main(Native Method)

Görünüşe göre kullanmadığım bir FragmentManager ile ilgisi var. Stacktrace kendi sınıflarımı göstermez, bu nedenle bu istisnanın nerede oluştuğu ve nasıl önleneceği hakkında hiçbir fikrim yok.

Kayıt için: Bir tabhost'um var ve her sekmede Etkinlikler arasında geçiş yapan bir ActivityGroup var.


2
Ben de aynı sorunu tartışırken bu soruyu buldum, ama orada da bir çözüm .. stackoverflow.com/questions/7469082/…
nhaarman

3
Kullanmadığınızda FragmentManager, Petek kesinlikle öyle. Bu gerçek Petek tabletlerde mi oluyor? Ya da birileri bir telefonda hacklenmiş bir Petek çalıştırıyor olabilir mi ve zor durumda olan saldırıya uğramış baskı mı?
CommonsWare

1
Hiç bir fikrim yok. Bu, Market Geliştirici Konsolu'nda aldığım tek bilgi, kullanıcı mesajı da yararlı bilgi
içermiyor

Android 3.0.1 ile 11 seans gösteren Flurry kullanıyorum ve bu istisna hakkında 11 raporum var. Yine de tesadüf olabilir. Android 3.1 ve 3.2 sırasıyla 56 ve 38 oturuma sahiptir.
nhaarman

Market hata raporunda bir 'Platform' bölümü var, bazen içinde cihazın Android sürümü var.
Nikolay Elenkov

Yanıtlar:


720

Lütfen cevabımı buradan kontrol edin . Temelde sadece:

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

Çağrısı yapmayın super()üzerinde saveInstanceStateyöntemle. Bu işleri karıştırıyordu ...

Bu, destek paketinde bilinen bir hatadır .

Örneği kaydetmeniz ve bir şey eklemeniz gerekiyorsa outState Bundleaşağıdakileri kullanabilirsiniz:

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

Sonunda (çözümlerde görüldüğü gibi) uygun çözüm kullanılacaktı:

transaction.commitAllowingStateLoss();

ekleme veya yaparken FragmentTransactionneden olduğunu Exception.


370
Commit () yerine commitAllowingStateLoss () kullanmalısınız
meh

18
CommitAllowingStateLoss () ile ilgili bu yorum kendi başına bir cevaptır - bu şekilde göndermelisiniz.
Risadinha

20
'CommitAllowingStateLoss' - /> "Bu tehlikelidir, çünkü aktivitenin daha sonra durumundan geri yüklenmesi gerektiğinde taahhüt kaybedilebilir, bu nedenle bu sadece UI durumunun beklenmedik şekilde değişmesinin uygun olduğu durumlarda kullanılmalıdır. Kullanıcı."
Aralık'ta kod değiştirildi

10
Eğer v4 kaynağına bakarsam popBackStackImmediate, durum kaydedilmişse derhal başarısız olur. Önceden parçayı eklemek commitAllowingStateLosshiçbir parçayı oynatmaz. Testlerim bunun doğru olduğunu gösteriyor. Bu özel istisna üzerinde hiçbir etkisi yoktur. İhtiyacımız olan bir popBackStackImmediateAllowingStateLossyöntem.
Synesso

3
@DanieleB evet, burada bir cevap gönderdim. Ama aslında Otto mesaj veriyolunu kullanarak daha iyi bir çözüm buldum: Parçayı abone olarak kaydedin ve otobüsün asenkron sonucunu dinleyin. Duraklatma sırasında kaydı silin ve özgeçmişte yeniden kayıt olun. Zaman uyumsuzluğunun, tamamlandığı ve parçanın duraklatıldığı zamanlar için bir Üretme yöntemine de ihtiyacı vardır. Zamanım geldiğinde cevabımı daha ayrıntılı olarak güncelleyeceğim.
Synesso

130

Benzer bir hata mesajıyla ilgili birçok sorun var. Bu yığın izinin ikinci satırını kontrol edin. Bu istisna özellikle çağrı ile ilgilidir FragmentManagerImpl.popBackStackImmediate.

Bu yöntem çağrısı gibi popBackStack, olacak her zaman başarısız IllegalStateExceptionoturum durumu zaten kaydedildi eğer. Kaynağı kontrol edin. Bu istisnanın atılmasını durdurmak için yapabileceğiniz hiçbir şey yoktur.

  • Çağrısını kaldırmak super.onSaveInstanceStateyardımcı olmaz.
  • Parçayı ile oluşturmak commitAllowingStateLossyardımcı olmaz.

Sorunu şu şekilde gözlemledim:

  • Gönder düğmesi olan bir form var.
  • Düğmeye tıklandığında bir iletişim kutusu oluşturulur ve zaman uyumsuz bir işlem başlar.
  • Kullanıcı işlem bitmeden ev anahtarını tıklar - onSaveInstanceStateçağrılır.
  • İşlem tamamlanır, geri arama yapılır ve popBackStackImmediatedenenir.
  • IllegalStateException Atıldı.

İşte bunu çözmek için yaptım:

IllegalStateExceptionGeri aramada kaçınmak mümkün olmadığından , yakalayın ve yok sayın.

try {
    activity.getSupportFragmentManager().popBackStackImmediate(name);
} catch (IllegalStateException ignored) {
    // There's no way to avoid getting this if saveInstanceState has already been called.
}

Bu uygulamanın çökmesini durdurmak için yeterlidir. Ancak şimdi kullanıcı uygulamayı geri yükleyecek ve bastıklarını düşündükleri düğmenin hiç basılmadığını görecek (düşünüyor). Form parçası hala gösteriyor!

Bunu düzeltmek için, iletişim kutusu oluşturulduğunda, işlemin başladığını belirtmek için bir durum belirtin.

progressDialog.show(fragmentManager, TAG);
submitPressed = true;

Ve bu durumu pakete kaydedin.

@Override
public void onSaveInstanceState(Bundle outState) {
    ...
    outState.putBoolean(SUBMIT_PRESSED, submitPressed);
}

Tekrar yüklemeyi unutmayın onViewCreated

Ardından, devam ettirirken, daha önce gönderme denendiğinde parçaları geri alın. Bu, kullanıcının gönderilmemiş bir form gibi görünen şeye geri dönmesini önler.

@Override
public void onResume() {
    super.onResume();
    if (submitPressed) {
        // no need to try-catch this, because we are not in a callback
        activity.getSupportFragmentManager().popBackStackImmediate(name);
        submitPressed = false;
    }
}

5
Burada ilginç bir okuma: androiddesignpatterns.com/2013/08/…
Pascal

DialogFragment kullanıyorsanız, burada bir alternatif yaptım: github.com/AndroidDeveloperLB/DialogShard
android geliştirici

Ya popBackStackImmediateAndroid'in kendisi tarafından çağrıldıysa?
Kimi Chiu

1
Kesinlikle harika. Bu kabul edilen cevap olmalı. Çok teşekkür ederim! Belki sendPressed = false; sonra popBackStackInmediate.
Neonigma

Genel void onSaveInstanceState (Bundle outState) yöntemini kullanmadım. OnSaveInstanceState (Bundle outState) adlı genel boşluk için boş yöntem ayarlamam gerekir mi?
Faxriddin Abdullayev

55

isFinishing()Parçayı göstermeden önce aktivitenin olup olmadığını kontrol edin ve dikkat edin commitAllowingStateLoss().

Misal:

if(!isFinishing()) {
FragmentManager fm = getSupportFragmentManager();
            FragmentTransaction ft = fm.beginTransaction();
            DummyFragment dummyFragment = DummyFragment.newInstance();
            ft.add(R.id.dummy_fragment_layout, dummyFragment);
            ft.commitAllowingStateLoss();
}

1
! isFinishing () &&! isDestroyed () benim için çalışmıyor.
Allen Vork

! isFinishing () &&! isDestroyed () benim için çalıştı, ancak API 17 gerektiriyor. Ancak basitçe bir DialogFragment . Bkz stackoverflow.com/questions/15729138/... , diğer iyi çözümleri için stackoverflow.com/a/41813953/2914140 bana yardımcı oldu.
CoolMind

29

Ekim 2017'dir ve Google, Lifecycle bileşeni olarak adlandırılan yeni şeylerle Android Destek Kütüphanesi yapar. Bu 'onSaveInstanceState sonrasında bu eylem gerçekleştirilemiyor' sorunu için bazı yeni fikirler sağlar.

Kısacası:

  • Parçanızı açmak için doğru zaman olup olmadığını belirlemek için yaşam döngüsü bileşenini kullanın.

Açıklamalı daha uzun versiyon:

  • bu sorun neden ortaya çıkıyor?

    Bunun nedeni FragmentManager, sizin için bir işlem yapmak için aktivitenizden (ki sanırım parçanızı tutacak mı?) Kullanmaya çalışmanızdır . Genellikle bu, yaklaşan bir parça için bazı işlemler yapmaya çalıştığınıza benziyor, bu arada ana bilgisayar etkinliği zaten çağrı savedInstanceStateyöntemini kullanıyor (kullanıcı ana düğmeye dokunuyor olabilir, bu nedenle etkinlik çağırıyor onStop(), benim durumumda neden)

    Genellikle bu sorun olmamalıdır - her zaman en başta aktiviteye parçayı yüklemeye çalışırız. onCreate() yöntem bunun için mükemmel bir yer. Ancak bazen bu gerçekleşir , özellikle bu aktiviteye hangi parçayı yükleyeceğinize karar veremediğinizde veya bir AsyncTaskbloktan parçayı yüklemeye çalıştığınızda (veya herhangi bir şey biraz zaman alacak). Parça işleminden önce geçen süre, ancak etkinliğin onCreate()yönteminden sonra , kullanıcı her şeyi yapabilir. Kullanıcı etkinliğin onSavedInstanceState()yöntemini tetikleyen ana sayfa düğmesine basarsa, bir can not perform this actionkilitlenme olur.

    Herkes bu konuda daha derin görmek istiyorsanız, ben onlara bu blog yazısı bir göz atın öneririz . Kaynak kodu katmanının derinliklerine bakar ve hakkında çok şey açıklar. Ayrıca, bu çökmeyi commitAllowingStateLoss()geçici olarak çözmek için yöntemi kullanmamanızın nedenini verir (güven bana, kodunuz için iyi bir şey sunmuyor)

  • Bunu nasıl düzeltirim?

    • Kullanmalı mıyım commitAllowingStateLoss() yüklemek yöntem ? Hayır yapmamalısınız ;

    • onSaveInstanceStateYöntemi geçersiz kılsam, yoksaysuper içindeki ? Hayır yapmamalısınız ;

    • isFinishingAna bilgisayar etkinliğinin parça işlemi için doğru anda olup olmadığını kontrol etmek için sihirli iç etkinliği kullanmalı mıyım ? Evet bu benziyor doğru bir yol .

  • Lifecycle bileşeninin neler yapabileceğine bir göz atın .

    Temel olarak, Google AppCompatActivitysınıf içinde bazı uygulamalar yapar (ve projenizde kullanmanız gereken birkaç temel sınıf), bu da mevcut yaşam döngüsü durumunu belirlemeyi . Sorunumuza bir bakın: bu sorun neden olur? Çünkü yanlış zamanda bir şeyler yapıyoruz. Bu yüzden yapmamaya çalışıyoruz ve bu sorun ortadan kalkacak.

    Kendi projem için biraz kod yazıyorum, işte burada kullanıyorum LifeCycle. Kotlin'de kod yazıyorum.

val hostActivity: AppCompatActivity? = null // the activity to host fragments. It's value should be properly initialized.

fun dispatchFragment(frag: Fragment) {
    hostActivity?.let {
       if(it.lifecyclecurrentState.isAtLeast(Lifecycle.State.RESUMED)){
           showFragment(frag)
       }
    }
}

private fun showFragment(frag: Fragment) {
    hostActivity?.let {
        Transaction.begin(it, R.id.frag_container)
                .show(frag)
                .commit()
    }

Yukarıda gösterdiğim gibi. Ana bilgisayar etkinliğinin yaşam döngüsü durumunu kontrol edeceğim. Destek kitaplığı içindeki Yaşam Döngüsü bileşeni ile bu daha spesifik olabilir. Kod lifecyclecurrentState.isAtLeast(Lifecycle.State.RESUMED), şu anki durum en azından onResumeondan sonra değil mi demektir? Bu, yöntemimin başka bir yaşam durumunda (örneğin onStop) yürütülmemesini sağlar .

  • Her şey bitti mi?

    Tabii ki değil. Gösterdiğim kod, uygulamanın çökmesini önlemek için yeni bir yol anlatıyor. Ama eğer durumuna giderse onStop, bu kod satırı bir şey yapmaz ve böylece ekranda hiçbir şey göstermez. Kullanıcılar uygulamaya geri döndüklerinde boş bir ekran görecekler, bu hiç bir parça göstermeyen boş ana bilgisayar etkinliği. Kötü bir deneyim (evet bir kazadan biraz daha iyi).

    Yani burada daha güzel bir şey olabilir keşke: daha sonra yaşam durumuna gelirse uygulama çökmez onResume , işlem yöntemi yaşam durumu farkında; ayrıca, kullanıcı uygulamamıza geri döndükten sonra etkinlik bu parça işlemi işlemini tamamlamaya devam edecektir.

    Bu yönteme bir şey daha ekliyorum:

class FragmentDispatcher(_host: FragmentActivity) : LifecycleObserver {
    private val hostActivity: FragmentActivity? = _host
    private val lifeCycle: Lifecycle? = _host.lifecycle
    private val profilePendingList = mutableListOf<BaseFragment>()

    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    fun resume() {
        if (profilePendingList.isNotEmpty()) {
            showFragment(profilePendingList.last())
        }
    }

    fun dispatcherFragment(frag: BaseFragment) {
        if (lifeCycle?.currentState?.isAtLeast(Lifecycle.State.RESUMED) == true) {
            showFragment(frag)
        } else {
            profilePendingList.clear()
            profilePendingList.add(frag)
        }
    }

    private fun showFragment(frag: BaseFragment) {
        hostActivity?.let {
            Transaction.begin(it, R.id.frag_container)
                    .show(frag)
                    .commit()
        }
    }
}

Bu dispatchersınıfta bir liste tutuyorum , bu parçayı saklamak için işlem işlemini bitirme şansı yok. Ve kullanıcı ana ekrandan döndüğünde ve hala başlatılmayı bekleyen bir parça bulunduğunda, açıklama resume()altındaki yönteme gidecektir @OnLifecycleEvent(Lifecycle.Event.ON_RESUME). Şimdi beklediğim gibi çalışması gerektiğini düşünüyorum.


8
Kotlin yerine Java kullanmak güzel olurdu
Shchvova

1
Neden FragmentDispatcheryalnızca tek bir parça geri yüklenecekse, listeyi kullanımınız bekleyen parçaları depolamak için kullanıyor?
fraherm

21

İşte bu soruna farklı bir çözüm.

Özel üye değişkeni kullanarak, döndürülen verileri daha sonra super.onResume () öğesinden sonra işlenebilecek bir amaç olarak ayarlayabilirsiniz.

Şöyle ki:

private Intent mOnActivityResultIntent = null; 

@Override
protected void onResume() {
    super.onResume();
    if(mOnActivityResultIntent != null){
        ... do things ...
        mOnActivityResultIntent = null;
    }
 }

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data){
    if(data != null){
        mOnActivityResultIntent = data;
    }
}

7
Yaptığınız eyleme izin verilmediğine bağlı olarak, bunun onResume () öğesinden daha geç bir ana geçmesi gerekebilir. İnsatcne için, sorun FragmentTransaction.commit () ise, bunun yerine onPostResume () öğesine gitmesi gerekir.
pjv

1
Benim için bu sorunun cevabı bu. Alınan bir NFC etiketini önceki etkinliğe yönlendirmem gerektiğinden, bunu benim için yaptı.
Janis Peisenieks

7
Benim için oluyordu çünkü aramıyordum super.onActivityResult().
Sufian

20

Kısa Ve Çalışma Çözümü:

Basit Adımları İzleyin

adımlar

Adım 1: İlgili parçadaki onSaveInstanceStatedurumu geçersiz kılın . Ve süper yöntemi ondan kaldırın.

 @Override
public void onSaveInstanceState( Bundle outState ) {

}  

2. Adım: Kullanın fragmentTransaction.commitAllowingStateLoss( );

fragmentTransaction.commit( ); fragman operasyonları yerine .


Cevap kopyalanmış veya edilmez sının birçok deneme yanılma yoluyla var benim çalışma çözümü ile insanlara yardım gönderildiğine ilişkin başka hakemli formu
Vinayak

12

DİKKAT , kullanmak transaction.commitAllowingStateLoss()kullanıcı için kötü bir deneyime neden olabilir. Bu özel durum nedeniyle ilgili daha fazla bilgi için, bkz Bu yayını .


6
Bu soruya cevap vermez, soru için geçerli bir cevap vermelisiniz
Umar Ata

10

Bu tür bir sorun için kirli bir çözüm buldum. Eğer hala ActivityGroupsherhangi bir sebepten ötürü tutmak istiyorsanız (zaman sınırlama nedenlerim vardı),

public void onBackPressed() {}

Kendi bünyesinde Activityve bazı yapmak backkod var. eski Cihazlarda böyle bir Yöntem olmasa bile, bu Yöntem daha yenileri tarafından çağrılır.


6

CommitAllowingStateLoss () kullanmayın, yalnızca kullanıcı arayüzünün kullanıcı üzerinde beklenmedik bir şekilde değişmesinin uygun olduğu durumlarda kullanılmalıdır.

https://developer.android.com/reference/android/app/FragmentTransaction.html#commitAllowingStateLoss ()

ParentFragment öğesinin ChildFragmentManager öğesinde işlem gerçekleşirse, bunun yerine denetlemek için parentFragment.isResume () yöntemini kullanın.

if (parentFragment.isResume()) {
    DummyFragment dummyFragment = DummyFragment.newInstance();
    transaction = childFragmentManager.BeginTransaction();
    trans.Replace(Resource.Id.fragmentContainer, startFragment);
}

5

Benzer bir sorun yaşadım, senaryo şöyle idi:

  • Etkinliğim, liste parçaları ekliyor / değiştiriyor.
  • Her liste parçasının, bir liste öğesine tıklandığında etkinliği (gözlemci modeli) bildirmek için etkinliğe bir referansı vardır.
  • Her liste parçası setRetainInstance (true) öğesini çağırır ; onun içinde onCreate yöntem.

OnCreate yöntem aktivitesi , bu gibi:

mMainFragment = (SelectionFragment) getSupportFragmentManager()
                .findFragmentByTag(MAIN_FRAGMENT_TAG);
        if (mMainFragment == null) {
            mMainFragment = new SelectionFragment();

            mMainFragment.setListAdapter(new ArrayAdapter<String>(this,
                    R.layout.item_main_menu, getResources().getStringArray(
                            R.array.main_menu)));
mMainFragment.setOnSelectionChangedListener(this);
            FragmentTransaction transaction = getSupportFragmentManager()
                    .beginTransaction();
            transaction.add(R.id.content, mMainFragment, MAIN_FRAGMENT_TAG);
            transaction.commit();
        }

Yapılandırma değiştiğinde (aygıt döndürüldüğünde) etkinlik oluşturulduğunda, ana parça parça yöneticisinin geçmişinden alındığı ve aynı zamanda parçanın yok edilen etkinliğe zaten bir OLD referansı olduğu için istisna atılmıştır.

Uygulamanın bu şekilde değiştirilmesi sorunu çözdü:

mMainFragment = (SelectionFragment) getSupportFragmentManager()
                .findFragmentByTag(MAIN_FRAGMENT_TAG);
        if (mMainFragment == null) {
            mMainFragment = new SelectionFragment();

            mMainFragment.setListAdapter(new ArrayAdapter<String>(this,
                    R.layout.item_main_menu, getResources().getStringArray(
                            R.array.main_menu)));
            FragmentTransaction transaction = getSupportFragmentManager()
                    .beginTransaction();
            transaction.add(R.id.content, mMainFragment, MAIN_FRAGMENT_TAG);
            transaction.commit();
        }
        mMainFragment.setOnSelectionChangedListener(this);

parçaların etkinliğin eski yok edilen örneklerine referansları olduğu durumlardan kaçınmak için etkinlik her oluşturulduğunda dinleyicilerinizi ayarlamanız gerekir.


5

Eğer devralınırsanız FragmentActivity, üst sınıfı şu adresten çağırmalısınız onActivityResult():

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

Bunu yapmazsanız ve bu yöntemde bir parça iletişim kutusu göstermeye çalışırsanız, OP'ler alabilirsiniz IllegalStateException. (Dürüst olmak gerekirse, süper çağrının sorunu neden düzelttiğini tam olarak anlamıyorum . Daha onActivityResult()önce çağrıldığından onResume(), bir parça iletişim kutusu gösterilmesine izin verilmemelidir.)


1
Bunun neden sorunu çözdüğünü bilmek isterim.
Büyük McLargeHuge

3

Harita parçası etkinliğindeki niyet seçiciyi iptal etmek için geri düğmesine basarken bu istisnayı alıyordum. Ben ontart (nerede i parçası başlatılması) ontart () kodunu değiştirerek bu sorunu çözdü ve uygulama iyi çalışıyor.Hope yardımcı olur.


2

Bence kullanmak transaction.commitAllowingStateLoss();en iyi çözüm değil. Bu özel durum, etkinliğin yapılandırması değiştiğinde ve parça onSavedInstanceState()çağrıldığında atılır ve daha sonra zaman uyumsuz geri çağırma yönteminiz parça işlemeye çalışır.

Basit çözüm, etkinliğin yapılandırmayı değiştirip değiştirmediğini kontrol etmek olabilir

örneğin kontrol et isChangingConfigurations()

yani

if(!isChangingConfigurations()) { //commit transaction. }

Ödemeye bu kuyunun olarak linki


Her nasılsa kullanıcı bir şey tıkladığında bu istisna var (tıklama işlem-taahhüt yapmak için tetikleyicidir). Bu nasıl olabilir? Çözümünüz burada olur mu?
android geliştirici

@androiddeveloper kullanıcı tıklamasıyla başka neler yapıyorsunuz. İşlemi gerçekleştirmeden önce bir şekilde parça durumunu kurtarıyor
Amol Desai

İstisna, kesin işlem taahhüdü satırında atıldı. Ayrıca, garip bir yazım vardı: "burada" yerine "burada çalışmak" demek istedim.
android geliştirici

@androiddeveloper haklısın! ama işlem yapmadan önce herhangi bir arka plan iş parçacığı ya da bir şey yumurtlama?
Amol Desai

Ben öyle düşünmüyorum (ofis dışında olduğum için üzgünüm), ama neden önemli? Burada tüm UI şeyler ... Bir arka plan iş parçacığında bir şey yaparsam, orada istisnalar olurdu, artı çok riskli olduğu için arka plan iş parçacıklarına UI ile ilgili şeyler koymuyorum.
android geliştirici

2

Muhtemelen benim durumumda bulduğum en pürüzsüz ve en basit çözüm, etkinlik sonucuna yanıt olarak rahatsız edici parçayı yığından atmaktan kaçınmaktı. Yani bu çağrıyı değiştirmek benim onActivityResult():

popMyFragmentAndMoveOn();

buna:

new Handler(Looper.getMainLooper()).post(new Runnable() {
    public void run() {
        popMyFragmentAndMoveOn();
    }
}

benim durumumda yardımcı oldu.


2

OnActivityResult içinde yapabileceğiniz şeyde bazı FragmentTransaction yapıyorsanız, onActivityResult içinde bazı boolean değerleri ayarlayabilirsiniz, sonra onResume içinde FragmentTransaction öğenizi boolean değerine göre yapabilirsiniz. Lütfen aşağıdaki kodu inceleyin.

@Override
protected void onResume() {
    super.onResume;
    if(isSwitchFragment){
        isSwitchFragment=false;
        bottomNavigationView.getTabAt(POS_FEED).select();
    }
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == FilterActivity.FILTER_REQUEST_EVENT && data != null) {
        isSwitchFragment=true;
    }
}

Lütfen kodu resim olarak göndermeyin, bunun yerine kod biçimlendirmesini kullanın.
Micer

1
Evet bana yardımcı oldu .., Bu hatmi cihazı için kesin ve doğru çözüm ben bu istisna var ve bu basit hile ile çözüldü .. !! Bundan dolayı oy kullandı.
sandhya sasane

2

Nezaket: IllegalStateException için çözüm

Bu konu beni çok fazla rahatsız etti ama neyse ki bunun için somut bir çözüm buldum. Ayrıntılı bir açıklaması burada .

CommitAllowStateloss () kullanmak bu istisnayı engelleyebilir ancak UI düzensizliklerine yol açabilir. Şimdiye kadar, Etkinlik durumu kaybolduktan sonra bir parça yürütmeye çalıştığımızda IllegalStateException ile karşılaştığımızı anladık. Basitçe böyle yapılabilir

İki özel boole değişkeni bildirin

 public class MainActivity extends AppCompatActivity {

    //Boolean variable to mark if the transaction is safe
    private boolean isTransactionSafe;

    //Boolean variable to mark if there is any transaction pending
    private boolean isTransactionPending;

Şimdi onPostResume () ve onPause'da, boolean değişkenimizi isTransactionSafe olarak ayarlıyoruz ve ayarını kaldırıyoruz. Fikir, eylemi yalnızca etkinlik ön planda olduğunda güvenli bir şekilde işaretlemektir, bu nedenle durum kaybı olasılığı yoktur.

/*
onPostResume is called only when the activity's state is completely restored. In this we will
set our boolean variable to true. Indicating that transaction is safe now
 */
public void onPostResume(){
    super.onPostResume();
    isTransactionSafe=true;
}
/*
onPause is called just before the activity moves to background and also before onSaveInstanceState. In this
we will mark the transaction as unsafe
 */

public void onPause(){
    super.onPause();
    isTransactionSafe=false;

}

private void commitFragment(){
    if(isTransactionSafe) {
        MyFragment myFragment = new MyFragment();
        FragmentManager fragmentManager = getFragmentManager();
        FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
        fragmentTransaction.add(R.id.frame, myFragment);
        fragmentTransaction.commit();
    }
}

-Şimdiye kadar yaptığımız şey, IllegalStateException özelliğinden kurtaracak, ancak faaliyet arka plana taşındıktan sonra yapıldıysa işlemlerimiz kaybolacaktır, örneğin, commandAllowStateloss () gibi. Buna yardımcı olmak için isTransactionPending boolean değişkenimiz var

public void onPostResume(){
   super.onPostResume();
   isTransactionSafe=true;
/* Here after the activity is restored we check if there is any transaction pending from
the last restoration
*/
   if (isTransactionPending) {
      commitFragment();
   }
}


private void commitFragment(){

 if(isTransactionSafe) {
     MyFragment myFragment = new MyFragment();
     FragmentManager fragmentManager = getFragmentManager();
     FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
     fragmentTransaction.add(R.id.frame, myFragment);
     fragmentTransaction.commit();
     isTransactionPending=false;
 }else {
     /*
     If any transaction is not done because the activity is in background. We set the
     isTransactionPending variable to true so that we can pick this up when we come back to
foreground
     */
     isTransactionPending=true;
 }
}

2

Parça işlemleri sonra yapılmamalıdır Activity.onStop()! Daha sonra işlem gerçekleştirebilecek herhangi bir geri arama olup olmadığınızı kontrol edin onStop(). Sorunu, aşağıdaki gibi yaklaşımlarla dolaşmaya çalışmak yerine düzeltmek daha iyidir.commitAllowingStateLoss()


1

Destek kitaplığı sürümünden başlayarak 24.0.0 Arayabileceğin FragmentTransaction.commitNow()yerine çağıran eşzamanlı Bu işlem taahhüt yöntemi commit()izledi executePendingTransactions(). Gibi belgeler daha iyi bu yaklaşım diyor ki:

CommitNow çağrısı, executePendingTransactions () öğesinin ardından executePendingTransactions () çağrılmasını tercih eder, çünkü ikincisi şu anda bekleyen tüm işlemleri istenen davranış olsun ya da olmasın yapmaya çalışmanın yan etkisi olacaktır.


1

Etkinliğinize bir parça yüklemeye çalıştığınızda, etkinliğin devam ettiğinden ve durma durumuna geçmeyeceğinden emin olun. Duraklatma durumunda, tamamlanan tamamlama işlemini kaybedebilirsiniz.

Parçayı yüklemek için transaction.commit () yerine transaction.commitAllowingStateLoss () yöntemini kullanabilirsiniz.

veya

Bir boole oluşturun ve etkinliğin duraklatılmayacağını kontrol edin

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

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

ardından parça kontrolü yüklenirken

if(mIsResumed){
//load the your fragment
}


1

@Anthonyeef'in büyük cevabı ile ilgili olarak, Java'da bir örnek kod aşağıdadır:

private boolean shouldShowFragmentInOnResume;

private void someMethodThatShowsTheFragment() {

    if (this.getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.RESUMED)) {
        showFragment();
    } else {
        shouldShowFragmentInOnResume = true;
    }
}

private void showFragment() {
    //Your code here
}

@Override
protected void onResume() {
    super.onResume();

    if (shouldShowFragmentInOnResume) {
        shouldShowFragmentInOnResume = false;
        showFragment();
    }
}

1

PopBackStack () veya popBackStackImmediate () yöntemiyle kilitlenmeniz durumunda lütfen düzeltmeyi deneyin:

        if (!fragmentManager.isStateSaved()) {
            fragmentManager.popBackStackImmediate();
        }

Bu benim için de işe yaradı.


API 26 ve üstü
sürümleri

1

Benim durumumda onActivityResult adlı bir geçersiz kılma yönteminde bu hatayı aldım. Kaztıktan sonra belki daha önce ' süper ' demeliydim.
Ekledim ve işe yaradı

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data); //<--- THIS IS THE SUPPER CALL
    if (resultCode == Activity.RESULT_OK && requestCode == 0) {
        mostrarFragment(FiltroFragment.newInstance())
    }

}

Belki de kodunuzdan önce yaptığınız geçersiz kılma işlemine bir 'süper' eklemeniz gerekir.


1

Kotlin uzantısı

fun FragmentManager?.replaceAndAddToBackStack(
    @IdRes containerViewId: Int,
    fragment: () -> Fragment,
    tag: String
) {
    // Find and synchronously remove a fragment with the same tag.
    // The second transaction must start after the first has finished.
    this?.findFragmentByTag(tag)?.let {
        beginTransaction().remove(it).commitNow()
    }
    // Add a fragment.
    this?.beginTransaction()?.run {
        replace(containerViewId, fragment, tag)
        // The next line will add the fragment to a back stack.
        // Remove if not needed.
        // You can use null instead of tag, but tag is needed for popBackStack(), 
        // see https://stackoverflow.com/a/59158254/2914140
        addToBackStack(tag)
    }?.commitAllowingStateLoss()
}

Kullanımı:

val fragment = { SomeFragment.newInstance(data) }
fragmentManager?.replaceAndAddToBackStack(R.id.container, fragment, SomeFragment.TAG)

() -> Fragmandan önce kaldırabilirsiniz
Claire

@Claire, teşekkürler! Bunu değiştirmek istiyor fragment: Fragmentmusunuz? Evet, bu varyantı denedim, ancak bu durumda tüm durumlarda bir parça oluşturulacaktır (fragmentManager == null olsa bile, ancak bu durumla karşılaşmadım). Cevabı güncelledim ve etiketlemek için null değerini değiştirdim addToBackStack().
CoolMind

1

Bu çökme, bir FragmentTransaction öğesinin sahibi olan Activity'nin yaşam döngüsü zatenSaveInstanceState üzerinde çalıştıktan sonra işlenmesinden kaynaklanır. Bunun nedeni genellikle FragmentTransactions öğesini eşzamansız bir geri aramadan yürütmektir. Daha fazla ayrıntı için bağlantılı kaynağa göz atın.

Parça İşlemleri ve Faaliyet Durumu Kaybı

http://www.androiddesignpatterns.com/2013/08/fragment-transaction-commit-state-loss.html


0

Bunu etkinliğinize ekleyin

@Override
public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    if (outState.isEmpty()) {
        // Work-around for a pre-Android 4.2 bug
        outState.putBoolean("bug:fix", true);
    }
}

0

Bu sorunu da yaşadım ve sorun her zaman içeriğiniz FragmentActivitydeğiştiğinde ortaya çıkıyor (örneğin Ekran yönü değişti, vb.). Bunun için en iyi çözüm, içeriğinizi kendi sayfanızdan güncellemektir FragmentActivity.


0

Bir temel parça oluşturup uygulamamdaki tüm parçaların onu genişletmesini sağladım

public class BaseFragment extends Fragment {

    private boolean mStateSaved;

    @CallSuper
    @Override
    public void onSaveInstanceState(Bundle outState) {
        mStateSaved = true;
        super.onSaveInstanceState(outState);
    }

    /**
     * Version of {@link #show(FragmentManager, String)} that no-ops when an IllegalStateException
     * would otherwise occur.
     */
    public void showAllowingStateLoss(FragmentManager manager, String tag) {
        // API 26 added this convenient method
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            if (manager.isStateSaved()) {
                return;
            }
        }

        if (mStateSaved) {
            return;
        }

        show(manager, tag);
    }
}

Ben kullandığım bir fragman göstermeye çalışacağı Sonra zaman showAllowingStateLossyerineshow

bunun gibi:

MyFragment.newInstance()
.showAllowingStateLoss(getFragmentManager(), MY_FRAGMENT.TAG);

Bu PR'dan bu çözüme geldim: https://github.com/googlesamples/easypermissions/pull/170/files


0

Her durumda yardımcı olup olmadığından emin olmadığım başka bir olası geçici çözüm (köken burada ):

@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
        final View rootView = findViewById(android.R.id.content);
        if (rootView != null) {
            rootView.cancelPendingInputEvents();
        }
    }
}

0

@Ovidiu Latcu tarafından kabul edilmiş bir cevap olduğunu biliyorum, ancak bir süre sonra hata hala devam ediyor.

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

Crashlytics hala bana bu garip hata mesajını gönderiyor.

Ancak şimdi yalnızca sürüm 7 + (Nougat) üzerinde oluşan hata Düzeltme, fragmentTransaction de commit () yerine commandAllowingStateLoss () kullanmak oldu.

Bu gönderi commitAllowingStateLoss () için yararlıdır ve bir daha asla fragman sorunu yaşamamıştır.

Özetle, burada kabul edilen cevap Nougat öncesi android sürümlerinde çalışabilir.

Bu, birkaç saat arama yapılmasını engelleyebilir. mutlu kodlamalar. <3 şerefe

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.