İletişim kutusu gösterildiğinde "Bu eylem onSaveInstanceState'ten sonra gerçekleştirilemiyor" mesajı alıyorum


121

Bazı kullanıcılar, bildirim çubuğundaki hızlı işlemi kullanırlarsa, zorla yaklaşıyorlar.

Bildirimde "TestDialog" sınıfını çağıran hızlı bir işlem gösteriyorum . TestDialog sınıfında "ertele" butonuna bastıktan sonra SnoozeDialog'u göstereceğim.

private View.OnClickListener btnSnoozeOnClick() {
    return new View.OnClickListener() {

        public void onClick(View v) {
            showSnoozeDialog();
        }
    };
}

private void showSnoozeDialog() {
    FragmentManager fm = getSupportFragmentManager();
    SnoozeDialog snoozeDialog = new SnoozeDialog();
    snoozeDialog.show(fm, "snooze_dialog");
}

Hata *IllegalStateException: Can not perform this action after onSaveInstanceState*.

IllegarStateException'ın tetiklendiği kod satırı:

snoozeDialog.show(fm, "snooze_dialog");

Sınıf "FragmentActivity" yi genişletiyor ve "SnoozeDialog" sınıfı "DialogFragment" ı genişletiyor.

İşte hatanın tam yığın izlemesi:

java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
at android.support.v4.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1327)
at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1338)
at android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:595)
at android.support.v4.app.BackStackRecord.commit(BackStackRecord.java:574)
at android.support.v4.app.DialogFragment.show(DialogFragment.java:127)
at com.test.testing.TestDialog.f(TestDialog.java:538)
at com.test.testing.TestDialog.e(TestDialog.java:524)
at com.test.testing.TestDialog.d(TestDialog.java:519)
at com.test.testing.g.onClick(TestDialog.java:648)
at android.view.View.performClick(View.java:3620)
at android.view.View$PerformClick.run(View.java:14292)
at android.os.Handler.handleCallback(Handler.java:605)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4507)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:790)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:557)
at dalvik.system.NativeStart.main(Native Method)

Bu hatayı yeniden oluşturamıyorum ama çok sayıda hata raporu alıyorum.

Bu hatayı nasıl düzeltebileceğime kimse yardım edebilir mi?


2
Bir çözüm buldunuz mu? Seninle aynı sorunu yaşıyorum. Burada sordum: stackoverflow.com/questions/15730878/… Lütfen sorumu kontrol edin ve durumum için çalışmayan olası çözümü görün. Belki senin için işe yarar.
rootpanthera

