Bir Android uygulaması arka plana gittiğinde ve ön plana geri döndüğünde nasıl tespit edilir


382

Bir süre sonra ön plana getirildiğinde belirli bir şey yapan bir uygulama yazmaya çalışıyorum. Bir uygulamanın arka plana gönderildiğini veya ön plana getirildiğini tespit etmenin bir yolu var mı?


2
Soruna bir kullanım durumu eklemek olabilir, çünkü açık gibi görünmüyor, bu nedenle verilen cevaplarda ele alınmıyor. Uygulama, aynı yığın içinde bulunup uygulamanın ekranlarından biri olarak görünecek başka bir uygulamayı (örneğin Galeri) başlatabilir ve ardından Ana Ekran düğmesine basabilir. Uygulama yaşam döngüsüne (hatta bellek yönetimine) dayanan yöntemlerin hiçbiri bunu algılayamaz. Ana Etkinlik düğmesine bastığınızda Ana Etkinlik düğmesine bastığınızda değil, arka plan durumunu hemen tetiklerler.
Dennis K


1
Google Çözümü'ne bakın: stackoverflow.com/questions/3667022/…
user1269737

Yanıtlar:


98

onPause()Ve onResume()uygulama tekrar arka plana ve ön plana getirildiğinde yöntemleri denir. Ancak, başvuru ilk kez başlatıldığında ve öldürülmeden önce de çağrılır. Etkinlik'te daha fazla bilgi bulabilirsiniz .

Orada arka plan veya ön planda iken uygulama durumunu almak için herhangi bir doğrudan bir yaklaşım değildir, ama gene de bu sorunla karşı karşıya ve birlikte çözüm bulduk onWindowFocusChangedve onStop.

Daha fazla ayrıntı için Android'i kontrol edin : Bir Android uygulamasının arka plana gittiğini ve getRunningTasks veya getRunningAppProcesses olmadan ön plana döndüğünü tespit etme çözümü .


173
Bununla birlikte, bu yaklaşım, diğerlerinin de işaret ettiği gibi yanlış pozitiflere neden olur, çünkü aynı yöntemdeki etkinlikler arasında geçiş yaparken bu yöntemler de çağrılır.
John Lehmann

9
Bundan daha kötü. Denedim ve bazen telefon kilitliyken onResume çağrılır. Belgelerde onResume'un tanımını görürseniz şunları bulacaksınız: onResume'un, etkinliğinizin kullanıcı tarafından görülebildiğinin en iyi göstergesi olmadığını unutmayın; tuş kilidi gibi bir sistem penceresi ön tarafta olabilir. Etkinliğinizin kullanıcı tarafından görülebildiğinden emin olmak için (örneğin, bir oyuna devam etmek için) onWindowFocusChanged (boolean) kullanın. developer.android.com/reference/android/app/…
J-Rou

2
Bağlantıda yayınlanan çözüm onResume / onPause kullanmaz, bunun yerine onBackPressed, onStop, onStart ve onWindowsFocusChanged kombinasyonunu kullanır. Benim için işe yaradı ve oldukça karmaşık bir kullanıcı arayüzü hiyerarşim var (çekmeceler, dinamik görüntüleyiciler vb. İle)
Martin Marconcini

18
OnPause ve onResume, Etkinliğe özgüdür. Uygulama değil. Bir Uygulama arka plana eklendiğinde ve devam ettirildiğinde, arka plana gitmeden önce bulunduğu belirli Etkinliği sürdürür. Bu, Uygulamanızın tüm Etkinliğinde arka plandan devam ettirmek istediğiniz her şeyi uygulamanız gerektiği anlamına gelir. Asıl sorunun Aktivite için değil Uygulama için "onResume" gibi bir şey aradığına inanıyorum.
SysHex

4
Böyle ortak bir ihtiyaç için uygun bir API sunulmadığına inanamıyorum. Başlangıçta onUserLeaveHint () keseceğini düşündüm, ancak kullanıcının uygulamadan ayrılıp ayrılmadığını söyleyemezsiniz
atsakiridis 27:15

197

2018: Android bunu yaşam döngüsü bileşenleri aracılığıyla yerel olarak destekliyor.

Mart 2018 GÜNCELLEME : Artık daha iyi bir çözüm var. Bkz. ProcessLifecycleOwner . Yeni mimari bileşenleri 1.1.0'ı kullanmanız gerekir (en son şu anda), ancak bunu yapmak için özel olarak tasarlanmıştır.

Bu cevapta verilen basit bir örnek var ama bunun için bir örnek uygulama ve bir blog yazısı yazdım .

Bunu 2014 yılında yazdığımdan beri farklı çözümler ortaya çıktı. Bazıları çalıştı, bazılarının çalıştığı düşünülüyordu , ancak kusurları vardı (benim dahil!) Ve biz, bir topluluk olarak (Android) sonuçlarla yaşamayı öğrendik ve özel durumlar için çözümler yazdık.

Asla tek bir kod snippet'inin aradığınız çözüm olduğunu varsaymayın, durum böyle değildir; daha da iyisi, ne yaptığını ve neden yaptığını anlamaya çalışın.

MemoryBossSınıf aslında burada yazıldığı gibi benim tarafımdan hiç kullanılmamıştır, bu işin başına sözde kod sadece bir parça oldu.

Yeni mimari bileşenlerini kullanmamanız için geçerli bir neden olmadığı sürece (ve özellikle de süper eski API'leri hedefliyorsanız) bazıları varsa, devam edin ve bunları kullanın. Mükemmel olmaktan uzaklar, ama ikisi de değildi ComponentCallbacks2.

GÜNCELLEME / NOTLAR (Kasım 2015) : İnsanlar iki yorum yapıyor, ilk önce >=bunun yerine kullanılması gerekiyor, ==çünkü belgeler kesin değerleri kontrol etmemeniz gerektiğini belirtiyor . Bu, çoğu durumda iyidir, ancak yalnızca uygulama arka plana gittiğinde bir şey yapmayı önemsiyorsanız , == kullanmanız ve ayrıca başka bir çözümle (Etkinlik Yaşam Döngüsü geri aramaları gibi) birleştirmeniz gerektiğini veya istediğiniz etkiyi elde edemeyebilirsiniz . Örnek (ve bu başıma) isterseniz olmasıdır kilidiarka plana gittiğinde uygulamanızı bir şifre ekranı ile (eğer aşina iseniz 1Password gibi), hafızanız azalırsa ve aniden test ediyorsanız uygulamanızı yanlışlıkla kilitleyebilirsiniz >= TRIM_MEMORY, çünkü Android bir LOW MEMORYaramayı tetikler ve sizinkinden daha yüksek. Bu yüzden nasıl / ne test ettiğine dikkat et.

Ayrıca, bazı insanlar geri döndüğünüzde nasıl tespit edeceğinizi sordular.

Aklıma gelen en basit yol aşağıda açıklanmıştır, ancak bazı insanlar buna aşina olmadığından, buraya bazı sahte kod ekliyorum. Var olduğunuzu YourApplicationve MemoryBosssınıflarınız olduğunu varsayarsak , class BaseActivity extends Activity(eğer yoksa bir tane oluşturmanız gerekir).

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

    if (mApplication.wasInBackground()) {
        // HERE YOU CALL THE CODE YOU WANT TO HAPPEN ONLY ONCE WHEN YOUR APP WAS RESUMED FROM BACKGROUND
        mApplication.setWasInBackground(false);
    }
}

Dialoglar bir etkinliği duraklatabileceği için onStart'ı öneriyorum, çünkü yaptığınız tek şey bir tam ekran iletişim kutusu görüntülüyse uygulamanızın "arka plana gittiğini" düşünmesini istemiyorsanız, ancak kilometreniz değişebilir.

Ve hepsi bu. İf bloğundaki kod yalnızca bir kez yürütülür , başka bir etkinliğe gitseniz bile, yeni olan (aynı zamanda extends BaseActivity) raporlanır wasInBackground, falseböylece çağrılana ve bayrak tekrar true değerine ayarlanana kadaronMemoryTrimmed kodu yürütmez. .

Umarım yardımcı olur.

GÜNCELLEME / NOTLAR (Nisan 2015) : Bu koddaki tüm Kopyala ve Yapıştır'a gitmeden önce,% 100 güvenilir olmayabileceği ve en iyi sonuçları elde etmek için diğer yöntemlerle birleştirilmesi gereken birkaç örnek bulduğumu unutmayın . Özellikle, orada bilinen iki örneğionTrimMemory çağrı geri yürütülecek garanti edilmemektedir:

  1. Uygulamanız görünür durumdayken telefonunuz ekranı kilitlerse (cihazınızın nn dakika sonra kilitlendiğini söyleyin), kilit ekranı sadece üstte olduğu için bu geri arama çağrılmaz (veya her zaman değil), ancak uygulamanız hala kapalı olsa da "çalışır".

  2. Cihazınızın belleği düşükse (ve bellek stresi altındaysa), İşletim Sistemi bu çağrıyı yok sayıyor ve doğrudan daha kritik seviyelere gidiyor gibi görünüyor.

