Aktivite yok edildiğinde aktivitenin Backstack'ın hatalı davranışı

İki faaliyetim var; A ve B diyelim. A etkinliği 'de, A aktivitesini bitirecek belirli bir olayı dinleyen bir yayın alıcısı var. > ve etkinliği A öğesinin onDestroy() öğesinde imha edilmesi.

Basit olması için, "Etkinlik A'yı Yok Et" adlı etkinlik B 'de bir düğmesi vardır. Bir kullanıcı düğmesini tıkladığında, etkinliği A yok edilmelidir.

Normalde bunların hepsi sorunsuz bir şekilde çalışıyor, ancak sorun aşağıdaki senaryolarda ortaya çıkıyor:

1) Diyelim ki B aktivitesi 'de olduğumu ve uygulamayı arka plana taşımak için Giriş tuşuna bastığımı ve sonra diğer kaynak ağırlıklı uygulamaları kullanırsam Android sistemimin hafızayı boşaltmak için uygulamamı öldüreceğini varsayalım. Daha sonra uygulamamı son görevlerden açarsam, B aktivitesi devam eder ve onCreate() , onResume() etc yöntemi olur. aradı. Şimdi etkinliği A yok etmek için tuşuna basın, ancak A etkinliği zaten yok edildi, bu nedenle etkinliği A 'nın onCreate() , onResume() etc yöntemleri, geri düğmesine basılarak etkinliği A 'a gitmediğim sürece çağrılmaz. Bu nedenle, yayın alıcısı etkinliği dinlemek için kayıtlı değil.

2) Aynı sorun, kullanıcı cihaz ayarlarındaki Geliştirici seçeneklerinden "Etkinlikleri tutma" yı seçtiğinde ortaya çıkacaktır.

Bu sorunu uzun zamandır çözmek istiyordum, ancak uygun bir cevap bulamıyorum. Bu senaryoyu ele almanın en iyi yolu nedir? Bu bir Android hatası mı? Bu konuda bir çözüm olmalı.

Lütfen bana yardım et.

25
Neden A’nın B’deki bir düğmeyle kaldırılmasını istiyorsunuz? Bir BroadcastReceiver sınıfına, her zaman ne zaman gerektiğinde Aktivite A'nın eylemini dinleyip çağırabilirsin.
katma yazar Ichthyocentaurs, kaynak
Burada asıl sorun ne olursa olsun, arka yığını kontrol etmeye yardımcı olmak için Amaç bayrakları veya öznitelikleri gibi diğer teknikler kullanılarak ele alınmalıdır. Başka bir etkinliği yok etmeye çalışan bir etkinliğiniz varsa, yanlış yapıyorsunuz demektir.
katma yazar CommonsWare, kaynak
... A aktivitesini bitirecek belirli bir olayı dinleyen kayıtlı bir yayın alıcısı var. A ... - Eğer etkinlik zaten yok edilmişse (bahsettiğin senaryolarda) bu alıcının kaydedilmesinin amacı etkinliği bitirecek bir etkinlik almak için? Aktiviteyi öldürmekten başka ekstra bir şey mi yapıyorsun?
katma yazar Luksprog, kaynak
Aktivite B'nizin Aktivite A'yı nasıl "yok ettiğini" merak ediyorum. Yapmamanız gereken bir şey yaptığınız gibi çok şüpheli geliyor.
katma yazar Kai, kaynak
ilk sorununuzu anlamak için bunu tamamen daha iyi okuyun; İkinci sayı kontrol ve kullanıcıdan "Aktiviteleri saklamama" seçeneğini devre dışı bırakmasını isteyin. etkin-in-ics "> bu .
katma yazar PAC, kaynak
Merhaba CommonsWare, hoş geldiniz. Evet bazı durumlar var, faaliyetin bayrağını açık üst gibi kullanabildiğim için ... Bazı rastgele aktiviteleri yok etmek istiyorum ... Başvurumda da aynı durum var. Bu yüzden faaliyetleri yok etmek için BroadcastReceiver kullanıyorum.
katma yazar Smeet, kaynak
Geri düğmesine bastığımda, "Etkinlik B" den "Etkinlik A" yı basarsam Etkinlik A'yı görmek istemiyorum.
katma yazar Smeet, kaynak
@CommonsWare, niyet bayraklarına dayanarak bir cevap gönderdim. Bunun hakkında yorum yapmak ister misiniz? Neden bir aşağı oy aldığından emin değilim.
katma yazar A.J., kaynak
İşletim sistemi ile A tahrip olsaydı ne olurdu?
katma yazar user1922137, kaynak

