Cara mendeteksi ketika aplikasi Android pergi ke latar belakang dan kembali ke latar depan

382

Saya mencoba untuk menulis aplikasi yang melakukan sesuatu yang spesifik ketika dibawa kembali ke latar depan setelah beberapa waktu. Apakah ada cara untuk mendeteksi kapan aplikasi dikirim ke latar belakang atau dibawa ke latar depan?

iHorse
sumber
2
Mungkin menambahkan kasus penggunaan ke pertanyaan karena tampaknya tidak jelas, jadi itu tidak dibahas dalam jawaban yang diberikan. Aplikasi dapat memulai aplikasi lain (Galeri misalnya), yang masih akan berada di tumpukan yang sama dan muncul sebagai salah satu layar aplikasi, dan kemudian tekan tombol Rumah. Tidak ada metode yang mengandalkan siklus hidup Aplikasi (atau bahkan manajemen memori) yang dapat mendeteksi ini. Mereka akan memicu status latar belakang tepat ketika Aktivitas eksternal muncul, bukan ketika Anda menekan Home.
Dennis K
Ini adalah jawaban yang Anda cari: stackoverflow.com/a/42679191/2352699
Fred Porciúncula
1
Lihat Google Solution: stackoverflow.com/questions/3667022/…
user1269737

Jawaban:

98

Metode onPause()dan onResume()dipanggil ketika aplikasi dibawa ke latar belakang dan ke latar depan lagi. Namun, mereka juga dipanggil ketika aplikasi dimulai untuk pertama kalinya dan sebelum dimatikan. Anda dapat membaca lebih lanjut di Activity .

Tidak ada pendekatan langsung untuk mendapatkan status aplikasi saat berada di latar belakang atau latar depan, tetapi bahkan saya telah menghadapi masalah ini dan menemukan solusinya dengan onWindowFocusChangeddan onStop.

Untuk detail lebih lanjut, periksa di sini Android: Solusi untuk mendeteksi ketika aplikasi Android pergi ke latar belakang dan kembali ke latar depan tanpa getRunningTasks atau getRunningAppProcesses .

Girish Nair
sumber
174
Namun pendekatan ini menyebabkan positif palsu seperti yang ditunjukkan oleh orang lain, karena metode ini juga disebut ketika beralih antar kegiatan dalam aplikasi yang sama.
John Lehmann
9
Lebih buruk dari itu. Saya mencobanya dan kadang-kadang onResume dipanggil saat telepon terkunci. Jika Anda melihat definisi onResume dalam dokumentasi, Anda akan menemukan: Ingatlah bahwa onResume bukanlah indikator terbaik bahwa aktivitas Anda dapat dilihat oleh pengguna; jendela sistem seperti pengaman tombol mungkin ada di depan. Gunakan onWindowFocusChanged (boolean) untuk mengetahui dengan pasti bahwa aktivitas Anda dapat dilihat oleh pengguna (misalnya, untuk melanjutkan permainan). developer.android.com/reference/android/app/...
J-Rou
2
Solusi yang dipasang di tautan tidak menggunakan onResume / onPause, alih-alih kombinasi onBackPressed, onStop, onStart, dan onWindowsFocusChanged. Itu berhasil bagi saya, dan saya memiliki hierarki UI yang agak rumit (dengan laci, viewpager dinamis, dll.)
Martin Marconcini
18
OnPause dan onResume khusus untuk Kegiatan. Bukan Aplikasi. Ketika suatu Aplikasi diletakkan di latar belakang dan kemudian dilanjutkan, itu akan melanjutkan kembali Kegiatan spesifik sebelum pergi ke latar belakang. Ini berarti bahwa Anda perlu menerapkan apa pun yang ingin Anda lakukan untuk melanjutkan dari latar belakang di semua Kegiatan Aplikasi Anda. Saya percaya pertanyaan awal mencari sesuatu seperti "onResume" untuk Aplikasi dan bukan Aktivitas.
SysHex
4
Saya tidak percaya API yang tepat tidak ditawarkan untuk kebutuhan umum seperti itu. Awalnya saya pikir padaUserLeaveHint () akan memotongnya, tetapi Anda tidak bisa memastikan apakah pengguna meninggalkan aplikasi atau tidak
atsakiridis
197

2018: Android mendukung ini secara alami melalui komponen siklus hidup.

PEMBARUAN Maret 2018 : Sekarang ada solusi yang lebih baik. Lihat ProcessLifecycleOwner . Anda perlu menggunakan komponen arsitektur baru 1.1.0 (terbaru saat ini) tetapi dirancang khusus untuk melakukan ini.

Ada contoh sederhana yang disediakan dalam jawaban ini tetapi saya menulis contoh aplikasi dan posting blog tentang itu.

Sejak saya menulis ini kembali pada tahun 2014, berbagai solusi muncul. Beberapa bekerja, beberapa dianggap bekerja , tetapi memiliki kekurangan (termasuk saya!) Dan kami, sebagai komunitas (Android) belajar untuk hidup dengan konsekuensinya dan menulis solusi untuk kasus-kasus khusus.

Jangan pernah menganggap satu cuplikan kode adalah solusi yang Anda cari, tidak mungkin demikian; lebih baik lagi, cobalah untuk memahami apa yang dilakukannya dan mengapa ia melakukannya.

The MemoryBosskelas tidak pernah benar-benar digunakan oleh saya seperti yang tertulis di sini, itu hanya bagian dari kode semu yang terjadi dengan pekerjaan.

Kecuali ada alasan yang sah bagi Anda untuk tidak menggunakan komponen arsitektur baru (dan ada beberapa, terutama jika Anda menargetkan apis super lama), maka lanjutkan dan gunakan. Mereka jauh dari sempurna, tetapi juga tidak ComponentCallbacks2.