Şimdi, uygulamanızın ne zaman arka plana gittiğini bilmenin sizin için ne kadar önemli olduğuna bağlı olarak, etkinlik yaşam döngüsünü ve neyin olmadığını takip ederek bu çözümü genişletmeniz gerekebilir veya gerekmeyebilir.

Yukarıdakileri aklınızda bulundurun ve iyi bir KG ekibine sahip olun;)

GÜNCELLEME SONU

Geç olabilir, ancak Ice Cream Sandwich (API 14) ve Üstü'nde güvenilir bir yöntem vardır .

Uygulamanız artık görünür kullanıcı arayüzü olmadığında, bir geri çağrının tetiklendiği ortaya çıkıyor. Özel bir sınıfta uygulayabileceğiniz geri aramaya ComponentCallbacks2 (evet, ikisiyle) denir . Bu geri arama yalnızca API Level 14 (Ice Cream Sandwich) ve üstü sürümlerde kullanılabilir.

Temel olarak yönteme bir çağrı alırsınız:

public abstract void onTrimMemory (int level)

Seviye 20 veya daha spesifiktir

public static final int TRIM_MEMORY_UI_HIDDEN

Bunu test ediyorum ve her zaman işe yarıyor, çünkü seviye 20 sadece uygulamanız artık görünmediği için bazı kaynakları serbest bırakmak isteyebileceğiniz bir "öneri".

Resmi dokümanları alıntılamak için:

OnTrimMemory (int) düzeyi: işlem bir kullanıcı arabirimi gösteriyordu ve artık bunu yapmıyor. Belleğin daha iyi yönetilebilmesi için bu noktada UI ile büyük ayırmalar serbest bırakılmalıdır.

Tabii ki, olasılıklar diğer olası resmi belgeler (bkz sonsuzdur vb kullanılmadan durduğunda bazı koleksiyonları temizlemek, aslında belirli bir süre içinde kullanılmamış olan (tasfiye bellek söylediklerini yapmak bu uygulamalıdır daha fazla kritik seviyeler).

Ama ilginç olan şey, işletim sisteminin size söylediği: HEY, uygulamanız arka plana gitti!

İlk etapta tam olarak bilmek istediğiniz şey budur.

Ne zaman döndüğünüzü nasıl belirlersiniz?

Kolay Eh, bu kadar bir "BaseActivity" eminim olabilir bayrağa gerçeği olduğunu sen geri onResume () kullanın. Çünkü geri dönmeyeceğinizi söyleyeceğiniz tek zaman aslında yukarıdaki onTrimMemoryyönteme bir çağrı aldığınızdadır .

İşe yarıyor. Yanlış pozitifler elde edemezsiniz. Bir etkinlik devam ediyorsa,% 100 oranında geri döndünüz. Kullanıcı tekrar arkaya giderse, başka bir onTrimMemory()çağrı alırsınız .

Etkinliklerinizi (veya daha iyisi, özel bir sınıfı) susturmanız gerekir.

Bunu her zaman alacağınızı garanti etmenin en kolay yolu, aşağıdaki gibi basit bir sınıf oluşturmaktır:

public class MemoryBoss implements ComponentCallbacks2 {
    @Override
    public void onConfigurationChanged(final Configuration newConfig) {
    }

    @Override
    public void onLowMemory() {
    }

    @Override
    public void onTrimMemory(final int level) {
        if (level == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
            // We're in the Background
        }
        // you might as well implement some memory cleanup here and be a nice Android dev.
    }
}

Bunu kullanmak için, Uygulama uygulamanızda ( bir tane var, SAĞ? ), Aşağıdakileri yapın :

MemoryBoss mMemoryBoss;
@Override
public void onCreate() {
   super.onCreate();
   if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
      mMemoryBoss = new MemoryBoss();
      registerComponentCallbacks(mMemoryBoss);
   } 
}

Bir oluşturursanız Interfacebir ekleyebilirsiniz elsebuna ifve uygulamak ComponentCallbacksAPI 14. altındaki hiçbir kullanılan (2 olmadan) geri arama yalnızca var, diyeceksin onLowMemory()yöntem ve arka planda giderken çağrılmadığı , ancak Döşeme belleğe kullanmalısınız .

Şimdi Uygulamanızı başlatın ve eve basın. Sizin onTrimMemory(final int level)(: eklenti günlüğü ipucu) yöntemi çağrılmalıdır.

Son adım geri aramanın kaydını silmek. Muhtemelen en iyi yer onTerminate()Uygulamanızın yöntemidir, ancak bu yöntem gerçek bir cihazda çağrılmaz:

/**
 * This method is for use in emulated process environments.  It will
 * never be called on a production Android device, where processes are
 * removed by simply killing them; no user code (including this callback)
 * is executed when doing so.
 */

Dolayısıyla, artık kayıt olmak istemediğiniz bir durumunuz yoksa, işleminiz yine de işletim sistemi düzeyinde ölüyor olduğundan, güvenliği göz ardı edebilirsiniz.

Bir noktada kaydını silmeye karar verirseniz (örneğin, uygulamanızın temizlenmesi ve ölmesi için bir kapatma mekanizması sağlarsanız) şunları yapabilirsiniz:

unregisterComponentCallbacks(mMemoryBoss);

Ve bu kadar.


Bunu bir servisten kontrol ederken, yalnızca ev düğmesine basıldığında ateş gibi görünüyor. Geri düğmesine basmak KitKat'ta bunu başlatmaz.
OpenGL ES

Hizmetin kullanıcı arayüzü yok, bu yüzden bununla ilgili olabilir. Check-out'ı bir etkinlikte değil, temel faaliyetinizde yapın. Kullanıcı arayüzünüzün ne zaman gizli olduğunu bilmek istersiniz (ve belki de servise söyleyin, böylece ön
plana

1
Telefonunuzu kapattığınızda çalışmaz. Tetiklenmez.
Juangcg

2
ComponentCallbacks2.onTrimMemory () (ActivityLifecycleCallbacks ile birlikte) kullanmak şimdiye kadar bulduğum tek güvenilir çözüm, teşekkürler Martin! İlgilenenler için verilen cevabımı inceleyin.
rickul

3
Bu yöntemi bir yıldan beri kullanıyorum ve benim için her zaman güvenilir oldu. Diğer insanların da kullandığını bilmek güzel. Ben sadece level >= ComponentCallbacks2.TRIM_MEMORY_UI_HIDDENgüncellemenizde sorundan kaçınıyorum, nokta 2'yi kullanıyorum.
sorianiv

175

İşte bunu nasıl çözdüm. Etkinlik geçişleri arasında bir zaman referansı kullanmanın, büyük olasılıkla bir uygulamanın "arka planı" olup olmadığına dair yeterli kanıt sağlayacağı öncülünde çalışır.

İlk olarak, bir zamanlayıcı, TimerTask, bir aktiviteden diğerine geçişin makul olarak alabileceği maksimum milisaniye sayısını temsil eden bir sabite sahip bir android.app.Application örneği kullandım (buna Uygulamam diyelim) 2s değerine sahip) ve uygulamanın "arka planda" olup olmadığını belirtmek için bir boole değeri:

public class MyApplication extends Application {

    private Timer mActivityTransitionTimer;
    private TimerTask mActivityTransitionTimerTask;
    public boolean wasInBackground;
    private final long MAX_ACTIVITY_TRANSITION_TIME_MS = 2000;
    ...

Uygulama ayrıca zamanlayıcı / görevi başlatmak ve durdurmak için iki yöntem sağlar:

public void startActivityTransitionTimer() {
    this.mActivityTransitionTimer = new Timer();
    this.mActivityTransitionTimerTask = new TimerTask() {
        public void run() {
            MyApplication.this.wasInBackground = true;
        }
    };

    this.mActivityTransitionTimer.schedule(mActivityTransitionTimerTask,
                                           MAX_ACTIVITY_TRANSITION_TIME_MS);
}

public void stopActivityTransitionTimer() {
    if (this.mActivityTransitionTimerTask != null) {
        this.mActivityTransitionTimerTask.cancel();
    }

    if (this.mActivityTransitionTimer != null) {
        this.mActivityTransitionTimer.cancel();
    }

    this.wasInBackground = false;
}

Bu çözümün son parçası, bu yöntemlerin her birine, tüm etkinliklerin onResume () ve onPause () olaylarından veya tercihen tüm somut Faaliyetlerinizin miras aldığı bir temel Faaliyete bir çağrı eklemektir:

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