Henüz çözüm yok :-( Ve öneriniz zaten
sınıfıma

Kabul edilen cevabı buradan kontrol edin. Bu sorunumu çözdü: stackoverflow.com/questions/14177781/…
bogdan

4
Bu diyalog tetiklendiğinde Aktiviteniz görünür mü? Bunun nedeni uygulamanızın duraklatılmış / durdurulmuş bir Aktiviteye ekli bir iletişim kutusu görüntülemeye çalışması olabilir.
Kai

Yanıtlar:


66

Bu yaygın bir sorundur . Bu sorunu show () 'u geçersiz kılarak ve DialogFragment genişletilmiş sınıfında istisnayı işleyerek çözdük

public class CustomDialogFragment extends DialogFragment {

    @Override
    public void show(FragmentManager manager, String tag) {
        try {
            FragmentTransaction ft = manager.beginTransaction();
            ft.add(this, tag);
            ft.commit();
        } catch (IllegalStateException e) {
            Log.d("ABSDIALOGFRAG", "Exception", e);
        }
    }
}

Bu yöntemi uygulamanın DialogFragment.class'ın dahili alanlarını değiştirmeyeceğini unutmayın:

boolean mDismissed;
boolean mShownByMe;

Bu, bazı durumlarda beklenmedik sonuçlara yol açabilir. Commit () yerine commitAllowingStateLoss () kullanın.


3
Peki bu sorun neden ortaya çıkıyor? Hatayı görmezden gelmek doğru mu? Yaparsan ne olur? Sonuçta, tıkladığında bu, etkinliğin canlı ve iyi olduğu anlamına gelir ... Neyse, bunu bir hata olarak gördüğüm için burada rapor ettim: code.google.com/p/android/issues/detail?id= 207269
android geliştiricisi

1
O halde buraya yıldız ekleyebilir ve / veya yorum yapabilir misiniz?
android geliştiricisi

2
try-catch cümlesi içinde super.show (yönetici, etiket) çağırmak daha iyidir. DialogFragment'a ait bayraklar bu şekilde güvende kalabilir
Shayan_Aryan

20
Bu noktada commit () yerine commitAllowingStateLoss () 'u çağırabilirsiniz. İstisna gündeme getirilmez.
ARLabs

1
@ARLabs Bu durumda bunun cevaplayıcı için de daha iyi olacağını düşünüyorum çünkü burada gösterildiği gibi istisnayı yakalarsanız, diyalog kesinlikle gösterilmeyecektir. Yapabiliyorsanız iletişim kutusunu göstermek daha iyidir ve durumun geri yüklenmesi gerekiyorsa, sadece gitmiş olabilir. Ayrıca arka planda uygulamanızın bellek kullanımını düşük tutun, böylece büyük olasılıkla yok edilmez.
androidguy

27

Seni kastediyorum O commit()( show()sonra parçanın DialogFragment durumunda) onSaveInstanceState().

Android, parça durumunuzu konumunda kaydedecek onSaveInstanceState(). Yani, eğer commit()parçalanırsanız , onSaveInstanceState()parçalanma durumu kaybolur.

Sonuç olarak, Etkinlik öldürülür ve daha sonra yeniden oluşturulursa, parça, kötü kullanıcı deneyimi olan etkinliğe eklenmeyecektir. Bu yüzden Android, ne pahasına olursa olsun durum kaybına izin vermiyor.

Kolay çözüm, durumun önceden kaydedilip kaydedilmediğini kontrol etmektir.

boolean mIsStateAlreadySaved = false;
boolean mPendingShowDialog = false;

@Override
public void onResumeFragments(){
    super.onResumeFragments();
    mIsStateAlreadySaved = false;
    if(mPendingShowDialog){
        mPendingShowDialog = false;
        showSnoozeDialog();
    }
}

@Override
public void onPause() {
    super.onPause();
    mIsStateAlreadySaved = true;
}

private void showSnoozeDialog() {
    if(mIsStateAlreadySaved){
        mPendingShowDialog = true;
    }else{
        FragmentManager fm = getSupportFragmentManager();
        SnoozeDialog snoozeDialog = new SnoozeDialog();
        snoozeDialog.show(fm, "snooze_dialog");
    }
}

Not: onResumeFragments (), parçalar devam ettirildiğinde çağıracaktır.


1
DialogFragment'ı başka bir parça içinde göstermek istersem ne olur?
android geliştiricisi

Çözümümüz, etkinlik ve parça temel sınıfı oluşturmak ve onResumeFragments'ı parçalamak için delege etmektir (parça temel sınıfında onResumeFragments oluştururuz). Güzel bir çözüm değil ama işe yarıyor. Daha iyi bir çözümünüz varsa lütfen bana bildirin :)
Pongpat

"OnStart" da iletişim kutusunu göstermenin iyi çalışacağını düşündüm, çünkü parça kesinlikle gösteriliyor, ama yine de bununla ilgili bazı çökme raporları görüyorum. Bunun yerine "onResume" ye koymam istendi. Alternatifler hakkında şunu gördüm: twigstechtips.blogspot.co.il/2014/01/… , ama bu oldukça tuhaf.
android geliştiricisi

Sanırım twigstechtips.blogspot.co.il/2014/01/… 'nin işe yaramasının nedeni, yeni bir iş parçacığı başlatması ve bu nedenle tüm yaşam döngüsü kodu, yani runOnUiThread kodundan önce çağrılan onStart, onResume vb. Bu, runOnUiThread çağrılmadan önce durumun zaten geri yüklendiği anlamına gelir.
Pongpat

2
Göndermek için tek çağrı kullanıyorum (çalıştırılabilir). GetFragmentManager ile ilgili olarak değişir. Bu iletişim kutusunu başka bir etkinlikle paylaşmak istiyorsanız, getFragmentManager'ı kullanmalısınız, ancak bu iletişim kutusu yalnızca parça ile mevcutsa getChildFragmentManager daha iyi bir seçim gibi görünür.
Pongpat

16
private void showSnoozeDialog() {
    FragmentManager fm = getSupportFragmentManager();
    SnoozeDialog snoozeDialog = new SnoozeDialog();
    // snoozeDialog.show(fm, "snooze_dialog");
    FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
    ft.add(snoozeDialog, "snooze_dialog");
    ft.commitAllowingStateLoss();
}

ref: bağlantı


11

Birkaç gün sonra ben geçersiz kılmak için yapmanız gerekir DialogFragment göstermek için, sorunu çözdüm nasıl benim çözüm paylaşmak istiyorum show()bunun yöntemini ve çağrı commitAllowingStateLoss()üzerine Transactionnesne. İşte Kotlin'de bir örnek:

override fun show(manager: FragmentManager?, tag: String?) {
        try {
            val ft = manager?.beginTransaction()
            ft?.add(this, tag)
            ft?.commitAllowingStateLoss()
        } catch (ignored: IllegalStateException) {

        }

    }

1
Yani geliştiriciler devralır gerekmez bundan DialogFragmentsen şu imzasıyla bir Kotlin uzatma fonksiyonu olacak şekilde bu değişebilir: fun DialogFragment.showAllowingStateLoss(fragmentManager: FragmentManager, tag: String). Ayrıca, commitAllowingStateLoss()yöntemi değil, commit()yöntemi çağırdığınız için try-catch gerekli değildir .
Adil Hussain

10

İletişim kutusu gerçekten önemli değilse (uygulama kapalıyken / artık görüntülenmediğinde gösterilmemekte sorun yoktur), şunu kullanın:

boolean running = false;

@Override
public void onStart() {
    running = true;
    super.onStart();
}

@Override
public void onStop() {
    running = false;
    super.onStop();
}

Ve diyaloğunuzu (parçayı) yalnızca çalışırken açın:

if (running) {
    yourDialog.show(...);
}

DÜZENLEYİN, MUHTEMELEN DAHA İYİ ÇÖZÜM:

Yaşam döngüsünde onSaveInstanceState çağrıldığında tahmin edilemez, bence daha iyi bir çözüm isSavedInstanceStateDone () 'u şu şekilde kontrol etmektir:

/**
 * True if SavedInstanceState was done, and activity was not restarted or resumed yet.
 */
private boolean savedInstanceStateDone;

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

    savedInstanceStateDone = false;
}

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

    savedInstanceStateDone = false;
}

protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    savedInstanceStateDone = true;
}


/**
 * Returns true if SavedInstanceState was done, and activity was not restarted or resumed yet.
 */
public boolean isSavedInstanceStateDone() {
    return savedInstanceStateDone;
}

"OnStart" yöntem çağrısında bu istisnayı aldığım için (DialogFragment'ı orada göstermeye çalışıyorum) bu işe yaramıyor gibi görünüyor.
android geliştiricisi

Günümü kurtardın. Teşekkür ederim Frank.
Cüneyt

7

Yıllardır bu soruna girdim.
İnternetler bu konuda çok sayıda (yüzbinlerce?) Tartışmayla doludur ve içlerindeki kafa karışıklığı ve dezenformasyon fazlasıyla görülmektedir.
Durumu daha da kötüleştirmek için ve xkcd "14 standartlar" çizgi romanının ruhuyla, cevabımı yüzüğe atıyorum.
xkcd 14 standartları

cancelPendingInputEvents(), commitAllowingStateLoss(), catch (IllegalStateException e), Benzer çözümler ve tüm iğrenç görünüyor.

Umarım aşağıdakiler sorunun nasıl yeniden üretilip düzeltileceğini kolayca gösterir:

private static final Handler sHandler = new Handler();
private boolean mIsAfterOnSaveInstanceState = true;

@Override
protected void onSaveInstanceState(Bundle outState)
{
    super.onSaveInstanceState(outState);
    mIsAfterOnSaveInstanceState = true; // <- To repro, comment out this line
}

@Override
protected void onPostResume()
{
    super.onPostResume();
    mIsAfterOnSaveInstanceState = false;
}

@Override
protected void onResume()
{
    super.onResume();
    sHandler.removeCallbacks(test);
}

@Override
protected void onPause()
{
    super.onPause();
    sHandler.postDelayed(test, 5000);
}

Runnable test = new Runnable()
{
    @Override
    public void run()
    {
        if (mIsAfterOnSaveInstanceState)
        {
            // TODO: Consider saving state so that during or after onPostResume a dialog can be shown with the latest text
            return;
        }

        FragmentManager fm = getSupportFragmentManager();
        DialogFragment dialogFragment = (DialogFragment) fm.findFragmentByTag("foo");
        if (dialogFragment != null)
        {
            dialogFragment.dismiss();
        }

        dialogFragment = GenericPromptSingleButtonDialogFragment.newInstance("title", "message", "button");
        dialogFragment.show(fm, "foo");

        sHandler.postDelayed(test, 5000);
    }
};

2
Hiçbir açıklama yapmadan olumsuz oy kullanan insanları seviyorum. Sadece olumsuz oy vermek yerine, çözümümün nasıl kusurlu olduğunu açıklasalar daha iyi olabilir mi? Olumsuz seçmen olumsuz oyu ekleyebilir miyim?
swooby

1
Evet, bu bir SO sorunu, bu sorunu her seferinde önerilerde yazıyorum, ancak çözmek istemiyorlar.
CoolMind

2
Bence olumsuz oylar gömülü XKCD'nin bir sonucu olabilir, cevaplar gerçekten sosyal yorumların yeri değil (ne kadar komik ve / veya doğru olursa olsun).
RestingRobot

6

lütfen FragmentManager yerine FragmentTransaction'ı kullanmayı deneyin. Aşağıdaki kodun sorununuzu çözeceğini düşünüyorum. Eğer değilse, lütfen beni bilgilendir.

FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
SnoozeDialog snoozeDialog = new SnoozeDialog();
snoozeDialog.show(ft, "snooze_dialog");

DÜZENLE:

Parça İşlemi

Lütfen bu bağlantıyı kontrol edin. Sorgularınızı çözeceğini düşünüyorum.


4
FragmentTransaction'ın neden sorunu çözdüğüne dair herhangi bir açıklama harika olurdu.
Hemanshu

3
Dialog # show (FragmentManager, tag) aynı şeyi yapar. Bu bir çözüm değil.
William

3
Bu cevap çözüm değil. DialogFragment # show (ft) ve show (fm) aynı şeyi yapar.
danijoo

@danijoo Her ikisinin de aynı işi yaptığı konusunda haklısın. Ancak birkaç telefonda, fragmenttransaction yerine fragmentmanager kullanıyorsanız buna benzer bazı problemler vardır. Benim durumumda bu sorunumu çözdü.
RIJO RV

6

Activity-KTX'in yeni yaşam döngüsü kapsamlarını kullanmak, aşağıdaki kod örneği kadar basittir:

lifecycleScope.launchWhenResumed {
   showErrorDialog(...)
}

Bu yöntem, onStop () 'dan sonra doğrudan çağrılabilir ve geri döndükten sonra onResume () çağrıldığında iletişim kutusunu başarılı bir şekilde gösterir.


3

Birçok görünüm, ertelenmiş çalıştırmak için olay kuyruğuna tıklama işleyicileri gibi üst düzey olayları gönderir. Dolayısıyla sorun şu ki "onSaveInstanceState" Etkinlik için zaten çağrılmış, ancak olay kuyruğu ertelenmiş "tıklama olayı" içeriyor. Dolayısıyla bu olay işleyicinize gönderildiğinde

at android.os.Handler.handleCallback(Handler.java:605)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:137)