7 cevap

Etkinlik A 'nuz Android işletim sistemi tarafından yok edildiyse, o zaman   izlemenin yolu yok.

Bazı kişiler, sistem işletim sistemi tarafından Etkinlik 'iniz tarafından öldürüldüğü takdirde, onDestroy yönteminde olayı listeleyerek bu Etkinlik A ' yı takip etmeyi önerdi. bu yöntem.

6
katma
Eğer düşük bellek üzerinde işletim sistemi varsa, bu yöntemleri çağırmaz.
katma yazar dhams, kaynak
Aranabilir veya aranmayabilir. Genellikle buna denir. Ancak, kişi denemeyeceği gibi buna güvenemez.
katma yazar A.J., kaynak
Evet, aranamayabilir. Aranmayacağından emin olamayız.
katma yazar A.J., kaynak

Bu, mevcut yayın mantığınızı korurken düzeltilemez.

Olayları backstack'tan öldürmek, imo, doğru bir yaklaşım değil. Navigasyon mantığınızı değiştirmeyi şiddetle düşünmelisiniz.

Ancak, projeniz büyük ve zaman bir sorunsa ve yeniden düzenleme söz konusu değilse, A.J. yaklaşımı işe yarıyor, ama öldürülmesi gereken birçok faaliyetin olduğunu söyledin, çözümü çok zorlaştı.

Önerdiğim şey şu. Bu en iyi fikir olmayabilir, ama başka bir şey düşünemiyorum. Yani belki bu yardımcı olabilir.

Aşağıdakilere sahip olmalısınız:

  • A Base Activity for all your activities.
  • A ArrayList activitiesToKill object at the application level. (If you did not extend Application you can have it as static variable

First we have to make sure that the activitiesToKill is not lost when the OS kills the app in low memory. In the BaseActivity we save the list during onSaveInstanceState and restore it in the onRestoreInstanceState

@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putSerializable("activitiesToKill", activitiesToKill);
}

private void onRestoreInstanceState(Bundle state) {
    if (state != null) {
        activitiesToKill = (ArrayList) state.getSerializable("activitiesToKill");
    super.onRestoreInstanceState(state); 
}

}

Buradaki fikir, isimlerini kullanarak listedeki hangi faaliyetlerin öldürülmesi gerektiğini kaydetmektir.

Mantık aşağıdaki gibidir:

Diyelim ki A, B, C, D ve E Aktiviteleri var.

E aktivitesinden, düğmesine basın ve B ve D'yi öldürmek istiyorsunuz.

E'deki Düğmeye bastığınızda, B ve D adlarını activitiesToKill nesnesine eklersiniz.