    MyApplication myApp = (MyApplication)this.getApplication();
    if (myApp.wasInBackground)
    {
        //Do specific came-here-from-background code
    }

    myApp.stopActivityTransitionTimer();
}

@Override
public void onPause()
{
    super.onPause();
    ((MyApplication)this.getApplication()).startActivityTransitionTimer();
}

Bu nedenle, kullanıcının uygulamanızın faaliyetleri arasında gezinmesi durumunda, çıkış yapan etkinliğin onPause () zamanlayıcısını başlatır, ancak girilen hemen hemen yeni etkinlik, maksimum geçiş süresine ulaşmadan önce zamanlayıcıyı iptal eder. Ve böylece wasInBackground olacağını yanlış .

Öte yandan, bir Aktivite Başlatıcı'dan ön plana geldiğinde, cihaz uyandırma, telefon görüşmesi bitirme vb., Bu olaydan önce çalıştırılan zamanlayıcı görevi büyük olasılıkla daha fazladır ve bu nedenle wasInBackground true olarak ayarlanmıştır .


4
Merhaba d60402, cevabınız gerçekten yararlı .. bu cevap için çok teşekkür ederim ... küçük bir uyarı .. MyApplication android gibi Manifest dosya uygulama etiketinde belirtilmelidir: name = "MyApplication", aksi takdirde uygulama çöküyor ... sadece yardım etmek benim gibi biri
praveenb

2
Büyük programcı, şimdiye kadar karşılaştığım en karmaşık sorunlardan birine basit bir çözüm.
Aashish Bhatnagar

2
Müthiş bir çözüm! Teşekkürler. Herkes "ClassCastException" hatası alırsa, Manifest.xml <uygulama android içindeki uygulama etiketine eklemeyi kaçırmış olabilirsiniz <uygulama android: name = "your.package.MyApplication"
Wahib Ul Haq 23:14

27
Bu güzel ve basit bir uygulamadır. Ancak bunun onPause / onResume yerine onStart / onStop üzerinde uygulanması gerektiğine inanıyorum. Etkinliği kısmen kapsayan bir iletişim kutusu başlatsam bile onPause çağrılır. Ve iletişim kapanış aslında onResume Uygulamanın yalnızca ön plana geldi sanki görünmesini sağlamak çağırır
Shubhayu

7
Bu çözümün bir varyasyonunu kullanmayı umuyorum. Yukarıda tanımlanan diyaloglarla ilgili nokta benim için bir problem, bu yüzden @ Shubhayu'nun önerisini (onStart / onStop) denedim. Ancak bu yardımcı olmaz çünkü A-> B'ye giderken B Etkinliğinin onStart () etkinliği A Etkinliğinin onStop () öğesinden önce çağrılır.
Trevor

150

Düzenleme: yeni mimari bileşenler umut verici bir şey getirdi: ProcessLifecycleOwner , bkz. @ Vokilam'ın cevabı


Google I / O konuşmasına göre gerçek çözüm :

class YourApplication : Application() {

  override fun onCreate() {
    super.onCreate()
    registerActivityLifecycleCallbacks(AppLifecycleTracker())
  }

}


class AppLifecycleTracker : Application.ActivityLifecycleCallbacks  {

  private var numStarted = 0

  override fun onActivityStarted(activity: Activity?) {
    if (numStarted == 0) {
      // app went to foreground
    }
    numStarted++
  }

  override fun onActivityStopped(activity: Activity?) {
    numStarted--
    if (numStarted == 0) {
      // app went to background
    }
  }

}

Evet. Burada çok garip çözümlere sahip olduğumuz için bu basit çözümün işe yaradığına inanmanın zor olduğunu biliyorum.

Ama umut var.


3
Mükemmel çalışıyor! Zaten çok fazla kusuru olan çok garip çözümler denedim ... çok teşekkürler! Bir süredir bunu arıyordum.
Eggakin Baconwalker

7
Birden fazla etkinlik için çalışır, ancak bir tane için onrotate tüm faaliyetlerin gittiğini veya arka planda olduğunu gösterir
deadfish

2
@Shyri haklısınız, ancak bu çözümün bir parçası bu yüzden endişelenmeniz gerekiyor. Firebase buna güveniyorsa, benim vasat uygulamamın da yapabileceğini düşünüyorum :) Harika cevap BTW.
ElliotM

3
@deadfish Cevabın üst kısmında verilen G / Ç bağlantısını kontrol edin. Gerçekten arka plana gidip gitmediğinizi belirlemek için etkinlik durdurma ve başlatma arasındaki zaman boşluklarını kontrol edebilirsiniz. Aslında bu harika bir çözüm.
Alex Berdnikov

2
Java çözümü var mı? Bu kotlin.
Giacomo Bartoli

116

ProcessLifecycleOwner umut verici bir çözüm gibi görünüyor.

ProcessLifecycleOwner sevk edecek ON_START, ON_RESUMEbu olaylar üzerinden bir ilk etkinlik hamle olarak, olayları. ON_PAUSE,, ON_STOPetkinlikler son etkinliklerden geçtikten sonra bir gecikmeyle gönderilir . Bu gecikme, ProcessLifecycleOwnerbir yapılandırma değişikliği nedeniyle etkinlikler yok edilir ve yeniden oluşturulursa herhangi bir etkinlik göndermeyeceğini garanti edecek kadar uzundur .

Bir uygulama kadar basit olabilir

class AppLifecycleListener : LifecycleObserver {

    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    fun onMoveToForeground() { // app moved to foreground
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    fun onMoveToBackground() { // app moved to background
    }
}

// register observer
ProcessLifecycleOwner.get().lifecycle.addObserver(AppLifecycleListener())

Kaynak koduna göre, mevcut gecikme değeri 700ms.

Ayrıca bu özelliği kullanmak için şunlar gerekir dependencies:

implementation "androidx.lifecycle:lifecycle-extensions:$lifecycleVersion"

10
implementation "android.arch.lifecycle:extensions:1.0.0"annotationProcessor "android.arch.lifecycle:compiler:1.0.0"google()
Yaşam

1
Bu benim için harika çalıştı, teşekkürler. Android bağımlılığının derleme ve çalışma zamanı sınıf yolu için farklı bir sürümü olduğunu belirten hata nedeniyle uygulama yerine api 'android.arch.lifecycle: extensions: 1.1.0' kullanmak zorunda kaldım.
FSUWX2011

Etkinlik referansına ihtiyaç duymadan modüllerde çalıştığı için bu harika bir çözüm!
Max

Uygulama çöktüğünde bu çalışmaz. Bu çözüm aracılığıyla da uygulama çöktü olayı almak için herhangi bir çözüm var mı
tejraj

Harika bir çözüm. Günümü kurtardım.
Sunny

69

Martín Marconcinis'in cevabına (teşekkürler!) Dayanarak sonunda güvenilir (ve çok basit) bir çözüm buldum.

public class ApplicationLifecycleHandler implements Application.ActivityLifecycleCallbacks, ComponentCallbacks2 {

    private static final String TAG = ApplicationLifecycleHandler.class.getSimpleName();
    private static boolean isInBackground = false;

    @Override
    public void onActivityCreated(Activity activity, Bundle bundle) {
    }

    @Override
    public void onActivityStarted(Activity activity) {
    }

    @Override
    public void onActivityResumed(Activity activity) {

        if(isInBackground){
            Log.d(TAG, "app went to foreground");
            isInBackground = false;
        }
    }

    @Override
    public void onActivityPaused(Activity activity) {
    }

    @Override
    public void onActivityStopped(Activity activity) {
    }

    @Override
    public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {
    }

    @Override
    public void onActivityDestroyed(Activity activity) {
    }

    @Override
    public void onConfigurationChanged(Configuration configuration) {
    }

    @Override
    public void onLowMemory() {
    }

    @Override
    public void onTrimMemory(int i) {
        if(i == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN){
            Log.d(TAG, "app went to background");
            isInBackground = true;
        }
    }
}

Daha sonra bunu Uygulama sınıfınızın onCreate () öğesine ekleyin

public class MyApp extends android.app.Application {

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

        ApplicationLifeCycleHandler handler = new ApplicationLifeCycleHandler();
        registerActivityLifecycleCallbacks(handler);
        registerComponentCallbacks(handler);

    }

}

Bunu bir uygulamada nasıl kullandığınızı gösterebilir misiniz, bunu App sınıfından veya başka bir yerden mi arayabilirim?
JPM