PEMBARUAN / CATATAN (November 2015) : Orang-orang telah membuat dua komentar, pertama adalah yang >=harus digunakan alih-alih ==karena dokumentasi menyatakan bahwa Anda tidak harus memeriksa nilai yang tepat . Ini bagus untuk sebagian besar kasus, tetapi ingatlah bahwa jika Anda hanya peduli melakukan sesuatu ketika aplikasi pergi ke latar belakang, Anda harus menggunakan == dan juga menggabungkannya dengan solusi lain (seperti callback Daur Hidup Aktivitas), atau Anda mungkin tidak mendapatkan efek yang Anda inginkan. Contohnya (dan ini terjadi pada saya) adalah jika Anda ingin mengunciaplikasi Anda dengan layar kata sandi ketika pergi ke latar belakang (seperti 1 Kata sandi jika Anda terbiasa dengan itu), Anda mungkin secara tidak sengaja mengunci aplikasi Anda jika kehabisan memori dan tiba-tiba menguji >= TRIM_MEMORY, karena Android akan memicu LOW MEMORYpanggilan dan itu lebih tinggi dari milikmu. Jadi berhati-hatilah bagaimana / apa yang Anda uji.

Selain itu, beberapa orang bertanya tentang cara mendeteksi ketika Anda kembali.

Cara paling sederhana yang dapat saya pikirkan dijelaskan di bawah ini, tetapi karena beberapa orang tidak terbiasa dengan itu, saya menambahkan beberapa kode pseudo di sini. Dengan asumsi Anda memiliki YourApplicationdan MemoryBosskelas - kelas, di class BaseActivity extends Activity(Anda perlu membuat satu jika Anda tidak memilikinya).

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

Saya merekomendasikan onStart karena Dialog dapat menjeda suatu kegiatan, jadi saya yakin Anda tidak ingin aplikasi Anda berpikir "itu pergi ke latar belakang" jika semua yang Anda lakukan adalah menampilkan dialog layar penuh, tetapi jarak tempuh Anda mungkin bervariasi.

Dan itu saja. Kode di blok if akan hanya akan dieksekusi sekali , bahkan jika Anda pergi ke kegiatan lain, yang baru (yang juga extends BaseActivity) akan melaporkan wasInBackgroundini falsesehingga tidak akan mengeksekusi kode, sampai onMemoryTrimmeddisebut dan bendera diatur ke benar lagi .

Semoga itu bisa membantu.

UPDATE / CATATAN (April 2015) : Sebelum Anda pergi semua Salin dan Tempel pada kode ini, perhatikan bahwa saya telah menemukan beberapa contoh di mana itu mungkin tidak 100% dapat diandalkan dan harus dikombinasikan dengan metode lain untuk mencapai hasil terbaik. Khususnya, ada dua contoh yang diketahui di mana onTrimMemorypanggilan kembali tidak dijamin akan dieksekusi:

  1. Jika ponsel Anda mengunci layar saat aplikasi Anda terlihat (mis. Perangkat Anda mengunci setelah beberapa menit), panggilan balik ini tidak dipanggil (atau tidak selalu) karena layar kunci hanya di atas, tetapi aplikasi Anda masih "berjalan" walaupun tertutup.

  2. Jika perangkat Anda relatif rendah pada memori (dan di bawah tekanan memori), Sistem Operasi tampaknya mengabaikan panggilan ini dan langsung ke tingkat yang lebih kritis.

Sekarang, tergantung seberapa penting bagi Anda untuk mengetahui kapan aplikasi Anda pergi ke latar belakang, Anda mungkin atau mungkin tidak perlu memperluas solusi ini bersama dengan melacak siklus hidup aktivitas dan yang lainnya.

Ingatlah selalu hal di atas dan miliki tim QA yang baik;)

AKHIR PEMBARUAN

Mungkin terlambat tetapi ada metode yang dapat diandalkan di Ice Cream Sandwich (API 14) dan di atasnya .

Ternyata ketika aplikasi Anda tidak memiliki UI yang lebih terlihat, panggilan balik dipicu. Callback, yang dapat Anda terapkan dalam kelas khusus, disebut ComponentCallbacks2 (ya, dengan dua). Callback ini hanya tersedia di API Level 14 (Ice Cream Sandwich) dan di atasnya.

Anda pada dasarnya mendapat panggilan ke metode:

public abstract void onTrimMemory (int level)

Levelnya adalah 20 atau lebih khusus

public static final int TRIM_MEMORY_UI_HIDDEN

Saya telah menguji ini dan selalu berhasil, karena level 20 hanyalah "saran" agar Anda ingin melepaskan beberapa sumber daya karena aplikasi Anda tidak lagi terlihat.

Mengutip dokumen resmi:

Level untuk onTrimMemory (int): proses telah menunjukkan antarmuka pengguna, dan tidak lagi melakukannya. Alokasi besar dengan UI harus dirilis pada saat ini untuk memungkinkan memori dikelola dengan lebih baik.

Tentu saja, Anda harus menerapkan ini untuk benar-benar melakukan apa yang dikatakannya (membersihkan memori yang belum pernah digunakan dalam waktu tertentu, membersihkan beberapa koleksi yang telah duduk tidak digunakan, dll. Kemungkinannya tidak terbatas (lihat dokumen resmi untuk kemungkinan lainnya lebih lanjut) level kritis ).

Tapi, yang menarik, OS memberitahu Anda: HEY, aplikasi Anda pergi ke latar belakang!

Itulah yang ingin Anda ketahui sejak awal.

Bagaimana Anda menentukan kapan Anda kembali?

Yah itu mudah, saya yakin Anda memiliki "BaseActivity" sehingga Anda dapat menggunakan onResume () Anda untuk menandai fakta bahwa Anda kembali. Karena satu-satunya saat Anda mengatakan Anda tidak kembali adalah ketika Anda benar-benar menerima panggilan ke onTrimMemorymetode di atas .

Berhasil. Anda tidak mendapatkan positif palsu. Jika suatu kegiatan dilanjutkan, Anda kembali, 100% dari waktu. Jika pengguna kembali ke belakang, Anda mendapat onTrimMemory()panggilan lain .

Anda harus memasukkan Aktivitas Anda (atau lebih baik, kelas khusus).