ve kodunuz showIllegalStateException atılır.

En basit çözüm, olay kuyruğunu temizlemektir. onSaveInstanceState

protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        // ..... do some work
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            findViewById(android.R.id.content).cancelPendingInputEvents();
        }
}

Bunun sorunu çözdüğünü gerçekten doğruladınız mı?
mhsmith

Google bunu şu anda beta ( activityve fragment) olan androidx kitaplıklarının bir sonraki sürümüne ekledi .
mhsmith

1
@mhsmith Bu çözümün IllegalStateException ile kodumdaki sorunu çözdüğünü hatırlıyorum
sim

2

İletişim kutusu parça nesnenizi global yapın ve onPause () yönteminde dismissAllowingStateLoss () öğesini çağırın

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

    if (dialogFragment != null) {
        dialogFragment.dismissAllowingStateLoss();
    }
}

Parçaya değer atamayı ve buton tıklandığında veya herhangi bir yerde show () 'u çağırmayı unutmayın.


1

Resmi olarak hiçbir yerde bahsedilmemesine rağmen, bu problemle birkaç kez karşılaştım. Deneyimlerime göre, uyumluluk kitaplığında eski platformlarda bu soruna neden olan parçaları destekleyen yanlış bir şeyler var. Bunu normal parça yöneticisi API'sini kullanarak test edersiniz. Hiçbir şey işe yaramazsa, iletişim kutusu parçası yerine normal iletişim kutusunu kullanabilirsiniz.