bu mükemmel teşekkür ederim !! şu ana kadar testte harika çalışıyor
aherrick

Bu örnek eksikse. RegisterActivityLifecycleCallbacks dosyası nedir?
Noman

sınıftaki bir yöntem android.app.Application
rickul

1
Üstüne çıkmak için +1 başarılı oldu, çünkü mükemmel, diğer cevaplara bakma, bu @reno cevabına dayanıyor ama gerçek bir örnekle
Stoycho Andreev

63

Bu yöntemi kullanıyoruz. Çalışmak için çok basit görünüyor, ama bizim app iyi test edildi ve aslında "ev" düğmesi, "dönüş" düğmesi veya ekran kilidi sonra ana ekrana gitmek de dahil olmak üzere her durumda şaşırtıcı derecede iyi çalışıyor. Bir şans ver.

Fikir, ön planda olduğunda, Android her zaman bir öncekini durdurmadan önce yeni etkinliklere başlar. Bu garanti edilmez, ama böyle çalışır. BTW, Flurry aynı mantığı kullanıyor gibi görünüyor (sadece bir tahmin, bunu kontrol etmedim, ama aynı olaylara kanca yapıyor).

public abstract class BaseActivity extends Activity {

    private static int sessionDepth = 0;

    @Override
    protected void onStart() {
        super.onStart();       
        sessionDepth++;
        if(sessionDepth == 1){
        //app came to foreground;
        }
    }

    @Override
    protected void onStop() {
        super.onStop();
        if (sessionDepth > 0)
            sessionDepth--;
        if (sessionDepth == 0) {
            // app went to background
        }
    }

}

Düzenleme: yorumlara göre, kodun sonraki sürümlerinde onStart () öğesine de taşındık. Ayrıca, ilk yazımda eksik olan süper çağrılar ekliyorum, çünkü bu bir çalışma kodundan çok bir kavramdı.


2
Bu en güvenilir yanıttır, ancak onResum yerine onStart kullanıyorum.
Greg Ennis

Geçersiz kılma yöntemlerinde super.onResume () ve super.onStop () işlevlerine çağrı eklemeniz gerekir. Aksi takdirde bir android.app.SuperNotCalledException atılır.
Jan Laussmann

1
benim için çalışmıyor ... ya da en azından cihazı da döndürürken olayı ateşliyor (bu bir tür yanlış pozitif imho).
Noya

Çok basit ve etkili bir çözüm! Ancak, önceki etkinliğin bazı bölümlerinin görünür olmasına izin veren kısmen şeffaf etkinliklerle çalıştığından emin değilim. Dokümanlardan onStop is called when the activity is no longer visible to the user.
Nicolas Buquet

3
kullanıcı ilk etkinliğin yönünü değiştirirse ne olur? Uygulamanın doğru olmayan arka plana gittiğini bildirecektir. Bu senaryoyu nasıl ele alıyorsunuz?
Nemrut Dayan

54

Uygulamanız bir sekme çubuğu widget'ı gibi birden fazla etkinlik ve / veya yığınlanmış etkinlik içeriyorsa, onPause () ve onResume () öğelerinin geçersiz kılınması çalışmaz. Yani yeni bir etkinlik başlatılırken, mevcut etkinlikler yeni etkinlik oluşturulmadan önce duraklatılır. Aynı şey bir etkinliği bitirirken ("geri" düğmesini kullanarak) uygulanır.

İstendiği gibi çalışan iki yöntem buldum.

Birincisi GET_TASKS iznini gerektirir ve paket adlarını karşılaştırarak cihazdaki en iyi çalışan etkinliğin uygulamaya ait olup olmadığını kontrol eden basit bir yöntemden oluşur:

private boolean isApplicationBroughtToBackground() {
    ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
    List<RunningTaskInfo> tasks = am.getRunningTasks(1);
    if (!tasks.isEmpty()) {
        ComponentName topActivity = tasks.get(0).topActivity;
        if (!topActivity.getPackageName().equals(context.getPackageName())) {
            return true;
        }
    }

    return false;
}

Bu yöntem Droid-Fu (şimdi Ateşleme olarak adlandırılır) çerçevesinde bulundu.

Kendimi uyguladığım ikinci yöntem GET_TASKS iznini gerektirmiyor, ki bu iyi. Bunun yerine uygulanması biraz daha karmaşıktır.

MainApplication sınıfınızda uygulamanızda çalışan etkinliklerin sayısını izleyen bir değişkeniniz vardır. Her etkinlik için onResume () öğesinde değişkeni artırırsınız ve onPause () öğesinde onu azaltırsınız.

Çalışan etkinlik sayısı 0'a ulaştığında, aşağıdaki koşullar geçerli olursa uygulama arka plana alınır:

  • Duraklatılan etkinlik bitmiyor ("geri" düğmesi kullanıldı). Bu yöntem activity.isFinishing () kullanılarak yapılabilir
  • Yeni bir etkinlik (aynı paket adı) başlatılmıyor. Bunu gösteren bir değişken ayarlamak için startActivity () yöntemini geçersiz kılabilir ve daha sonra bir etkinlik oluşturulduğunda / sürdürüldüğünde çalıştırılacak son yöntem olan onPostResume () yönteminde sıfırlayabilirsiniz.

Uygulamanın arka plana istifa ettiğini tespit edebildiğinizde, ön plana geri getirildiğinde de kolayca tespit edilebilir.


18
Google büyük olasılıkla ActivityManager.getRunningTasks () kullanan bir uygulamayı reddedecektir. Dokümantasyonda bunun sadece amaç amaçlı olduğu belirtiliyor. developer.android.com/reference/android/app/…
Sky Kelsey


1
Bu yaklaşımların bir kombinasyonunu kullanmak zorunda olduğumu fark ettim. 14'te bir etkinlik başlatılırken onUserLeaveHint () çağrıldı. `@ @UverLeaveHint () {inBackground = isApplicationBroughtToBackground (); } `
tekne listeleme

7
Kullanıcılar güçlü bir izin android.permission.GET_TASKS kullanma konusunda çok mutlu olmayacak.
MSquare

6
getRunningTasks, API düzey 21'de kullanımdan kaldırıldı.
Noya

33

Bir oluşturun sınıfını genişletir Application. Sonra içinde geçersiz kılma yöntemini kullanabiliriz onTrimMemory().

Uygulamanın arka plana gidip gitmediğini tespit etmek için şunu kullanacağız:

 @Override
    public void onTrimMemory(final int level) {
        if (level == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) { // Works for Activity
            // Get called every-time when application went to background.
        } 
        else if (level == ComponentCallbacks2.TRIM_MEMORY_COMPLETE) { // Works for FragmentActivty
        }
    }

1
Ayrıca FragmentActivitysiz de eklemek isteyebilirsiniz level == ComponentCallbacks2.TRIM_MEMORY_COMPLETE.
Srujan Simha

2
Bu yönteme işaret ettiğiniz için çok teşekkür ederim, kullanıcı arka plan için aktiviteyi sürdürdüğünde bir Pin İletişim Kutusu göstermem gerekiyor, bir ön değer yazmak için bu yöntemi kullandı ve baseActivity'de bu değeri kontrol ettim.
Sam

18

OnUserLeaveHint kullanmayı düşünün. Bu yalnızca uygulamanız arka plana girdiğinde çağrılacaktır. onPause, başka nedenlerle çağrılabileceğinden, ele alınması gereken köşe kasalara sahip olacaktır; örneğin kullanıcı uygulamanızda ayarlar sayfanız gibi başka bir etkinlik açarsa, ana etkinliğinizin onPause yöntemi uygulamanızda bulunsa bile çağrılır; ne olduğunu izlemek, sadece sorduğunuz şeyi yapan onUserLeaveHint geri aramasını kullanabileceğiniz zaman hatalara yol açacaktır.

UserLeaveHint üzerinde çağrıldığında, bir boolean inBackground bayrağını true olarak ayarlayabilirsiniz. OnResume çağrıldığında, yalnızca inBackground bayrağı ayarlanmışsa ön plana geri döndüğünüzü varsayalım. Bunun nedeni, kullanıcı yalnızca ayarlar menünüzdeyse ve uygulamadan hiç ayrılmadıysa onResume'un ana etkinliğinizde de çağrılmasıdır.