Cara termudah untuk menjamin bahwa Anda selalu menerima ini adalah dengan membuat kelas sederhana seperti ini:

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.
    }
}

Untuk menggunakan ini, dalam implementasi Aplikasi Anda ( Anda memilikinya, KANAN? ), Lakukan sesuatu seperti:

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

Jika Anda membuat, InterfaceAnda bisa menambahkan elseitu ifdan menerapkan ComponentCallbacks(tanpa 2) yang digunakan dalam apa pun di bawah API 14. Callback itu hanya memiliki onLowMemory()metode dan tidak dipanggil ketika Anda pergi ke latar belakang , tetapi Anda harus menggunakannya untuk memangkas memori .

Sekarang luncurkan Aplikasi Anda dan tekan home. AndaonTrimMemory(final int level)Metode harus dipanggil (petunjuk: tambahkan logging).

Langkah terakhir adalah membatalkan registrasi dari callback. Mungkin tempat terbaik adalah onTerminate()metode Aplikasi Anda, tetapi metode itu tidak dipanggil di perangkat nyata:

/**
 * 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.
 */

Jadi, kecuali Anda benar-benar memiliki situasi di mana Anda tidak lagi ingin didaftarkan, Anda dapat dengan aman mengabaikannya, karena proses Anda sedang sekarat di tingkat OS.

Jika Anda memutuskan untuk membatalkan registrasi di beberapa titik (jika Anda, misalnya, memberikan mekanisme mematikan aplikasi untuk membersihkan dan mati), Anda dapat melakukan:

unregisterComponentCallbacks(mMemoryBoss);

Dan itu saja.

Martin Marconcini
sumber
Saat memeriksa ini dari layanan, tampaknya hanya menyala ketika tombol home ditekan. Menekan tombol kembali tidak menyalakan ini di KitKat.
Pelajari OpenGL ES
Layanan tidak memiliki UI sehingga dapat dikaitkan dengan itu. Lakukan pengecekan dalam aktivitas dasar Anda, bukan pada layanan. Anda ingin tahu kapan UI Anda disembunyikan (dan mungkin memberi tahu layanannya, begitu seterusnya Foreground)
Martin Marconcini
1
Tidak berfungsi saat Anda mematikan telepon. Itu tidak dipicu.
Juangcg
2
Menggunakan ComponentCallbacks2.onTrimMemory () (dalam kombinasi dengan ActivityLifecycleCallbacks) adalah satu-satunya solusi andal yang saya temukan sejauh ini, terima kasih Martin! Bagi yang berminat, lihat jawaban saya.
rickul
3
Saya telah menggunakan metode ini sejak setahun yang lalu dan selalu dapat diandalkan untuk saya. Senang mengetahui orang lain menggunakannya juga. Saya hanya menggunakan level >= ComponentCallbacks2.TRIM_MEMORY_UI_HIDDENyang menghindari masalah dalam pembaruan Anda, poin 2. Mengenai poin 1, itu bukan masalah bagi saya, karena aplikasi tidak benar-benar pergi ke latar belakang, jadi itulah cara yang seharusnya berfungsi.
sorianiv
175

Begini cara saya berhasil menyelesaikan ini. Ini berfungsi pada premis bahwa menggunakan referensi waktu antara transisi aktivitas kemungkinan besar akan memberikan bukti yang memadai bahwa aplikasi telah "dilatar belakangi" atau tidak.

Pertama, saya telah menggunakan contoh android.app.Application (sebut saja MyApplication) yang memiliki Timer, TimerTask, konstanta untuk mewakili jumlah milidetik maksimum yang dapat diambil oleh transisi dari satu aktivitas ke aktivitas lainnya (saya pergi dengan nilai 2s), dan boolean untuk menunjukkan apakah aplikasi tersebut "di latar belakang":

public class MyApplication extends Application {

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

Aplikasi ini juga menyediakan dua metode untuk memulai dan menghentikan timer / tugas:

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;
}

Bagian terakhir dari solusi ini adalah untuk menambahkan panggilan ke masing-masing metode ini dari peristiwa onResume () dan onPause () dari semua aktivitas atau, lebih disukai, dalam Kegiatan dasar yang mewarisi semua Aktivitas konkret Anda:

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

Jadi dalam kasus ketika pengguna hanya menavigasi antara aktivitas aplikasi Anda, onPause () dari aktivitas yang berangkat memulai timer, tetapi hampir segera aktivitas yang dimasukkan membatalkan timer sebelum dapat mencapai waktu transisi maks. Dan begitu pula InBackground akan salah .

Di sisi lain ketika suatu Kegiatan datang ke latar depan dari Peluncur, bangun perangkat, mengakhiri panggilan telepon, dll., Kemungkinan besar tugas penghitung waktu dieksekusi sebelum acara ini, dan dengan demikian InBackground disetel ke true .

d60402
sumber
4
Hai d60402, jawaban Anda sangat membantu .. terima kasih banyak atas balasan ini ... pemberitahuan kecil .. MyApplication harus menyebutkan dalam tag aplikasi file Manifest seperti android: name = "MyApplication", jika tidak aplikasi crash ... hanya untuk membantu seseorang seperti saya
praveenb
2
tanda programmer hebat, solusi sederhana untuk salah satu masalah paling rumit yang pernah saya temui.
Aashish Bhatnagar
2
Solusi yang luar biasa! Terima kasih. Jika ada orang yang mengalami kesalahan "ClassCastException" maka Anda mungkin telah melewatkan menambahkannya di tag aplikasi di dalam Manifest.xml <application android: name = "your.package.MyApplication"
Wahib Ul Haq
27
Ini adalah implementasi yang bagus dan sederhana. Namun saya percaya ini harus diimplementasikan di onStart / onStop daripada onPause / onResume. OnPause akan dipanggil bahkan jika saya memulai dialog yang sebagian mencakup aktivitas. Dan menutup dialog sebenarnya akan memanggil Resume membuatnya tampak seolah-olah aplikasi baru saja datang ke latar depan
Shubhayu
7
Saya berharap dapat menggunakan variasi dari solusi ini. Poin tentang dialog yang diidentifikasi di atas adalah masalah bagi saya, jadi saya mencoba saran @ Shubhayu (onStart / onStop). Namun tidak membantu karena ketika pergi A-> B, Aktivitas B's onStart () dipanggil sebelum Activity A's onStop ().
Trevor
150