1
  1. Bu sınıfı projenize ekleyin: ( android.support.v4.app paketinde olmalıdır )
paket android.support.v4.app;


/ **
 * Gil tarafından 8/16/2017 tarihinde oluşturulmuştur.
 * /

public class StatelessDialogFragment DialogFragment {
    / **
     * Mevcut bir işlemi kullanarak parçayı ekleyerek ve ardından
     * durum kaybına izin verirken işlem.
* * Çoğu zaman {@link #show (FragmentTransaction, String)} kullanmanızı öneririm ama * Bu, gerçekten umursamadığınız diyaloglar içindir. (Hata Ayıklama / İzleme / Reklamlar vb.) * * @param işlemi * Parçanın ekleneceği mevcut işlem. * @param etiketi * Bu parçanın etiketine göre * {@link FragmentTransaction # add (Fragment, String) FragmentTransaction.add}. * @return Göre, taahhüt edilen işlemin tanımlayıcısını döndürür. * {@link FragmentTransaction # commit () FragmentTransaction.commit ()}. * @ see StatelessDialogFragment # showAllowingStateLoss (FragmentManager, String) * / public int showAllowingStateLoss (FragmentTransaction işlemi, String etiketi) { mDismissed = yanlış; mShownByMe = true; transaction.add (bu, etiket); mViewDestroyed = yanlış; mBackStackId = transaction.commitAllowingStateLoss (); mBackStackId döndür; } / ** * Parçayı verilen FragmentManager'a ekleyerek iletişim kutusunu görüntüleyin. Bu bir rahatlık * açıkça bir işlem oluşturmak, parçayı verilen etiketle buna eklemek ve * devleti önemsemeden işlemek. Bu mu değil için işlem eklemek * arka yığın. Parça reddedildiğinde, onu kaldırmak için yeni bir işlem yürütülecektir. * faaliyetten.
* * Çoğu zaman {@link #show (FragmentManager, String)} kullanmanızı öneririm ama bu * gerçekten umursamadığınız diyaloglar için. (Hata Ayıklama / İzleme / Reklamlar vb.) * * * @param yöneticisi * FragmentManager, bu fragmana eklenecek. * @param etiketi * Bu parçanın etiketine göre * {@link FragmentTransaction # add (Fragment, String) FragmentTransaction.add}. * @ see StatelessDialogFragment # showAllowingStateLoss (FragmentTransaction, String) * / public void showAllowingStateLoss (FragmentManager yöneticisi, String etiketi) { mDismissed = yanlış; mShownByMe = true; FragmentTransaction ft = manager.beginTransaction (); ft.add (bu, etiket); ft.commitAllowingStateLoss (); } }
  1. DialogFragment yerine StatelessDialogFragment'ı genişlet
  2. Show yerine showAllowingStateLoss yöntemini kullanın

  3. Zevk almak ;)


Tüm bu boole alanları ne için? Neden sınıf üyesi olarak ilan edilmiyorlar?
tanımlanmamış

1
Boole alanları, DialogFragment'ın korumalı üyeleridir, isimleri açıkça ne işe yaradıklarını gösterir ve DialogFragment mantığına müdahale etmemek için onları güncellememiz gerekir. Orijinal DialogFragment sınıfında bu işlevlerin var olduğunu, ancak genel erişim olmadığını unutmayın
Gil SH

Bu üyeler korumalı değiller ama dahili StatelessDialogFragment. Paketlerimden birine koyduğumda derleme hataları alıyordum teşekkürler dostum yakında üretimde test edeceğim.
tanımsız

1

bu kodu kullan

FragmentTransaction ft = fm.beginTransaction();
        ft.add(yourFragment, "fragment_tag");
        ft.commitAllowingStateLoss();

onun yerine

yourFragment.show(fm, "fragment_tag");

1

Yansıma kullanarak bu soruna zarif bir çözüm buldum. Yukarıdaki tüm çözümlerin sorunu, mDismissed ve mShownByMe alanlarının durumlarını değiştirmemesidir.

Aşağıdaki örnek gibi kendi özel alt sayfa diyalog parçanızdaki "göster" yöntemini geçersiz kılmanız yeterlidir (Kotlin)

override fun show(manager: FragmentManager, tag: String?) {
        val mDismissedField = DialogFragment::class.java.getDeclaredField("mDismissed")
        mDismissedField.isAccessible = true
        mDismissedField.setBoolean(this, false)

        val mShownByMeField = DialogFragment::class.java.getDeclaredField("mShownByMe")
        mShownByMeField.isAccessible = true
        mShownByMeField.setBoolean(this, true)

        manager.beginTransaction()
                .add(this, tag)
                .commitAllowingStateLoss()
    }

4
"Bu soruna yansımayı kullanarak zarif bir çözüm buldum." nasıl zarif
Mark Buikema

zarif, şık, şık, akıllı, hoş, zarif
Рома Богдан

1
benim için işe yarayan tek çözüm bu. Zarif olduğunu düşünüyorum
MBH

0

Aşağıdaki uygulama, Activityyaşam döngüsü sırasında, özellikle diyalogların gösterilmesi için güvenli durum değişikliklerinin gerçekleştirilmesi sorununu çözmek için kullanılabilir : eğer örnek durumu zaten kaydedilmişse (örneğin bir konfigürasyon değişikliği nedeniyle), devam ettirilen duruma kadar bunları erteler. gerçekleştirildi.

public abstract class XAppCompatActivity extends AppCompatActivity {

    private String TAG = this.getClass().getSimpleName();

    /** The retained fragment for this activity */
    private ActivityRetainFragment retainFragment;

    /** If true the instance state has been saved and we are going to die... */
    private boolean instanceStateSaved;

    @Override
    protected void onPostCreate(Bundle savedInstanceState) {
        super.onPostCreate(savedInstanceState);

        // get hold of retain Fragment we'll be using
        retainFragment = ActivityRetainFragment.get(this, "Fragment-" + this.getClass().getName());
    }

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

        // reset instance saved state
        instanceStateSaved = false;

        // execute all the posted tasks
        for (ActivityTask task : retainFragment.tasks) task.exec(this);
        retainFragment.tasks.clear();
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        instanceStateSaved = true;
    }

    /**
     * Checks if the activity state has been already saved.
     * After that event we are no longer allowed to commit fragment transactions.
     * @return true if the instance state has been saved
     */
    public boolean isInstanceStateSaved() {
        return instanceStateSaved;
    }

    /**
     * Posts a task to be executed when the activity state has not yet been saved
     * @param task The task to be executed
     * @return true if the task executed immediately, false if it has been queued
     */
    public final boolean post(ActivityTask task)
    {
        // execute it immediately if we have not been saved
        if (!isInstanceStateSaved()) {
            task.exec(this);
            return true;
        }

        // save it for better times
        retainFragment.tasks.add(task);
        return false;
    }

    /** Fragment used to retain activity data among re-instantiations */
    public static class ActivityRetainFragment extends Fragment {

        /**
         * Returns the single instance of this fragment, creating it if necessary
         * @param activity The Activity performing the request
         * @param name The name to be given to the Fragment
         * @return The Fragment
         */
        public static ActivityRetainFragment get(XAppCompatActivity activity, String name) {

            // find the retained fragment on activity restarts
            FragmentManager fm = activity.getSupportFragmentManager();
            ActivityRetainFragment fragment = (ActivityRetainFragment) fm.findFragmentByTag(name);

            // create the fragment and data the first time
            if (fragment == null) {
                // add the fragment
                fragment = new ActivityRetainFragment();
                fm.beginTransaction().add(fragment, name).commit();
            }

            return fragment;
        }

        /** The queued tasks */
        private LinkedList<ActivityTask> tasks = new LinkedList<>();

        @Override
        public void onCreate(Bundle savedInstanceState)
        {
            super.onCreate(savedInstanceState);

            // retain this fragment
            setRetainInstance(true);
        }

    }

    /** A task which needs to be performed by the activity when it is "fully operational" */
    public interface ActivityTask {

        /**
         * Executed this task on the specified activity
         * @param activity The activity
         */
        void exec(XAppCompatActivity activity);
    }
}

Sonra böyle bir sınıf kullanarak:

/** AppCompatDialogFragment implementing additional compatibility checks */
public abstract class XAppCompatDialogFragment extends AppCompatDialogFragment {