activitiesToKill.add(B.class.getSimpleName()
activitiesToKill.add(D.class.getSimpleName()

BaseActivity'nin onCreate yönteminde, şunları kontrol etmemiz gerekir

if(savedInstanceState != null)
{
    //The activity is being restored. We check if the it is in the lest to Kill and we finish it                
    if(activitiesToKill.contains(this.getClass().getSimpleName()))
    {
        activitiesToKill.remove(this.getClass().getSimpleName())
        finish();
    }
}

Yayın boyunca öldürülürse etkinliğin adını kaldırdığınızdan emin olun.

Yani temelde, her senaryoda olan budur.

If the app is running normally, and you click the button, the broadcast gets sent and B and D will get killed. Make sure to remove B and D from the activitiesToKill

Uygulama öldürülürse ve geri yüklenirse, düğmeye basarsanız, yayın etkisiz olur, ancak activitiesToKill nesnesine B ve D eklediniz. Böylece, geri tıklattığınızda etkinlik oluşturulur ve savedInstanceState değeri boş olmaz, etkinlik tamamlanır.

Bu yaklaşım, E aktivitesinin hangi aktiviteleri öldürmesi gerektiğini bildiğini düşünmektedir.

E'den hangi aktiviteleri öldüreceğinizi bilemezseniz, bu mantığı biraz değiştirmelisiniz:

Instead of using an ArrayList use a HashMap

Aktivite B oluşturulduğunda, hasheme kendisini kaydeder:

activitiesToKill.put(this.class.getSimpleName(), false)

Then from Activity E, all you have to do is set all the entries to true

Daha sonra, temel aktivitenin yaratılmasında, bu aktivitenin activitiesToKill'e kayıtlı olup olmadığını kontrol etmeniz gerekir (hashmap anahtarı içerir) VE boolean true olur (öldürmeyi unutmayın yanlış yapmak veya anahtarı kaldırmak)

Bu, her bir aktivitenin kendisini HashMap'e kaydetmesini ve Aktivite E'nin öldürecek tüm aktiviteleri bilmemesini sağlar. Ve yayın onları öldürürse diye çıkarmayı unutma.

Bu yaklaşım aynı zamanda aktivitenin normal olarak bir niyetten açıldığında öldürülmemesini de sağlar, çünkü bu durumda onSaveInstanceState onCreate'da boş kalır, bu yüzden hiçbir şey olmaz.

Farklı koşullar altında sonlandırılması gereken faaliyet gruplarına sahip olmanız durumunda (sadece bir tuşla değil) daha gelişmiş kontroller yapılabilir, böylece onları kategorilere ayırmak için bir HashMap HashMapına sahip olabilirsiniz.

Ayrıca, aynı adda ancak farklı paketlerde birden fazla etkinliğiniz varsa getSimpleName yerine getName kullanabileceğinizi unutmayın.

Umarım açıklamam kafamdan yazdığım kadar açıktır, herhangi bir alanın net olup olmadığını bana bildirin.

İyi şanslar

3
katma
@Setet Harika, Yardım edebildiğime sevindim. Bununla birlikte, navigasyonun tamamını yeniden düzenlemeyi şiddetle öneririm ve backstack'ten etkinlikleri kapatmamak için bir mantık bulmaya çalışıyorum. Ama bunun bir yıllık bir proje olabileceğini biliyorum :)
katma yazar Youssef, kaynak
@ Garip Garip, çünkü sadece denedim ve çocuğa basit bir isim aldım. bunun yerine getName'i deneyebilir misiniz?
katma yazar Youssef, kaynak
Ya Android'de geri yığını yönetme çok zordur. Kullanıcı ana tuşuna bastığında ve uygulamam arka plana gittiğinde bu sorunla karşılaşıyorum. Ve uzun zamandır kullanıcı uygulamama geri dönüyor ve aynı zamanda oturumun süresi doluysa, o zaman istiflenmiş etkinliklerin bir kısmını öldürmek istiyorum. Ancak en iyi çözümü bulmaya çalışıyorum ve eğer varsa buraya gönderiyorum.
katma yazar Smeet, kaynak
Mükemmel cevap. İyi çalışıyor. Çaba ve zaman ayırdığınız için teşekkürler Youssef.
katma yazar Smeet, kaynak
Şunu gibi söz ettiniz: if (savedInstanceState! = Null) {// Etkinlik geri yükleniyor. Öldürmek için test edilip edilmediğini kontrol ederiz ve eğer bitirirsek şunu yaparız (activitiesToKill.contains (this.getClass (). bitiş(); }} this.getClass (). getSimpleName() bu, bana BaseActivity adını verir ve A, B, C vb. gibi öldürülecek olan etkinlikleri değil. Bunu kontrol ettin mi?
katma yazar Smeet, kaynak

Aklıma birçok çözüm geldi, ancak uygulamanız hakkında fazla bilgi vermediğiniz için, bunun genel olarak çalışması gerektiğini düşünüyorum.

Etkinlik A'yı öldürmek için bir yayın başlatmak yerine, Etkinlik B'de "Etkinlik A'yı Öldür" düğmesine basıldığında aşağıdaki kodu uygulamanız yeterlidir.

        Intent intent = new Intent(getApplicationContext(),
                ActivityA.class);
        intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
        intent.putExtra("EXIT", true);
        startActivity(intent);

A aktivitesine aşağıdaki kodu ekleyin

@Override
protected void onNewIntent(Intent intent) {
    super.onNewIntent(intent);
    if (intent.getBooleanExtra("EXIT", false)) {
        finish();
    }
}

protected void onCreate(Bundle savedInstanceState) {
    //Ideally, there should not be anything before this
    super.onCreate(savedInstanceState);
    if(getIntent().getBooleanExtra("EXIT", false)){
        finish();
        return;
    }

Açıkça ayarlanan A aktivitesi için "singleTop" başlatma modunu ayarlayın.


Bunun aşağıdaki sonuçları olacaktır:

  • Etkinlik A zaten çalışıyorsa, etkinlik yığınının önüne getirilir ve bitirilir, böylece yığıntan çıkarılır.
  • Eğer Faaliyet A tahrip edilmiş ancak aktivite yığında hala mevcutsa (geri düğmesine basıldığında başlatılacaksa), başlatılacak, öne alınacak ve bitecek ve böylece aktivite yığından çıkarılacaktır.
  • A Etkinliği zaten yok edilmişse ve etkinlik yığında mevcut değilse ve hala "Etkinlik A'yı Kaldır" düğmesine basmaya devam ederseniz, başlatılacak, öne getirilip bitirilecektir.

Genelde titremeyi görmemelisiniz.

Bu fikre dayanarak, uygulamanız için daha iyi performans gösteren bir çözüm oluşturabilirsiniz. Örneğin, FLAG_ACTIVITY_CLEAR_TOP özelliğini kullanabilir ve Etkinlik A'yı Etkinlik B'nin onBackPressed() bölümünde bitirin.

1
katma
Haklısın. Ama basit bir senaryo verdim. Broadcast Receiver'ı kullanmam için birden fazla etkinliği imha etmek istiyorum. Örneğin, backstack'ta 10 etkinlik var (Bu, kullanıcı navigasyon senaryosuna dayanarak bilmediğim herhangi bir sayıda etkinlik olabilir). 2., 4. ve 5. aktivitelere son vermek istiyorum. Nasıl tahrip edebilirim? Cepheyi yeniden sıralayamıyorum çünkü arkaya yığılmış etkinlikleri bilmiyorum.
katma yazar Smeet, kaynak
Hangi etkinlikleri yok edeceğini biliyorsanız, bu yöntemle hepsini yok edebilirsiniz. Sadece tarif edilen amacı belirle ve hepsine yolla.
katma yazar A.J., kaynak
Aşağı oy kullanan kim, nedenini açıklamak ister?
katma yazar A.J., kaynak

Etkinlikler ile ana kurallardan biri, ön plan etkinliği dışında canlı olan hiçbir etkinliğe güvenemeyeceğiniz 'dir. Yayınlarla yapmaya çalıştığınız şeyin arka yığınla hiçbir ilgisi yok - arka yığın, tüm etkinliklerin her zaman canlı olmasını garanti etmiyor, ancak ön plana çıkma zamanı geldiğinde yeniden yaratılmalarını sağlayacak.

Örneğinizde (ne yapmak istediğinizi anlıyorsam), A altındaki bir şeye gitmeniz gerekir, diyorsunuz, Etkinlik Z . bu: ZA- [B] . geri tuşuna bastığınız normal olayların seyri vardır ve sizi A 'a, ardından başka bir hitden sonra - Z ' a götürür, ancak belirli bir durumda (bir düğmeye basarak) A'yı atlayarak Z konumuna geri dönmek istiyorsunuz - bu FLAG_ACTIVITY_CLEAR_TOP ve açıkça Z başlatın:

Intent intent = new Intent(this, ActivityZ.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);

Bu, hem B hem de A işlemlerini tamamlar ve amacı Z 'ye teslim eder. Muhtemelen FLAG_ACTIVITY_SINGLE_TOP bayrağına da ihtiyacınız olacak, FLAG_ACTIVITY_CLEAR_TOP , göz önünde bulundurmanız gereken bazı hileler var.

0
katma

Verdiğiniz bilgilerle, halihazırda kayıtlı olup olmadığını kontrol ettikten sonra yayını, Aktivite B'nin onCreate bölümüne kaydetmeye ne dersiniz? Bahsedilen senaryolardan herhangi birinde Faaliyet A'nın Zarar Görevi çağrılırsa, Yayının kayıt görevlisi aranacaktır. Böylece, bu durumda, Yayınınızı onCreate B Aktivitesine kaydedebilir, böylece sırt çantanızda yalnızca Aktivite B olsa bile dinleyebilirsiniz.

0
katma

Yapışkan Yayını kullanmayı düşündünüz mü? Ayrıca, alıcınızı uygulama düzeyinde (açık olarak) kaydedebilir ve Etkinlik A durumundan bağımsız olarak bu olayı dinleyebilirsiniz.

Ancak, zaten Youssef dediği gibi, backstack'tan etkinlik öldürmek doğru bir yaklaşım değildir. Navigasyon mantığınızı değiştirmeyi şiddetle düşünmelisiniz.

0
katma

Bunu "uygun" bir şekilde halletmenin mümkün olup olmadığını bilmiyorum.

Aklıma gelen, A faaliyetini bir şekilde işaretlemektir. startActivityForResult() özelliğini kullanamazsınız çünkü sonucu onResume() çağrılmadan önce alırsınız, yani kullanıcı arayüzü zaten şişirilir.

Bir Otto kullanıyorsanız, yapışkan bir olayla deneyebilirsiniz. Aksi halde, bayrağı ele almak veya paylaşılan tercihlere kaydetmek için bir singleton'a ihtiyacınız olacaktır.

Bayrak doğruysa etkinliği tamamladıysanız, setContentView() öğesini çağırmadan önce onCreate() yönteminizde bu bayrağını kontrol etmeniz gerekir.

0
katma
Bir etkinlik oluşturulurken setContentView öğesini çağırmazsanız, ekranda hiçbir şey gösterilmez. SetContentView'ü ararsanız ve ardından etkinliği öldürürseniz bir titreme görünecektir.
katma yazar Axxiss, kaynak
Evet beyaz veya siyah arka plan ile gösterilecektir. Ancak asıl sorun, bunu ne zaman yapabilirim? Belirli bir olayı almalıyım ki onu yok edebileyim.
katma yazar Smeet, kaynak
Bu, soruyu daha net ve kolay hale getirmek için verilen basit senaryodur, ancak gerçek karmaşık senaryoda çok sayıda bayrağı yönetmem ve bunu yapmak için kullanıcıya titrek efektler gibi görünmem gerekir. Çünkü birkaç dakika boyunca kullanıcı tarafından görülebilir ve sonra yok oluruz.
katma yazar Smeet, kaynak