Sunting: komponen arsitektur baru membawa sesuatu yang menjanjikan: ProcessLifecycleOwner , lihat jawaban @ vokilam


Solusi aktual menurut Google I / O talk :

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
    }
  }

}

Iya. Saya tahu sulit untuk percaya solusi sederhana ini bekerja karena kami memiliki begitu banyak solusi aneh di sini.

Tapi ada harapan.

Fred Porciúncula
sumber
3
Ini bekerja dengan sempurna! Saya sudah mencoba banyak solusi aneh yang memiliki banyak kekurangan ... terima kasih banyak! Saya sudah mencari ini untuk sementara waktu.
Eggakin Baconwalker
7
Ini bekerja untuk banyak aktivitas, tetapi untuk satu - onrotate akan menunjukkan tentang semua kegiatan hilang atau di latar belakang
ikan mati
2
@ Syri Anda benar, tapi itu bagian dari solusi ini jadi perlu khawatir. Jika firebase bergantung pada ini, saya pikir aplikasi biasa-biasa saja saya juga bisa :) Jawaban bagus BTW.
ElliotM
3
@deadfish Periksa tautan ke I / O yang disediakan di bagian atas jawaban. Anda dapat memeriksa kesenjangan waktu antara penghentian aktivitas dan mulai menentukan apakah Anda benar-benar pergi ke latar belakang atau tidak. Ini adalah solusi yang brilian, sebenarnya.
Alex Berdnikov
2
Apakah ada solusi Java? Ini kotlin.
Giacomo Bartoli
116

ProcessLifecycleOwner tampaknya menjadi solusi yang menjanjikan juga.

ProcessLifecycleOwner akan mengirimkan ON_START, ON_RESUMEperistiwa, sebagai aktivitas pertama bergerak melalui peristiwa ini. ON_PAUSE,, ON_STOPacara akan dikirim dengan penundaan setelah aktivitas terakhir melewati mereka. Penundaan ini cukup lama untuk menjamin bahwa ProcessLifecycleOwnertidak akan mengirim peristiwa apa pun jika aktivitas dihancurkan dan diciptakan kembali karena perubahan konfigurasi.

Implementasi bisa sesederhana

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())

Menurut kode sumber, nilai penundaan saat ini adalah 700ms.

Juga menggunakan fitur ini memerlukan dependencies:

implementation "androidx.lifecycle:lifecycle-extensions:$lifecycleVersion"
vokilam
sumber
10
Perhatikan bahwa Anda perlu menambahkan dependensi siklus hidup implementation "android.arch.lifecycle:extensions:1.0.0"dan annotationProcessor "android.arch.lifecycle:compiler:1.0.0"dari repositori Google (mis. google())
Sir Codesalot
1
Ini bekerja dengan baik untuk saya, terima kasih. Saya harus menggunakan api 'android.arch.lifecycle: extensions: 1.1.0' alih-alih implementasi karena kesalahan yang menyatakan ketergantungan Android memiliki versi berbeda untuk kompilasi dan runtime classpath.
FSUWX2011
Ini adalah solusi hebat karena bekerja dalam modul tanpa memerlukan referensi Aktivitas!
Maks
Ini tidak berfungsi saat aplikasi macet. Apakah ada solusi untuk mendapatkan aplikasi yang mengalami crash juga melalui solusi ini
tejraj
Solusi bagus Menyelamatkan hariku.
Sunny
69

Berdasarkan jawaban Martín Marconcinis (terima kasih!) Saya akhirnya menemukan solusi yang andal (dan sangat sederhana).

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;
        }
    }
}

Kemudian tambahkan ini ke onCreate () dari kelas Aplikasi Anda

public class MyApp extends android.app.Application {

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

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

    }

}
rickul
sumber
Dapatkah Anda menunjukkan bagaimana Anda menggunakan ini di suatu aplikasi, apakah saya menyebutnya dari kelas Aplikasi atau di tempat lain?
JPM
ini sempurna terima kasih !! bekerja sangat baik dalam pengujian sejauh ini
aherrick
Contoh ini jika tidak lengkap. Apa itu registerActivityLifecycleCallbacks?
Noman
ini adalah metode di kelas android.app.Application
rickul
1
+1 dilakukan dengan baik untuk naik ke atas, karena itu sempurna, tidak mencari jawaban lain, ini didasarkan pada jawaban @reno tetapi dengan contoh nyata
Stoycho Andreev
63

Kami menggunakan metode ini. Kelihatannya terlalu sederhana untuk bekerja, tetapi telah teruji dengan baik di aplikasi kami dan ternyata bekerja dengan sangat baik dalam semua kasus, termasuk pergi ke layar beranda dengan tombol "home", dengan tombol "kembali", atau setelah kunci layar. Cobalah.

Gagasannya adalah, ketika di latar depan, Android selalu memulai aktivitas baru sebelum menghentikan yang sebelumnya. Itu tidak dijamin, tapi begitulah cara kerjanya. BTW, Flurry tampaknya menggunakan logika yang sama (hanya tebakan, saya tidak memeriksa itu, tapi itu mengait pada acara yang sama).

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
        }
    }

}

Sunting: sesuai komentar, kami juga pindah ke onStart () di versi kode yang lebih baru. Juga, saya menambahkan panggilan super, yang hilang dari posting awal saya, karena ini lebih merupakan konsep daripada kode yang berfungsi.