    /**
     * Shows this dialog as soon as possible
     * @param activity The activity to which this dialog belongs to
     * @param tag The dialog fragment tag
     * @return true if the dialog has been shown immediately, false if the activity state has been saved
     *         and it is not possible to show it immediately
     */
    public boolean showRequest(XAppCompatActivity activity, final String tag) {
        return showRequest(activity, tag, null);
    }

    /**
     * Shows this dialog as soon as possible
     * @param activity The activity to which this dialog belongs to
     * @param tag The dialog fragment tag
     * @param args The dialog arguments
     * @return true if the dialog has been shown immediately, false if the activity state has been saved
     *         and it is not possible to show it immediately
     */
    public boolean showRequest(XAppCompatActivity activity, final String tag, final Bundle args)
    {
        return activity.post(new XAppCompatActivity.ActivityTask() {
            @Override
            public void exec(XAppCompatActivity activity) {
                if (args!= null) setArguments(args);
                show(activity.getSupportFragmentManager(), tag);
            }
        });
    }

    /**
     * Dismiss this dialog as soon as possible
     * @return true if the dialog has been dismissed immediately, false if the activity state has been saved
     *         and it is not possible to dismissed it immediately
     */
    public boolean dismissRequest()
    {
        return dismissRequest(null);
    }