Kullanıcı ayarlar ekranınızdayken ana sayfa düğmesine basarsa, ayarlar etkinliğinizde onUserLeaveHint'in çağrıldığını ve ayarlar etkinliğinizde Resume öğesine geri dönüleceğini unutmayın. Bu algılama kodunu ana faaliyetinizde sadece bu kullanım durumunu kaçıracaksınız. Bu kodu, kodu çoğaltmadan tüm etkinliklerinizde kullanmak için, Etkinliği genişleten ve ortak kodunuzu içine koyan soyut bir etkinlik sınıfına sahip olun. O zaman sahip olduğunuz her etkinlik bu soyut etkinliği genişletebilir.

Örneğin:

public abstract AbstractActivity extends Activity {
    private static boolean inBackground = false;

    @Override
    public void onResume() {
        if (inBackground) {
            // You just came from the background
            inBackground = false;
        }
        else {
            // You just returned from another activity within your own app
        }
    }

    @Override
    public void onUserLeaveHint() {
        inBackground = true;
    }
}

public abstract MainActivity extends AbstractActivity {
    ...
}

public abstract SettingsActivity extends AbstractActivity {
    ...
}

19
onUserLeaveHint başka bir etkinliğe giderken de çağrılır
Jonas Stawski

3
onUserLeaveHint, örneğin bir telefon çağrısı geldiğinde ve arama etkinliği etkinleştiğinde çağrılmaz, bu nedenle bunun da bir uç durumu vardır - onUserLeaveHint çağrısını bastırmak amacıyla bir bayrak ekleyebileceğiniz için başka durumlar da olabilir. developer.android.com/reference/android/content/…
Groxx

1
Ayrıca, onResume iyi çalışmıyor. Denedim ve bazen telefon kilitliyken onResume çağrılır. Belgelerde onResume'un tanımını görürseniz şunları bulacaksınız: onResume'un, etkinliğinizin kullanıcı tarafından görülebildiğinin en iyi göstergesi olmadığını unutmayın; tuş kilidi gibi bir sistem penceresi ön tarafta olabilir. Etkinliğinizin kullanıcı tarafından görülebildiğinden emin olmak için (örneğin, bir oyuna devam etmek için) onWindowFocusChanged (boolean) kullanın. developer.android.com/reference/android/app/…
J-Rou

Bu çözüm, birden fazla etkinlik varsa ön plana / arka plana karar vermede yardımcı
olmaz.Lütfen

14

ActivityLifecycleCallback'ler ilgi çekici olabilir, ancak iyi belgelenmemiştir.

Yine de, registerActivityLifecycleCallbacks () öğesini çağırırsanız , Etkinlikler oluşturulduğunda, yok edildiğinde, vb. İçin geri arama alabilmeniz gerekir. Etkinlik için getComponentName () öğesini çağırabilirsiniz .


11
API seviyesi 14 = \ yana
Imort

Bu temiz ve benim için çalışıyor gibi görünüyor. Teşekkürler
duanbo1983

Bu, kabul edilen cevaptan nasıl farklıdır, her ikisi de aynı faaliyet yaşam döngüsüne dayanır değil mi?
Saitama

13

Android.arch.lifecycle paketi yaşam döngüsü farkında bileşenleri oluşturmasına izin verin sınıflar ve arayüzler sağlar

Uygulamanız LifecycleObserver arayüzünü uygulamalıdır:

public class MyApplication extends Application implements LifecycleObserver {

    @Override
    public void onCreate() {
        super.onCreate();
        ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    private void onAppBackgrounded() {
        Log.d("MyApp", "App in background");
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    private void onAppForegrounded() {
        Log.d("MyApp", "App in foreground");
    }
}

Bunu yapmak için build.gradle dosyanıza bu bağımlılığı eklemeniz gerekir:

dependencies {
    implementation "android.arch.lifecycle:extensions:1.1.1"
}

Google tarafından önerildiği gibi, yaşam döngüsü etkinlik yöntemlerinde yürütülen kodu en aza indirmelisiniz:

Yaygın bir örüntü, bağımlı bileşenlerin eylemlerini yaşam döngüsü aktivite ve parça yöntemlerinde uygulamaktır. Bununla birlikte, bu model, kodun zayıf bir organizasyonuna ve hataların çoğalmasına yol açar. Yaşam döngüsüne duyarlı bileşenleri kullanarak, bağımlı bileşenlerin kodunu yaşam döngüsü yöntemlerinden ve bileşenlerin kendilerine taşıyabilirsiniz.

Daha fazla bilgiyi buradan edinebilirsiniz: https://developer.android.com/topic/libraries/architecture/lifecycle


ve bunu aşağıdaki gibi tezahür etmek için ekleyin: <application android: name = ". AnotherApp">
AnotherApp

9

Uygulamanızda geri aramayı ekleyin ve kök etkinliğini aşağıdaki gibi kontrol edin:

@Override
public void onCreate() {
    super.onCreate();
    registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
        @Override
        public void onActivityStopped(Activity activity) {
        }

        @Override
        public void onActivityStarted(Activity activity) {
        }

        @Override
        public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
        }

        @Override
        public void onActivityResumed(Activity activity) {
        }

        @Override
        public void onActivityPaused(Activity activity) {
        }

        @Override
        public void onActivityDestroyed(Activity activity) {
        }

        @Override
        public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
            if (activity.isTaskRoot() && !(activity instanceof YourSplashScreenActivity)) {
                Log.e(YourApp.TAG, "Reload defaults on restoring from background.");
                loadDefaults();
            }
        }
    });
}

Bu uygulama biçimini kullanmayı düşünürüm. Bir aktiviteden diğerine geçiş sadece birkaç milisaniye sürer. Son etkinliğin kaybolduğu zamana bağlı olarak, kullanıcının belirli bir stratejiyle yeniden oturum açtığı düşünülebilir.
drindt

6

Github app-foreground-background-listen üzerine bir proje oluşturdum

Uygulamanızdaki tüm Etkinlik için bir BaseActivity oluşturun.

public class BaseActivity extends Activity {

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
    }

    public static boolean isAppInFg = false;
    public static boolean isScrInFg = false;
    public static boolean isChangeScrFg = false;

    @Override
    protected void onStart() {
        if (!isAppInFg) {
            isAppInFg = true;
            isChangeScrFg = false;
            onAppStart();
        }
        else {
            isChangeScrFg = true;
        }
        isScrInFg = true;

        super.onStart();
    }

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

        if (!isScrInFg || !isChangeScrFg) {
            isAppInFg = false;
            onAppPause();
        }
        isScrInFg = false;
    }

    public void onAppStart() {

        // Remove this toast
        Toast.makeText(getApplicationContext(), "App in foreground",    Toast.LENGTH_LONG).show();

        // Your code
    }

    public void onAppPause() {

        // Remove this toast
        Toast.makeText(getApplicationContext(), "App in background",  Toast.LENGTH_LONG).show();

        // Your code
    }
}

Şimdi bu BaseActivity'yi MainActivity gibi tüm Aktivitelerinizin süper sınıfı olarak kullanın, BaseActivity'yi genişletir ve uygulamanızı başlattığınızda onAppStart çağrılır ve uygulama herhangi bir ekrandan arka plana gittiğinde onAppPause () çağrılır.


@kiran boghra: Çözümünüzde yanlış pozitif var mı?
Harish Vishwakarma

Bu durumda onStart () ve onStop () işlevi mükemmel bir şekilde kullanılabilir. uygulamanızı anlatır
Pir Fahim Shah

6

ProcessLifecycleOwner ile bu oldukça kolay

Bu bağımlılıkları ekle

implementation "android.arch.lifecycle:extensions:$project.archLifecycleVersion"
kapt "android.arch.lifecycle:compiler:$project.archLifecycleVersion"

In KOTLIN :

class ForegroundBackgroundListener : LifecycleObserver {


    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    fun startSomething() {
        Log.v("ProcessLog", "APP IS ON FOREGROUND")
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    fun stopSomething() {
        Log.v("ProcessLog", "APP IS IN BACKGROUND")
    }
}

Sonra temel faaliyetinizde:

override fun onCreate() {
        super.onCreate()

        ProcessLifecycleOwner.get()
                .lifecycle
                .addObserver(
                        ForegroundBackgroundListener()
                                .also { appObserver = it })
    }

Bu konuyla ilgili makaleme bakın: https://medium.com/@egek92/how-to-actually-detect-foreground-background-changes-in-your-android-application-without-wanting-9719cc822c48


5

Bir yaşam döngüsü gözlemcisi ekleyerek ProcessLifecycleOwner'ı kullanabilirsiniz .

  public class ForegroundLifecycleObserver implements LifecycleObserver {