Nick Frolov
sumber
2
Ini adalah jawaban yang paling dapat diandalkan, meskipun saya menggunakan onStart bukan onResume.
Greg Ennis
Anda harus menambahkan panggilan ke super.onResume () dan super.onStop () pada metode yang diganti. Kalau tidak, android.app.SuperNotCalledException dilempar.
Jan Laussmann
1
bagi saya itu tidak berfungsi ... atau setidaknya itu memecat acara ketika Anda memutar perangkat juga (yang semacam imho positif palsu).
Noya
Solusi yang sangat sederhana dan efektif! Tetapi saya tidak yakin ini berfungsi dengan aktivitas transparan sebagian yang membuat beberapa bagian dari aktivitas sebelumnya terlihat. Dari dokumen onStop is called when the activity is no longer visible to the user,.
Nicolas Buquet
3
apa yang terjadi jika pengguna mengubah orientasi pada aktivitas pertama? Ini akan melaporkan bahwa aplikasi pergi ke latar belakang yang tidak benar. Bagaimana Anda menangani skenario ini?
Nimrod Dayan
54

Jika aplikasi Anda terdiri dari beberapa aktivitas dan / atau aktivitas bertumpuk seperti widget tab bar, maka mengesampingkan onPause () dan onResume () tidak akan berfungsi. Yaitu ketika memulai aktivitas baru, aktivitas saat ini akan dijeda sebelum yang baru dibuat. Hal yang sama berlaku ketika menyelesaikan (menggunakan tombol "kembali") suatu kegiatan.

Saya telah menemukan dua metode yang tampaknya berfungsi seperti yang diinginkan.

Yang pertama membutuhkan izin GET_TASKS dan terdiri dari metode sederhana yang memeriksa apakah aktivitas yang berjalan paling atas di perangkat milik aplikasi, dengan membandingkan nama paket:

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;
}

Metode ini ditemukan dalam kerangka Droid-Fu (sekarang disebut Ignition).

Metode kedua yang saya terapkan sendiri tidak memerlukan izin GET_TASKS, yang bagus. Sebaliknya itu sedikit lebih rumit untuk diterapkan.

Di kelas MainApplication Anda, Anda memiliki variabel yang melacak jumlah aktivitas yang berjalan di aplikasi Anda. Di onResume () untuk setiap aktivitas Anda meningkatkan variabel dan di onPause () Anda menguranginya.

Ketika jumlah aktivitas yang berjalan mencapai 0, aplikasi diletakkan di latar belakang JIKA kondisi berikut ini benar:

  • Aktivitas yang dijeda tidak selesai (tombol "kembali" digunakan). Ini dapat dilakukan dengan menggunakan metode activity.isFinishing ()
  • Aktivitas baru (nama paket yang sama) tidak dimulai. Anda bisa mengganti metode startActivity () untuk menetapkan variabel yang menunjukkan ini dan kemudian mengaturnya kembali di onPostResume (), yang merupakan metode terakhir yang akan dijalankan ketika suatu aktivitas dibuat / dilanjutkan.

Ketika Anda dapat mendeteksi bahwa aplikasi telah mengundurkan diri ke latar belakang, mudah dideteksi ketika dibawa kembali ke latar depan juga.

Emil
sumber
18
Google mungkin akan menolak aplikasi yang menggunakan ActivityManager.getRunningTasks (). Dokumentasi menyatakan bahwa itu adalah tujuan musuh saja. developer.android.com/reference/android/app/…
Sky Kelsey
1
Saya menemukan saya harus menggunakan kombinasi dari pendekatan ini. onUserLeaveHint () dipanggil saat meluncurkan aktivitas di 14. `@Override public void onUserLeaveHint () {inBackground = isApplicationBroughtToBackground (); } `
daftar perahu
7
Pengguna tidak akan terlalu senang menggunakan izin android.permission.GET_TASKS yang kuat.
MSquare
6
getRunningTasks sudah tidak digunakan lagi di API level 21.
Noya
33

Buat kelas yang diperluas Application. Kemudian di dalamnya kita dapat menggunakan metode override-nya,onTrimMemory() ,.

Untuk mendeteksi jika aplikasi pergi ke latar belakang, kami akan menggunakan:

 @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
        }
    }
Harpreet
sumber
1
Untuk FragmentActivityAnda juga mungkin ingin menambahkan level == ComponentCallbacks2.TRIM_MEMORY_COMPLETEjuga.
Srujan Simha
2
Terima kasih banyak untuk menunjukkan metode ini, saya perlu menunjukkan Pin Dialog setiap kali pengguna melanjutkan aktivitas untuk latar belakang, menggunakan metode ini untuk menulis nilai pref dan memeriksa nilai ini pada baseActivity.
Sam
18

Pertimbangkan untuk menggunakan onUserLeaveHint. Ini hanya akan dipanggil ketika aplikasi Anda masuk ke latar belakang. onPause akan memiliki kasus sudut untuk ditangani, karena dapat dipanggil karena alasan lain; misalnya jika pengguna membuka aktivitas lain di aplikasi Anda seperti halaman pengaturan Anda, metode onPause aktivitas utama Anda akan dipanggil meskipun mereka masih di aplikasi Anda; melacak apa yang terjadi akan mengarah ke bug ketika Anda bisa menggunakan callback onUserLeaveHint yang melakukan apa yang Anda minta.

Ketika pada UserLeaveHint dipanggil, Anda dapat mengatur flag boolean inBackground menjadi true. Saat onResume dipanggil, anggap saja Anda kembali ke latar depan jika flag inBackground diatur. Ini karena onResume juga akan dipanggil pada aktivitas utama Anda jika pengguna hanya di menu pengaturan Anda dan tidak pernah meninggalkan aplikasi.

Ingat bahwa jika pengguna menekan tombol beranda saat di layar pengaturan Anda, onUserLeaveHint akan dipanggil dalam aktivitas pengaturan Anda, dan ketika mereka kembali onResume akan dipanggil dalam aktivitas pengaturan Anda. Jika Anda hanya memiliki kode deteksi ini di aktivitas utama Anda, Anda akan kehilangan use case ini. Untuk memiliki kode ini di semua aktivitas Anda tanpa kode duplikat, miliki kelas aktivitas abstrak yang memperluas Aktivitas, dan masukkan kode umum Anda di dalamnya. Maka setiap aktivitas yang Anda miliki dapat memperluas aktivitas abstrak ini.