    /**
     * Dismiss this dialog as soon as possible
     * @param runnable Actions to be performed before dialog dismissal
     * @return true if the dialog has been dismissed immediately, false if the activity state has been saved
     *         and it is not possible to dismissed it immediately
     */
    public boolean dismissRequest(final Runnable runnable)
    {
        // workaround as in rare cases the activity could be null
        XAppCompatActivity activity = (XAppCompatActivity)getActivity();
        if (activity == null) return false;

        // post the dialog dismissal
        return activity.post(new XAppCompatActivity.ActivityTask() {
            @Override
            public void exec(XAppCompatActivity activity) {
                if (runnable != null) runnable.run();
                dismiss();
            }
        });
    }
}

Uygulama durumu hakkında endişelenmeden diyalogları güvenle gösterebilirsiniz:

public class TestDialog extends XAppCompatDialogFragment {

    private final static String TEST_DIALOG = "TEST_DIALOG";

    public static void show(XAppCompatActivity activity) {
        new TestDialog().showRequest(activity, TEST_DIALOG);
    }

    public TestDialog() {}

    @NonNull
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState)
    {
        return new AlertDialog.Builder(getActivity(), R.style.DialogFragmentTheme /* or null as you prefer */)
                .setTitle(R.string.title)
                // set all the other parameters you need, e.g. Message, Icon, etc.
                ).create();
    }
}

ve sonra TestDialog.show(this)kendi içinden arayın XAppCompatActivity.

Eğer parametrelerle daha genel iletişim sınıf oluşturmak istiyorsanız, bir de bunları kaydedebilirsiniz Bundleargümanlar ile show()yöntem ve onları almak getArguments()içinde onCreateDialog().

Tüm yaklaşım biraz karmaşık görünebilir, ancak etkinlikler ve diyaloglar için iki temel sınıf oluşturduğunuzda, kullanımı oldukça kolaydır ve mükemmel şekilde çalışır. Aynı Fragmentsorundan etkilenebilecek diğer tabanlı işlemler için kullanılabilir .


0

Bu hata, giriş olaylarının (tuş basma veya onclick olayları gibi) onSaveInstanceStateçağrıldıktan sonra teslim edilmesinden kaynaklanıyor gibi görünüyor.

Çözüm, onSaveInstanceStateAktivitenizi geçersiz kılmak ve bekleyen olayları iptal etmektir.

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