    @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
    public void onAppCreated() {
        Timber.d("onAppCreated() called");
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    public void onAppStarted() {
        Timber.d("onAppStarted() called");
    }

    @OnLifecycleEvent(Event.ON_RESUME)
    public void onAppResumed() {
        Timber.d("onAppResumed() called");
    }

    @OnLifecycleEvent(Event.ON_PAUSE)
    public void onAppPaused() {
        Timber.d("onAppPaused() called");
    }

    @OnLifecycleEvent(Event.ON_STOP)
    public void onAppStopped() {
        Timber.d("onAppStopped() called");
    }
}

daha sonra, onCreate()Uygulama sınıfınızın üzerinde şunu çağırırsınız:

ProcessLifecycleOwner.get().getLifecycle().addObserver(new ForegroundLifecycleObserver());

bununla birlikte arka planda gittiğinde gerçekleşen olayları ON_PAUSEve ON_STOPuygulamanızı yakalayabileceksiniz .


4

Uygulamanın ne zaman arka plan / ön plana gittiğini size söyleyecek basit bir yaşam döngüsü yöntemi yoktur.

Bunu basit bir şekilde yaptım. Uygulama arka plan / ön plan aşamasını tespit etmek için aşağıdaki talimatları izleyin.

Biraz geçici bir çözümle mümkündür. Burada, ActivityLifecycleCallbacks kurtarmaya gelir. Adım adım ilerleyeyim.

  1. İlk olarak, android.app.Application öğesini genişleten ve ActivityLifecycleCallbacks arabirimini uygulayan bir sınıf oluşturun . Application.onCreate () içinde geri aramayı kaydedin.

    public class App extends Application implements 
        Application.ActivityLifecycleCallbacks {
    
        @Override
        public void onCreate() {
            super.onCreate();
            registerActivityLifecycleCallbacks(this);
        }
    }
  2. Aşağıdaki gibi Manifest'te “App” sınıfını kaydedin <application android:name=".App",.

  3. Uygulama ön planda olduğunda başlangıç ​​durumunda en az bir Etkinlik olur ve uygulama arka planda olduğunda başlangıç ​​durumunda hiç Etkinlik olmaz.

    “App” sınıfında aşağıdaki 2 değişkeni bildirin.

    private int activityReferences = 0;
    private boolean isActivityChangingConfigurations = false;

    activityReferencesetkinliklerin sayısı sayısı tutacak başladı halde. isActivityChangingConfigurationsgeçerli Etkinliğin yön değiştirme anahtarı gibi yapılandırma değişikliğinden geçip geçmediğini gösteren bir işarettir.

  4. Aşağıdaki kodu kullanarak uygulamanın ön plana çıkıp çıkmadığını tespit edebilirsiniz.

    @Override
    public void onActivityStarted(Activity activity) {
        if (++activityReferences == 1 && !isActivityChangingConfigurations) {
            // App enters foreground
        }
    }
  5. Uygulamanın arka plana gidip gitmediğini nasıl tespit edersiniz.

    @Override
    public void onActivityStopped(Activity activity) {
        isActivityChangingConfigurations = activity.isChangingConfigurations();
        if (--activityReferences == 0 && !isActivityChangingConfigurations) {
            // App enters background
        }
    }

Nasıl çalışır:

Bu, Yaşam Döngüsü yöntemlerinin sırayla çağrılmasıyla yapılan küçük bir numaradır. Bir senaryo boyunca ilerleyeyim.

Kullanıcının Uygulamayı başlattığını ve Başlatıcı Etkinliği A'nın başlatıldığını varsayın. Yaşam Döngüsü çağrıları,

A.onCreate ()

A.onStart () (++ activityReferences == 1) (Uygulama Ön Plana girer)

A.onResume ()

Şimdi A Etkinliği B Etkinliğini başlatır.

A.onPause ()

B.onCreate ()

B.onStart () (++ activityReferences == 2)

B.onResume ()

A.onStop () (--activityReferences == 1)

Ardından kullanıcı Etkinlik B'den geri döner,

B.onPause ()

A.onStart () (++ activityReferences == 2)

A.onResume ()

B.onStop () (--activityReferences == 1)

B.onDestroy ()

Ardından kullanıcı Ana Ekran düğmesine basar,

A.onPause ()

A.onStop () (--activityReferences == 0) (Uygulama Arka Plana Giriyor)

Kullanıcının Geri düğmesi yerine Etkinlik B'den Ana Ekran düğmesine basması durumunda yine de aynı olur ve etkinlik 0 . Bu nedenle, Uygulama Arkaplan giren olarak tespit edebilir.

Peki rolü isActivityChangingConfigurationsnedir? Yukarıdaki senaryoda, Etkinlik B'nin yönlendirmeyi değiştirdiğini varsayalım. Geri arama sırası,

B.onPause ()

B.onStop () (--activityReferences == 0) (Uygulama Arka Plana Giriyor ??)

B.onDestroy ()

B.onCreate ()

B.onStart () (++ activityReferences == 1) (Uygulama Ön Plana Giriyor ??)

B.onResume ()

Bu nedenle isActivityChangingConfigurations, Etkinlik Yapılandırma değişikliklerinden geçerken senaryodan kaçınmak için ek bir kontrolümüz var .


3

Uygulamayı tespit etmek için ön plana veya arka plana girmek için iyi bir yöntem buldum. İşte kodum . Umarım bu sana yardımcı olur.

/**
 * Custom Application which can detect application state of whether it enter
 * background or enter foreground.
 *
 * @reference http://www.vardhan-justlikethat.blogspot.sg/2014/02/android-solution-to-detect-when-android.html
 */
 public abstract class StatusApplication extends Application implements ActivityLifecycleCallbacks {

public static final int STATE_UNKNOWN = 0x00;
public static final int STATE_CREATED = 0x01;
public static final int STATE_STARTED = 0x02;
public static final int STATE_RESUMED = 0x03;
public static final int STATE_PAUSED = 0x04;
public static final int STATE_STOPPED = 0x05;
public static final int STATE_DESTROYED = 0x06;

private static final int FLAG_STATE_FOREGROUND = -1;
private static final int FLAG_STATE_BACKGROUND = -2;

private int mCurrentState = STATE_UNKNOWN;
private int mStateFlag = FLAG_STATE_BACKGROUND;

@Override
public void onCreate() {
    super.onCreate();
    mCurrentState = STATE_UNKNOWN;
    registerActivityLifecycleCallbacks(this);
}

@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
    // mCurrentState = STATE_CREATED;
}

@Override
public void onActivityStarted(Activity activity) {
    if (mCurrentState == STATE_UNKNOWN || mCurrentState == STATE_STOPPED) {
        if (mStateFlag == FLAG_STATE_BACKGROUND) {
            applicationWillEnterForeground();
            mStateFlag = FLAG_STATE_FOREGROUND;
        }
    }
    mCurrentState = STATE_STARTED;

}

@Override
public void onActivityResumed(Activity activity) {
    mCurrentState = STATE_RESUMED;

}

@Override
public void onActivityPaused(Activity activity) {
    mCurrentState = STATE_PAUSED;

}

@Override
public void onActivityStopped(Activity activity) {
    mCurrentState = STATE_STOPPED;

}

@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {

}

@Override
public void onActivityDestroyed(Activity activity) {
    mCurrentState = STATE_DESTROYED;
}

@Override
public void onTrimMemory(int level) {
    super.onTrimMemory(level);
    if (mCurrentState == STATE_STOPPED && level >= TRIM_MEMORY_UI_HIDDEN) {
        if (mStateFlag == FLAG_STATE_FOREGROUND) {
            applicationDidEnterBackground();
            mStateFlag = FLAG_STATE_BACKGROUND;
        }
    }else if (mCurrentState == STATE_DESTROYED && level >= TRIM_MEMORY_UI_HIDDEN) {
        if (mStateFlag == FLAG_STATE_FOREGROUND) {
            applicationDidDestroyed();
            mStateFlag = FLAG_STATE_BACKGROUND;
        }
    }
}

/**
 * The method be called when the application been destroyed. But when the
 * device screen off,this method will not invoked.
 */
protected abstract void applicationDidDestroyed();

/**
 * The method be called when the application enter background. But when the
 * device screen off,this method will not invoked.
 */
protected abstract void applicationDidEnterBackground();

/**
 * The method be called when the application enter foreground.
 */
protected abstract void applicationWillEnterForeground();

}


3

Kullanabilirsiniz:

korumalı geçersizlik onRestart ()

Yeni başlangıç ​​ve yeniden başlatmalar arasında farklılık göstermek için.

resim açıklamasını buraya girin


3

Edit 2: Aşağıda yazdıklarım aslında çalışmaz. Google, ActivityManager.getRunningTasks () çağrısını içeren bir uygulamayı reddetti. Gönderen dokümantasyon , API hata ayıklama ve geliştirme amaçlı olduğu açıkça görülmektedir. GitHub projesini zamanlayıcıları kullanan ve neredeyse iyi olan yeni bir şema ile güncellemek için zamanım olduğunda bu yazıyı güncelleyeceğim.

Edit 1: Bir blog yazısı yazdım ve bunu kolaylaştırmak için basit bir GitHub deposu oluşturdum .

Kabul edilen ve en yüksek puan alan cevapların her ikisi de gerçekten en iyi yaklaşım değildir. En yüksek puan alan cevabın isApplicationBroughtToBackground () uygulaması, Uygulamanın ana Etkinliğinin aynı Uygulamada tanımlanan bir Etkinliğe ulaştığı durumu işlemez, ancak farklı bir Java paketine sahiptir. Bu durumda işe yarayacak bir yol buldum.

Bunu onPause () içinde çağırın, başka bir uygulama başlatıldığından veya kullanıcı ana sayfa düğmesine bastığından uygulamanızın arka plana girip girmediğini size söyleyecektir.

public static boolean isApplicationBroughtToBackground(final Activity activity) {
  ActivityManager activityManager = (ActivityManager) activity.getSystemService(Context.ACTIVITY_SERVICE);
  List<ActivityManager.RunningTaskInfo> tasks = activityManager.getRunningTasks(1);

  // Check the top Activity against the list of Activities contained in the Application's package.
  if (!tasks.isEmpty()) {
    ComponentName topActivity = tasks.get(0).topActivity;
    try {
      PackageInfo pi = activity.getPackageManager().getPackageInfo(activity.getPackageName(), PackageManager.GET_ACTIVITIES);
      for (ActivityInfo activityInfo : pi.activities) {
        if(topActivity.getClassName().equals(activityInfo.name)) {
          return false;
        }
      }
    } catch( PackageManager.NameNotFoundException e) {
      return false; // Never happens.
    }
  }
  return true;
}

FYI, bunun yerine onStart () 'da çağrılması, örneğin bir alarmın çalması gibi basit bir iletişim kutusu açıldığında çağrılmasını önleyecektir.
Sky Kelsey

2

Doğru cevap burada

Uygulamam adında aşağıdaki gibi bir sınıf oluşturun:

public class MyApp implements Application.ActivityLifecycleCallbacks, ComponentCallbacks2 {