Sebagai contoh:

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 {
    ...
}
OldSchool4664
sumber
19
onUserLeaveHint juga dipanggil saat menavigasi ke aktivitas lain
Jonas Stawski
3
onUserLeaveHint tidak dipanggil ketika mis. panggilan telepon masuk dan aktivitas panggilan menjadi aktif, jadi ini memiliki kasus tepi juga - mungkin ada kasus lain juga, karena Anda dapat menambahkan bendera dengan maksud untuk menekan panggilan onUserLeaveHint. developer.android.com/reference/android/content/…
Groxx
1
Juga, onResume tidak berfungsi dengan baik. Saya mencobanya dan kadang-kadang onResume dipanggil saat telepon terkunci. Jika Anda melihat definisi onResume dalam dokumentasi, Anda akan menemukan: Ingatlah bahwa onResume bukanlah indikator terbaik bahwa aktivitas Anda terlihat oleh pengguna; jendela sistem seperti pengaman tombol mungkin ada di depan. Gunakan onWindowFocusChanged (boolean) untuk mengetahui dengan pasti bahwa aktivitas Anda dapat dilihat oleh pengguna (misalnya, untuk melanjutkan permainan). developer.android.com/reference/android/app/…
J-Rou
solusi ini tidak membantu untuk menentukan latar depan / latar belakang jika ada beberapa kegiatan. Mohon
Raj Trivedi
14

ActivityLifecycleCallbacks mungkin menarik, tetapi tidak didokumentasikan dengan baik.

Padahal, jika Anda memanggil registerActivityLifecycleCallbacks (), Anda harus bisa mendapatkan panggilan balik untuk saat Aktivitas dibuat, dihancurkan, dll. Anda dapat memanggil getComponentName () untuk Aktivitas.

Reno
sumber
11
Karena api level 14 = \
imort
Sepertinya ini bersih dan bekerja untuk saya. Terima kasih
duanbo1983
Bagaimana ini berbeda dari jawaban yang diterima, keduanya mengandalkan siklus hidup aktivitas yang sama, kan?
Saitama
13

The android.arch.lifecycle menyediakan paket kelas dan interface yang memungkinkan Anda membangun komponen siklus hidup-sadar

Aplikasi Anda harus mengimplementasikan antarmuka LifecycleObserver:

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");
    }
}

Untuk melakukan itu, Anda perlu menambahkan ketergantungan ini ke file build.gradle Anda:

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

Seperti yang direkomendasikan oleh Google, Anda harus meminimalkan kode yang dieksekusi dalam metode aktivitas siklus hidup:

Pola umum adalah untuk mengimplementasikan aksi komponen dependen dalam metode siklus hidup dari aktivitas dan fragmen. Namun, pola ini mengarah pada organisasi kode yang buruk dan menjamurnya kesalahan. Dengan menggunakan komponen yang memperhatikan siklus hidup, Anda dapat memindahkan kode komponen tergantung dari metode siklus hidup dan ke dalam komponen itu sendiri.

Anda dapat membaca lebih lanjut di sini: https://developer.android.com/topic/libraries/architecture/lifecycle

matdev
sumber
dan tambahkan ini ke manifes seperti: <application android: name = ". AnotherApp">
Dan Alboteanu
9

Di Aplikasi Anda, tambahkan panggilan balik dan periksa aktivitas root dengan cara seperti ini:

@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();
            }
        }
    });
}
Cynichniy Bandera
sumber
Saya akan mempertimbangkan menggunakan cara implementasi ini. Transisi dari satu aktivitas ke aktivitas lain hanya membutuhkan beberapa milidetik. Berdasarkan waktu ketika aktivitas terakhir menghilang dapat dianggap login kembali pengguna dengan strategi tertentu.
drindt
6

Saya telah membuat proyek di Github app-foreground-background-listen

Buat BaseActivity untuk semua Kegiatan di aplikasi Anda.

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
    }
}

Sekarang gunakan BaseActivity ini sebagai kelas super dari semua Aktivitas Anda seperti MainActivity meluas BaseActivity dan onAppStart akan dipanggil ketika Anda memulai aplikasi Anda dan onAppPause () akan dipanggil ketika aplikasi pergi latar belakang dari layar apa pun.

kiran boghra
sumber
@kiran boghra: Apakah ada kesalahan positif dalam solusi Anda?
Harish Vishwakarma
Jawab dengan sempurna fungsi onStart () dan onStop () dapat digunakan dalam kasus ini. yang memberitahu Anda tentang aplikasi Anda
Pir Fahim Shah
6

Ini cukup mudah ProcessLifecycleOwner

Tambahkan dependensi ini

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

Di 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")
    }
}

Kemudian dalam aktivitas dasar Anda:

override fun onCreate() {
        super.onCreate()

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

Lihat artikel saya tentang topik ini: https://medium.com/@egek92/how-to-actual-detect-foreground-background-changes-in-your-android-application-without-wanting-9719cc822c48

Ege Kuzubasioglu
sumber
5

Anda dapat menggunakan ProcessLifecycleOwner melampirkan pengamat siklus hidup untuk itu.

  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");
    }
}

maka pada onCreate()kelas Aplikasi Anda, Anda menyebutnya:

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

dengan ini Anda akan dapat menangkap peristiwa ON_PAUSEdan ON_STOPaplikasi Anda yang terjadi ketika berjalan di latar belakang.

Alécio Carvalho
sumber
4

Tidak ada metode siklus langsung untuk memberi tahu Anda ketika seluruh Aplikasi berjalan latar / latar depan.

Saya telah melakukan ini dengan cara sederhana. Ikuti instruksi di bawah ini untuk mendeteksi fase latar belakang / latar depan aplikasi.

Dengan sedikit penyelesaian, itu mungkin. Di sini, ActivityLifecycleCallbacks datang untuk menyelamatkan. Biarkan saya berjalan selangkah demi selangkah.

  1. Pertama, buat kelas yang memperluas aplikasi android.app.plikasi dan mengimplementasikan antarmuka ActivityLifecycleCallbacks . Di Application.onCreate (), daftarkan callback.

    public class App extends Application implements 
        Application.ActivityLifecycleCallbacks {
    
        @Override
        public void onCreate() {
            super.onCreate();
            registerActivityLifecycleCallbacks(this);
        }
    }
  2. Daftarkan kelas "Aplikasi" di Manifest seperti di bawah ini <application android:name=".App",.

  3. Akan ada setidaknya satu Kegiatan di negara mulai ketika aplikasi di latar depan dan tidak akan ada Kegiatan di negara mulai ketika aplikasi berada di latar belakang.

    Deklarasikan 2 variabel seperti di bawah ini di kelas "Aplikasi".

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

    activityReferencesakan menyimpan hitungan jumlah aktivitas dalam kondisi awal . isActivityChangingConfigurationsadalah tanda untuk menunjukkan jika Aktivitas saat ini mengalami perubahan konfigurasi seperti sakelar orientasi.

  4. Dengan menggunakan kode berikut, Anda dapat mendeteksi jika Aplikasi datang di latar depan.

    @Override
    public void onActivityStarted(Activity activity) {
        if (++activityReferences == 1 && !isActivityChangingConfigurations) {
            // App enters foreground
        }
    }
  5. Ini adalah cara mendeteksi jika Aplikasi berjalan latar.

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

Bagaimana itu bekerja:

Ini adalah sedikit trik yang dilakukan dengan cara metode Siklus Hidup disebut secara berurutan. Biarkan saya menelaah sebuah skenario.

Asumsikan bahwa pengguna meluncurkan Aplikasi dan Kegiatan Peluncur A diluncurkan. Panggilan Siklus Hidup akan menjadi,

A.onCreate ()

A.onStart () (++ activityReferences == 1) (Aplikasi masuk Foreground)

A.onResume ()

Sekarang Aktivitas A memulai Kegiatan B.

A.onPause ()

B.onCreate ()

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

B.onResume ()

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

Kemudian pengguna menavigasi kembali dari Kegiatan B,

B.onPause ()

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

A.onResume ()

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

B.onDestroy ()

Kemudian pengguna menekan tombol Home,

A.onPause ()

A.onStop () (--activityReferences == 0) (Aplikasi memasuki Latar Belakang)

Dalam hal ini, jika pengguna menekan tombol Beranda dari Aktivitas B dan bukan tombol Kembali, tetap saja akan sama dan aktivitas referensi akan 0 . Oleh karena itu, kita dapat mendeteksi sebagai Aplikasi memasuki Latar Belakang.

Jadi, apa perannya isActivityChangingConfigurations? Dalam skenario di atas, anggap Kegiatan B mengubah orientasi. Urutan panggilan balik akan menjadi,

B.onPause ()

B.onStop () (--activityReferences == 0) (Aplikasi memasuki Background ??)

B.onDestroy ()

B.onCreate ()

B.onStart () (++ activityReferences == 1) (Aplikasi masuk Foreground ??)

B.onResume ()

Itu sebabnya kami memiliki pemeriksaan tambahan isActivityChangingConfigurationsuntuk menghindari skenario ketika Aktivitas melalui Konfigurasi berubah.

Komal Nikhare
sumber
3

Saya menemukan metode yang baik untuk mendeteksi aplikasi apakah masuk foreground atau latar belakang. Ini kode saya . Semoga ini bisa membantu Anda.