    private Context context;
    public void setContext(Context context)
    {
        this.context = context;
    }

    private boolean isInBackground = false;

    @Override
    public void onTrimMemory(final int level) {
        if (level == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {


            isInBackground = true;
            Log.d("status = ","we are out");
        }
    }


    @Override
    public void onActivityCreated(Activity activity, Bundle bundle) {

    }

    @Override
    public void onActivityStarted(Activity activity) {

    }

    @Override
    public void onActivityResumed(Activity activity) {

        if(isInBackground){

            isInBackground = false;
            Log.d("status = ","we are in");
        }

    }

    @Override
    public void onActivityPaused(Activity activity) {

    }

    @Override
    public void onActivityStopped(Activity activity) {

    }

    @Override
    public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {

    }

    @Override
    public void onActivityDestroyed(Activity activity) {

    }

    @Override
    public void onConfigurationChanged(Configuration configuration) {

    }

    @Override
    public void onLowMemory() {

    }
}

Ardından, istediğiniz her yerde (uygulamada başlatılan daha iyi ilk etkinlik) aşağıdaki kodu ekleyin:

MyApp myApp = new MyApp();
registerComponentCallbacks(myApp);
getApplication().registerActivityLifecycleCallbacks(myApp);

Bitti! Şimdi uygulama arka planda status : we are out olduğunda günlük alıyoruz ve uygulamaya gittiğimizde günlük alıyoruzstatus : we are out


1

Çözümüm @ d60402'nin cevabından ilham aldı ve aynı zamanda bir zaman penceresine dayanıyor, ancak aşağıdakileri kullanmıyor Timer:

public abstract class BaseActivity extends ActionBarActivity {

  protected boolean wasInBackground = false;

  @Override
  protected void onStart() {
    super.onStart();
    wasInBackground = getApp().isInBackground;
    getApp().isInBackground = false;
    getApp().lastForegroundTransition = System.currentTimeMillis();
  }

  @Override
  protected void onStop() {
    super.onStop();
    if( 1500 < System.currentTimeMillis() - getApp().lastForegroundTransition )
      getApp().isInBackground = true;
  }

  protected SingletonApplication getApp(){
    return (SingletonApplication)getApplication();
  }
}

burada sınıfın SingletonApplicationbir uzantısıdır Application:

public class SingletonApplication extends Application {
  public boolean isInBackground = false;
  public long lastForegroundTransition = 0;
}

1

Bunu Google Analytics EasyTracker ile kullanıyordum ve işe yaradı. Basit bir tamsayı kullanarak aradığınız şeyi yapmak için genişletilebilir.

public class MainApplication extends Application {

    int isAppBackgrounded = 0;

    @Override
    public void onCreate() {
        super.onCreate();
        appBackgroundedDetector();
    }

    private void appBackgroundedDetector() {
        registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
            @Override
            public void onActivityCreated(Activity activity, Bundle bundle) {

            }

            @Override
            public void onActivityStarted(Activity activity) {
                EasyTracker.getInstance(MainApplication.this).activityStart(activity);
            }

            @Override
            public void onActivityResumed(Activity activity) {
                isAppBackgrounded++;
                if (isAppBackgrounded > 0) {
                    // Do something here
                }
            }

            @Override
            public void onActivityPaused(Activity activity) {
                isAppBackgrounded--;
            }

            @Override
            public void onActivityStopped(Activity activity) {
                EasyTracker.getInstance(MainApplication.this).activityStop(activity);
            }

            @Override
            public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {

            }

            @Override
            public void onActivityDestroyed(Activity activity) {

            }
        });
    }
}

1

Ben biraz geç biliyorum ama ben aşağıdaki gibi yaptım ve bu mükemmel çalışır tüm bu cevaplar bazı sorunlar var düşünüyorum.

şöyle bir aktivite yaşam döngüsü geri çağrısı oluşturun:

 class ActivityLifeCycle implements ActivityLifecycleCallbacks{

    @Override
    public void onActivityCreated(Activity activity, Bundle savedInstanceState) {

    }

    @Override
    public void onActivityStarted(Activity activity) {

    }

    Activity lastActivity;
    @Override
    public void onActivityResumed(Activity activity) {
        //if (null == lastActivity || (activity != null && activity == lastActivity)) //use this condition instead if you want to be informed also when  app has been killed or started for the first time
        if (activity != null && activity == lastActivity) 
        {
            Toast.makeText(MyApp.this, "NOW!", Toast.LENGTH_LONG).show();
        }

        lastActivity = activity;
    }

    @Override
    public void onActivityPaused(Activity activity) {

    }

    @Override
    public void onActivityStopped(Activity activity) {

    }

    @Override
    public void onActivitySaveInstanceState(Activity activity, Bundle outState) {

    }

    @Override
    public void onActivityDestroyed(Activity activity) {

    }
}

ve aşağıdaki gibi uygulama sınıfınıza kaydetmeniz yeterlidir:

public class MyApp extends Application {

@Override
public void onCreate() {
    super.onCreate();
    registerActivityLifecycleCallbacks(new ActivityLifeCycle());
}

Bu, her etkinlik için her zaman çağrılır. Örneğin, çevrimiçi kullanıcı durumunu saptamak istersem bunu nasıl kullanabilirim
Maksim Kniazev

sorunun istediği bu. yalnızca ana ekrana gidip herhangi bir etkinliğe döndüğünüzde çağrılır.
Amir Ziarati

internet bağlantısı demek istiyorsanız, ihtiyacınız olduğunu kontrol etmek daha iyi olduğunu düşünüyorum. Bir API çağırmanız gerekiyorsa, aramadan hemen önce internet bağlantısını kontrol edin.
Amir Ziarati

1

Bu, Android'deki en karmaşık sorulardan biri gibi görünüyor (çünkü bu yazı itibariyle) Android'in iOS eşdeğerlerine applicationDidEnterBackground()veya applicationWillEnterForeground()geri aramalarına sahip değil . @Jenzz tarafından bir araya getirilen bir AppState Kütüphanesi kullandım .

[AppState], uygulama durumu değişikliklerini izleyen RxJava tabanlı basit ve reaktif bir Android kütüphanesidir. Uygulama arka plana girdiğinde ve ön plana geri döndüğünde aboneleri bilgilendirir.

Bu benim app tam olarak ihtiyaç duyduğu ortaya çıktı, çünkü özellikle benim app birden fazla aktivite vardı, bu yüzden sadece kontrol onStart()veya onStop()bir aktivite onu kesecek değildi.

İlk önce bu bağımlılıkları derecelendirmek için ekledim:

dependencies {
    compile 'com.jenzz.appstate:appstate:3.0.1'
    compile 'com.jenzz.appstate:adapter-rxjava2:3.0.1'
}

Ardından, bu satırları kodunuzdaki uygun bir yere eklemek basit bir konuydu:

//Note that this uses RxJava 2.x adapter. Check the referenced github site for other ways of using observable
Observable<AppState> appState = RxAppStateMonitor.monitor(myApplication);
//where myApplication is a subclass of android.app.Application
appState.subscribe(new Consumer<AppState>() {
    @Override
    public void accept(@io.reactivex.annotations.NonNull AppState appState) throws Exception {
        switch (appState) {
            case FOREGROUND:
                Log.i("info","App entered foreground");
                break;
            case BACKGROUND:
                Log.i("info","App entered background");
                break;
        }
    }
});

Gözlenebilirlere nasıl abone olduğunuza bağlı olarak, bellek sızıntılarını önlemek için aboneliğinizi iptal etmeniz gerekebilir. Yine github sayfasında daha fazla bilgi .


1

Bu @ d60402'nin cevabının değiştirilmiş versiyonudur: https://stackoverflow.com/a/15573121/4747587

Orada belirtilen her şeyi yapın. Ama bunun yerine bir sahip Base Activityve her aktivite için bir ebeveyn olarak belirttik ve geçersiz kılma onResume()ve onPauseaşağıda yapın:

Uygulama sınıfınıza şu satırı ekleyin:

registerActivityLifecycleCallbacks (Application.ActivityLifecycleCallbacks geri arama);

Bu callback, tüm etkinlik yaşam döngüsü yöntemlerine sahiptir ve şimdi onActivityResumed()ve öğelerini geçersiz kılabilirsiniz onActivityPaused().

Bu özete bir göz atın: https://gist.github.com/thsaravana/1fa576b6af9fc8fff20acfb2ac79fa1b


1

Bunu kolayca ActivityLifecycleCallbacksve ComponentCallbacks2aşağıdaki gibi bir şeyle başarabilirsiniz .

AppLifeCycleHandlerBahsedilen arabirimlerin üzerinde bir sınıf oluşturun .

package com.sample.app;

import android.app.Activity;
import android.app.Application;
import android.content.ComponentCallbacks2;
import android.content.res.Configuration;
import android.os.Bundle;

/**
 * Created by Naveen on 17/04/18
 */
public class AppLifeCycleHandler
    implements Application.ActivityLifecycleCallbacks, ComponentCallbacks2 {

  AppLifeCycleCallback appLifeCycleCallback;

  boolean appInForeground;

  public AppLifeCycleHandler(AppLifeCycleCallback appLifeCycleCallback) {
    this.appLifeCycleCallback = appLifeCycleCallback;
  }

  @Override
  public void onActivityResumed(Activity activity) {
    if (!appInForeground) {
      appInForeground = true;
      appLifeCycleCallback.onAppForeground();
    }
  }

  @Override
  public void onTrimMemory(int i) {
    if (i == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
      appInForeground = false;
      appLifeCycleCallback.onAppBackground();
    }
  }

  @Override
  public void onActivityCreated(Activity activity, Bundle bundle) {

  }

  @Override
  public void onActivityStarted(Activity activity) {

  }

  @Override
  public void onActivityPaused(Activity activity) {

  }

  @Override
  public void onActivityStopped(Activity activity) {

  }

  @Override
  public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {

  }

  @Override
  public void onActivityDestroyed(Activity activity) {

  }

  @Override
  public void onConfigurationChanged(Configuration configuration) {

  }

  @Override
  public void onLowMemory() {

  }

  interface AppLifeCycleCallback {

    void onAppBackground();

    void onAppForeground();
  }
}

Senin sınıfında uzanan Applicationuygulamak AppLifeCycleCallbackuygulama ön planda ve arka plan arasında geçiş yaparken geri aramalar alır. Aşağıdaki gibi bir şey.

public class BaseApplication extends Application implements AppLifeCycleHandler.AppLifeCycleCallback{

    @Override
    public void onCreate() {
        super.onCreate();
        AppLifeCycleHandler appLifeCycleHandler = new AppLifeCycleHandler(this);
        registerActivityLifecycleCallbacks(appLifeCycleHandler);
        registerComponentCallbacks(appLifeCycleHandler);
    }

    @Override
    public void onAppBackground() {
        Log.d("LifecycleEvent", "onAppBackground");
    }

    @Override
    public void onAppForeground() {
        Log.d("LifecycleEvent", "onAppForeground");
    }
}

Bu yardımcı olur umarım.

EDIT Alternatif olarak artık Yaşam döngüsü farkında mimari bileşenini kullanabilirsiniz.


1

Zaman damgalarını kontrol etmeden rotasyonu da ele alan herhangi bir yaklaşım bulamadığımdan, şimdi de bunu uygulamamızda nasıl yaptığımızı paylaştığımı düşündüm. Bu cevaba https://stackoverflow.com/a/42679191/5119746 adresindeki tek ekleme , yönlendirmeyi de dikkate almamızdır.

class MyApplication : Application(), Application.ActivityLifecycleCallbacks {

   // Members

   private var mAppIsInBackground = false
   private var mCurrentOrientation: Int? = null
   private var mOrientationWasChanged = false
   private var mResumed = 0
   private var mPaused = 0

Sonra, geri aramalar için önce özgeçmişimiz var:

   // ActivityLifecycleCallbacks

   override fun onActivityResumed(activity: Activity?) {

      mResumed++

      if (mAppIsInBackground) {

         // !!! App came from background !!! Insert code

         mAppIsInBackground = false
      }
      mOrientationWasChanged = false
    }

Ve onActivityStopped:

   override fun onActivityStopped(activity: Activity?) {

       if (mResumed == mPaused && !mOrientationWasChanged) {

       // !!! App moved to background !!! Insert code

        mAppIsInBackground = true
    }

Ve sonra, ek: İşte yön değişikliklerini kontrol etme:

   override fun onConfigurationChanged(newConfig: Configuration) {

       if (newConfig.orientation != mCurrentOrientation) {
           mCurrentOrientation = newConfig.orientation
           mOrientationWasChanged = true
       }
       super.onConfigurationChanged(newConfig)
   }

Bu kadar. Umarım bu birine yardımcı olur :)


1

Bu çözümü aşağıdakileri kullanarak genişletebiliriz LiveData:

class AppForegroundStateLiveData : LiveData<AppForegroundStateLiveData.State>() {

    private var lifecycleListener: LifecycleObserver? = null

    override fun onActive() {
        super.onActive()
        lifecycleListener = AppLifecycleListener().also {
            ProcessLifecycleOwner.get().lifecycle.addObserver(it)
        }
    }

    override fun onInactive() {
        super.onInactive()
        lifecycleListener?.let {
            this.lifecycleListener = null
            ProcessLifecycleOwner.get().lifecycle.removeObserver(it)
        }
    }

    internal inner class AppLifecycleListener : LifecycleObserver {

        @OnLifecycleEvent(Lifecycle.Event.ON_START)
        fun onMoveToForeground() {
            value = State.FOREGROUND
        }

        @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
        fun onMoveToBackground() {
            value = State.BACKGROUND
        }
    }

    enum class State {
        FOREGROUND, BACKGROUND
    }
}

Şimdi bu LiveData'ya abone olabilir ve gerekli olayları yakalayabiliriz. Örneğin:

appForegroundStateLiveData.observeForever { state ->
    when(state) {
        AppForegroundStateLiveData.State.FOREGROUND -> { /* app move to foreground */ }
        AppForegroundStateLiveData.State.BACKGROUND -> { /* app move to background */ }
    }
}

0

Bu cevaplar doğru görünmüyor. Bu yöntemler, başka bir etkinlik başladığında ve bittiğinde de çağrılır. Yapabileceğiniz şey global bir bayrak tutmaktır (evet, globaller kötü :) ve her yeni aktiviteye başladığınızda bunu true olarak ayarlayın. Her etkinliğin onCreate öğesinde false olarak ayarlayın. Ardından, onPause'da bu bayrağı kontrol edersiniz. Yanlışsa, uygulamanız arka plana gider veya öldürülür.


Bir veritabanı hakkında konuşmadım ... ne demek istiyorsun?
Joris Weimar

Cevabını destekliyorum. duraklama çağrısı sırasında bu bayrak değerini veritabanına kaydedebilmemize rağmen bu iyi bir çözüm değildir ..
Sandeep P
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.