/**
 * 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();

}

Folyd
sumber
3

Kamu bisa menggunakan:

batal onRestart ()

Untuk membedakan antara mulai baru dan mulai ulang.

masukkan deskripsi gambar di sini

AYBABTU
sumber
3

Sunting 2: Apa yang saya tulis di bawah ini sebenarnya tidak akan berfungsi. Google telah menolak aplikasi yang menyertakan panggilan ke ActivityManager.getRunningTasks (). Dari dokumentasi , jelas bahwa API ini hanya untuk tujuan debugging dan pengembangan. Saya akan memperbarui posting ini segera setelah saya punya waktu untuk memperbarui proyek GitHub di bawah ini dengan skema baru yang menggunakan timer dan hampir sama baiknya.

Sunting 1: Saya telah menulis posting blog dan membuat repositori GitHub sederhana untuk membuatnya sangat mudah.

Jawaban yang diterima dan diberi peringkat teratas bukanlah pendekatan yang terbaik. Implementasi jawaban teratas dari isApplicationBroughtToBackground () tidak menangani situasi di mana Aktivitas utama Aplikasi menghasilkan ke Aktivitas yang didefinisikan dalam Aplikasi yang sama, tetapi memiliki paket Java yang berbeda. Saya datang dengan cara untuk melakukan ini yang akan berhasil dalam kasus itu.

Panggil ini di onPause (), dan itu akan memberi tahu Anda jika aplikasi Anda masuk ke latar belakang karena aplikasi lain telah dimulai, atau pengguna telah menekan tombol beranda.

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;
}
Sky Kelsey
sumber
FYI, memanggil ini di onStart () sebagai gantinya akan menghindarinya dipanggil saat dialog sederhana dilontarkan, misalnya, alarm berbunyi.
Sky Kelsey
2

Jawaban yang benar di sini

Buat kelas dengan nama MyApp seperti di bawah ini:

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() {

    }
}

Lalu, di mana pun Anda inginkan (aktivitas pertama yang lebih baik diluncurkan di aplikasi), tambahkan kode di bawah ini:

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

Selesai! Sekarang ketika aplikasi di latar belakang, kita mendapatkan log status : we are out dan ketika kita masuk aplikasi, kita mendapatkan logstatus : we are out

erfan
sumber
1

Solusi saya terinspirasi oleh jawaban @ d60402 dan juga bergantung pada jendela waktu, tetapi tidak menggunakan 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();
  }
}

di mana SingletonApplicationmerupakan perluasan Applicationkelas:

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

Saya menggunakan ini dengan Google Analytics EasyTracker, dan itu berhasil. Bisa diperluas untuk melakukan apa yang Anda cari menggunakan integer sederhana.

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) {

            }
        });
    }
}
Bill Mote
sumber
1

Saya tahu ini sedikit terlambat tetapi saya pikir semua jawaban ini memiliki beberapa masalah sementara saya melakukannya seperti di bawah ini dan itu bekerja dengan sempurna.

buat callback daur hidup aktivitas seperti ini:

 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) {

    }
}

dan cukup daftarkan di kelas aplikasi Anda seperti di bawah ini:

public class MyApp extends Application {

@Override
public void onCreate() {
    super.onCreate();
    registerActivityLifecycleCallbacks(new ActivityLifeCycle());
}
Amir Ziarati
sumber
Ini dipanggil sepanjang waktu pada setiap aktivitas. Bagaimana saya bisa menggunakan ini jika misalnya saya ingin mendeteksi status online pengguna
Maksim Kniazev
Itulah yang diinginkan pertanyaan itu. itu hanya dipanggil ketika Anda pergi ke layar awal dan kembali ke aktivitas apa pun.
Amir Ziarati
jika Anda maksud konektivitas internet saya pikir lebih baik untuk memeriksa ketika Anda membutuhkannya. jika Anda perlu memanggil api, periksa koneksi internet sebelum menelepon.
Amir Ziarati
1

Ini tampaknya menjadi salah satu pertanyaan paling rumit di Android karena (saat tulisan ini dibuat) Android tidak memiliki padanan applicationDidEnterBackground()atau applicationWillEnterForeground()panggilan balik iOS . Saya menggunakan Perpustakaan AppState yang disatukan oleh @jenzz .

[AppState adalah] perpustakaan Android sederhana dan reaktif berdasarkan RxJava yang memonitor perubahan status aplikasi. Ini memberi tahu pelanggan setiap kali aplikasi masuk ke latar belakang dan kembali ke latar depan.

Ternyata ini adalah persis apa yang saya butuhkan, terutama karena aplikasi saya memiliki beberapa kegiatan sehingga hanya memeriksa onStart()atau onStop()pada suatu kegiatan tidak akan memotongnya.

Pertama saya menambahkan dependensi ini ke gradle:

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

Maka itu masalah sederhana menambahkan baris ini ke tempat yang sesuai dalam kode Anda:

//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;
        }
    }
});

Bergantung pada bagaimana Anda berlangganan yang diamati, Anda mungkin harus berhenti berlangganan untuk menghindari kebocoran memori. Lagi info lebih lanjut di halaman github .

deniz
sumber
1

Ini adalah versi jawaban @ d60402 yang dimodifikasi: https://stackoverflow.com/a/15573121/4747587

Lakukan semua yang disebutkan di sana. Tetapi alih-alih memiliki Base Activitydan menjadikannya sebagai orangtua untuk setiap aktivitas dan yang menimpa onResume()danonPause , lakukan di bawah ini:

Di kelas aplikasi Anda, tambahkan baris:

registerActivityLifecycleCallbacks (Application.ActivityLifecycleCallbacks callback);

Ini callbackmemiliki semua metode siklus aktivitas dan sekarang Anda dapat mengganti onActivityResumed()danonActivityPaused() .

Lihatlah Gist ini: https://gist.github.com/thsaravana/1fa576b6af9fc8fff20acfb2ac79fa1b

Henry
sumber
1

Anda dapat mencapai ini dengan mudah dengan bantuan ActivityLifecycleCallbacksdan ComponentCallbacks2sesuatu seperti di bawah ini.

Buat kelas yang AppLifeCycleHandlermengimplementasikan antarmuka di atas.

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

Di kelas Anda yang meluas Applicationimplement AppLifeCycleCallbackuntuk mendapatkan callback ketika aplikasi beralih antara latar depan dan latar belakang. Sesuatu seperti di bawah ini.

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");
    }
}

Semoga ini membantu.

EDIT Sebagai alternatif, Anda sekarang dapat menggunakan komponen arsitektur sadar siklus hidup.

Naveen TP
sumber
1

Karena saya tidak menemukan pendekatan apa pun, yang juga menangani rotasi tanpa memeriksa perangko waktu, saya pikir saya juga membagikan bagaimana kami sekarang melakukannya di aplikasi kami. Satu-satunya tambahan untuk jawaban ini https://stackoverflow.com/a/42679191/5119746 adalah, bahwa kami juga mempertimbangkan orientasi tersebut.

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

Kemudian, untuk callback kami memiliki resume terlebih dahulu:

   // ActivityLifecycleCallbacks

   override fun onActivityResumed(activity: Activity?) {

      mResumed++

      if (mAppIsInBackground) {

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

         mAppIsInBackground = false
      }
      mOrientationWasChanged = false
    }

Dan padaAktivitas Berhenti:

   override fun onActivityStopped(activity: Activity?) {

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

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

        mAppIsInBackground = true
    }

Dan kemudian, inilah tambahan: Memeriksa perubahan orientasi:

   override fun onConfigurationChanged(newConfig: Configuration) {

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

Itu dia. Semoga ini bisa membantu seseorang :)

Julian Horst
sumber
1

Kami dapat memperluas solusi ini menggunakan 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
    }
}

Sekarang kita bisa berlangganan LiveData ini dan menangkap acara yang diperlukan. Sebagai contoh:

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

Jawaban ini sepertinya tidak benar. Metode-metode ini juga disebut ketika aktivitas lain dimulai dan berakhir. Yang bisa Anda lakukan adalah menjaga bendera global (ya, global buruk :) dan atur ini menjadi true setiap kali Anda memulai aktivitas baru. Setel ke false di onCreate setiap aktivitas. Lalu, di onPause Anda memeriksa tanda ini. Jika itu salah, aplikasi Anda masuk ke latar belakang, atau terbunuh.

Joris Weimar
sumber
Saya tidak berbicara tentang database ... apa maksud Anda?
Joris Weimar
Saya mendukung jawaban Anda. meskipun kita dapat menyimpan nilai bendera itu di basis data saat jeda panggilan itu bukan solusi yang baik ..
Sandeep P