Перевірка, чи програма Android працює у фоновому режимі


329

Я маю на увазі, я не маю на увазі, що жодна з дій програми зараз не бачна користувачеві?



7
Я тут розгублений .. чому андроїд не може забезпечити просте переосмислення класу додатків для цього? Чи занадто важко це знати на рівні платформи? @Override захищений недійсним наApplicationSentToBackground () {}
Чак D

2
@ChuckD - це мало б сенс, саме тому, схоже, хотілося б, щоб Android SDK уникав цього робити. : /
Марк


1
iOS має це в лопатах, не впевнений, чому Google робить це так важко. Це така очевидна потреба.
Джеррі Дестремпс

Відповіді:


388

Є кілька способів виявити, чи працює ваша програма у фоновому режимі, але лише один із них є повністю надійним:

  1. Правильне рішення (кредити йдуть до Дену , CommonsWare і NeTeInStEiN )
    видимості Трек вашої програми самостійно , використовуючи Activity.onPause, Activity.onResumeметоди. Зберігайте статус "видимості" в якомусь іншому класі. Хороший вибір - це ваша власна реалізація Applicationабо Service(також є кілька варіантів цього рішення, якщо ви хочете перевірити видимість діяльності від служби).
     
    Приклад
    Реалізація користувацького Applicationкласу (зверніть увагу на isActivityVisible()статичний метод):

    public class MyApplication extends Application {
    
      public static boolean isActivityVisible() {
        return activityVisible;
      }  
    
      public static void activityResumed() {
        activityVisible = true;
      }
    
      public static void activityPaused() {
        activityVisible = false;
      }
    
      private static boolean activityVisible;
    }

    Зареєструйте свій клас заявок у AndroidManifest.xml:

    <application
        android:name="your.app.package.MyApplication"
        android:icon="@drawable/icon"
        android:label="@string/app_name" >

    Додайте onPauseі onResumeдо кожного Activityв проекті (ви можете створити спільного предка для своєї діяльності, якщо хочете, але якщо ваша діяльність вже розширена з MapActivity/ ListActivityі т.д., вам все одно потрібно написати вручну):

    @Override
    protected void onResume() {
      super.onResume();
      MyApplication.activityResumed();
    }
    
    @Override
    protected void onPause() {
      super.onPause();
      MyApplication.activityPaused();
    }

     
    Оновлення
    ActivityLifecycleCallbacks додано на рівні API 14 (Android 4.0). Ви можете використовувати їх, щоб відстежувати, чи активність вашої програми наразі бачна користувачеві. Перевірте відповідь кукурудзяні стебла нижче для деталей.

  2. Неправильний, з якого
    я пропонував таке рішення:

    Ви можете виявити на даний момент передній план / фонову програму, за допомогою ActivityManager.getRunningAppProcesses()якої повертається список RunningAppProcessInfoзаписів. Щоб визначити, чи є ваша програма на передньому плані, RunningAppProcessInfo.importanceполе перевірки рівності до RunningAppProcessInfo.IMPORTANCE_FOREGROUNDчасу RunningAppProcessInfo.processNameдорівнює назві пакету додатків.

    Крім того, якщо ви зателефонуєте ActivityManager.getRunningAppProcesses()з інтерфейсу інтерфейсу програми, він поверне значення IMPORTANCE_FOREGROUNDдля вашої задачі, незалежно від того, перебуває він на передньому плані чи ні. Зателефонуйте у фонову нитку (наприклад, через AsyncTask), і вона поверне правильні результати.

    Хоча це рішення може працювати (і воно справді працює більшу частину часу), я настійно рекомендую утриматися від його використання. І ось чому. Як писала Діанна Хакборн :

    Ці API не існують для додатків, щоб базувати свій потік інтерфейсу, але для того, щоб показати користувачеві запущені програми чи менеджер завдань тощо.

    Так, є список, який зберігається в пам'яті для цих речей. Однак він вимикається в іншому процесі, керованому потоками, що працюють окремо від вашого, і не те, на що ви можете розраховувати (а) побачити вчасно прийняти правильне рішення або (б) мати послідовну картину до моменту повернення. Плюс рішення про те, що "наступна" діяльність потрібно перейти, завжди робиться в тому місці, де має відбутися перемикання, і це не до того моменту, коли стан активності коротко заблокований, щоб зробити перемикач. насправді точно знаю, що буде далі.

    І впровадження та глобальна поведінка тут не гарантують, що вони залишаться однаковими і надалі.

    Я хотів би, щоб я прочитав це перед тим, як розмістити відповідь на ПЗ, але, сподіваюся, не пізно визнати свою помилку.

  3. Ще одне неправильне рішення бібліотека
    Droid-Fu, згадана в одній з відповідей, використовує ActivityManager.getRunningTasksдля свого isApplicationBroughtToBackgroundметоду. Дивіться коментар Діанні вище і також не використовуйте цей метод.


4
Щоб дізнатися, чи натиснули ви кнопку додому чи якийсь інший додаток привернув увагу: 1) реалізуйте хороше рішення . 2) На OnStopпрохання до isActivityVisible.
Brais Gabin

28
На жаль, ваше «правильне» рішення для мене не працює. Подумайте, що ви переходите через діяльність в додатку. Потім трапляється так, що ваш прапор 'inForeground' має такий вигляд: True, False (між On-Pause 1-ї активності та onResume 2-ї активності), потім True знову і т. Д. Тоді вам знадобиться якийсь гістерезис.
Раду

14
Це рішення не працює, якщо ви не можете безпосередньо контролювати всі дії. Наприклад, якщо у вас є активність від сторонніх sdk або навіть запуск наміру ACTION_VIEW.
користувач123321

66
Android - така вигадлива краха. Ніхто не думав, що хтось може захотіти зберегти дані про рівень додатків? Дайте мені перерву

8
Схоже, справжня відповідь на це питання "Ви не можете перевірити це належним чином". Так зване «правильне» рішення в кращому випадку є вирішенням, так це і використання ActivityLifecycleCallbacks. Ви все ще повинні розглянути можливість переключення між видами діяльності, які були б зареєстровані як "не на передньому плані". Мені здається, що ви не можете перевірити таку просту річ, як це ...
serine

263

НЕ ВИКОРИСТОВУЙТЕ ЦЕ ВІДПОВІДЬ

відповідь user1269737 - це правильний спосіб (затверджений Google / Android) . Іди, прочитай їх відповідь і дай +1.

Я залишу тут свою оригінальну відповідь заради нащадків. Це було найкраще в 2012 році, але тепер Android має належну підтримку для цього.

Оригінальна відповідь

Ключ використовується ActivityLifecycleCallbacks(зауважте, що для цього потрібен Android API рівня 14 (Android 4.0)). Просто перевірте, чи кількість зупинених дій дорівнює кількості розпочатих заходів. Якщо вони рівні, ваш додаток буде фоновим. Якщо буде розпочато більше заходів, ваша програма все ще буде видно. Якщо є більше відновлених, ніж призупинених дій, ваша програма не лише видно, але й на першому плані. Тоді є три основні стани, за якими ваша діяльність може бути, а потім: видима і на передньому плані, видима, але не на передньому плані, і не видима і не на передньому плані (тобто на задньому плані).

Дуже приємна річ у цьому методі полягає в тому, що він не має асинхронних проблем getRunningTasks(), але вам також не потрібно змінювати кожен Activityу вашій програмі, щоб встановити / вимкнути щось у onResumed()/ onPaused(). Це лише кілька рядків коду, які містяться в собі, і він працює у всій вашій програмі. Плюс, не потрібні також і фанкі.

MyLifecycleHandler.java:

public class MyLifecycleHandler implements ActivityLifecycleCallbacks {
    // I use four separate variables here. You can, of course, just use two and
    // increment/decrement them instead of using four and incrementing them all.
    private int resumed;
    private int paused;
    private int started;
    private int stopped;

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

    @Override
    public void onActivityDestroyed(Activity activity) {
    }

    @Override
    public void onActivityResumed(Activity activity) {
        ++resumed;
    }

    @Override
    public void onActivityPaused(Activity activity) {
        ++paused;
        android.util.Log.w("test", "application is in foreground: " + (resumed > paused));
    }

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

    @Override
    public void onActivityStarted(Activity activity) {
        ++started;
    }

    @Override
    public void onActivityStopped(Activity activity) {
        ++stopped;
        android.util.Log.w("test", "application is visible: " + (started > stopped));
    }

    // If you want a static function you can use to check if your application is
    // foreground/background, you can use the following:
    /*
    // Replace the four variables above with these four
    private static int resumed;
    private static int paused;
    private static int started;
    private static int stopped;

    // And these two public static functions
    public static boolean isApplicationVisible() {
        return started > stopped;
    }

    public static boolean isApplicationInForeground() {
        return resumed > paused;
    }
    */
}

MyApplication.java:

// Don't forget to add it to your manifest by doing
// <application android:name="your.package.MyApplication" ...
public class MyApplication extends Application {
    @Override
    public void onCreate() {
        // Simply add the handler, and that's it! No need to add any code
        // to every activity. Everything is contained in MyLifecycleHandler
        // with just a few lines of code. Now *that's* nice.
        registerActivityLifecycleCallbacks(new MyLifecycleHandler());
    }
}

@Mewzer задав кілька хороших питань щодо цього методу, на які я хотів би відповісти у цій відповіді для всіх:

onStop()не викликається в ситуаціях з низькою пам'яттю; це тут проблема?

Ні. Документи для onStop()вимови:

Зауважте, що цей спосіб ніколи не може бути викликаний у ситуаціях з низькою пам’яттю, коли в системі недостатньо пам’яті, щоб підтримувати процес вашої діяльності після виклику методу onPause ().

Ключовим моментом тут є «тримати вашу діяльність в процес запущений ...» Якщо ця низька ситуація пам'яті коли - або досягнутий, ваш процес насправді убитий (не тільки вашу діяльність). Це означає, що цей спосіб перевірки наявності backgrounded-ness все ще дійсний, тому що: a) ви не можете перевірити, чи не працює фоновий процес, якщо ваш процес загинув, і b) якщо ваш процес запуститься знову (оскільки створена нова активність), член змінні (статичні чи ні) для MyLifecycleHandlerбуде скинуто до 0.

Чи працює це для зміни конфігурації?

За замовчуванням немає. Ви повинні чітко встановити configChanges=orientation|screensize( |що завгодно) у своєму файлі маніфесту та обробити зміни конфігурації, інакше ваша діяльність буде знищена та відтворена. Якщо ви не встановили це, методи вашої діяльності буде називатися в наступному порядку: onCreate -> onStart -> onResume -> (now rotate) -> onPause -> onStop -> onDestroy -> onCreate -> onStart -> onResume. Як бачите, перекриття немає (як правило, дві дії перекриваються дуже коротко при перемиканні між ними, саме так працює цей метод виявлення фонового режиму). Щоб обійти це, ви повинні налаштувати configChangesтак, щоб ваша діяльність не була знищена. На щастя, мені довелося встановитиconfigChangesвже у всіх моїх проектах, оскільки мені було небажано всю мою діяльність знищувати під час повороту / зміни розміру екрана, тому я ніколи не вважав це проблематичним. (дякую dpimka за те, що оновив мою пам’ять і виправив мене!)

Одна примітка:

Коли я сказав у цьому відповіді "фон", я мав на увазі, що "ваша програма більше не видно". Діяльність Android може бути помітна ще не на передньому плані (наприклад, якщо є прозора накладання повідомлень). Тому я оновив цю відповідь, щоб це відобразити.

Важливо знати, що в Android є дивний момент кінцівки при перемиканні діяльності, де нічого не виходить на перший план . З цієї причини, якщо ви перевірите, чи ваша програма стоїть на передньому плані при переключенні між видами діяльності (у тому самому додатку), вам скажуть, що ви не на передньому плані (навіть якщо ваш додаток все ще є активним додатком і його видно ).

Ви можете перевірити , якщо ваш додаток знаходиться на передньому плані у вашій Activity«S onPause()метод після super.onPause() . Пам’ятайте лише про дивний стан кінцівки, про який я щойно говорив.

Ви можете перевірити , якщо ваш додаток видиме (тобто , якщо це не в фоновому режимі) в вашому Activity«S onStop()методі після super.onStop() .


1
Це виглядає цікаво - але що відбувається в ситуаціях з низькою пам’яттю? Не гарантується, що onStop () буде викликаний. Чи можемо ми коли-небудь потрапити в ситуацію, коли onStop () не викликається і зупинений лічильник не збільшується - це означає, що фонова перевірка вже не є надійною? Або це ніколи не відбудеться?
Mewzer

1
Також, чи зміниться ця конфігурація ігнорування? Або додаток вважатиметься фоновим, якщо активність буде створено в результаті зміни конфігурації (наприклад, Зміна орієнтації) ?. Вибачте, за запитання, але я думаю, що ви щось вирішили, і мені цікаво дізнатися, чи спрацьовує це в цих кращих випадках.
Mewzer

1
@Mewzer: Я збирався відповісти як коментар, але для отримання цих відповідей знадобиться трохи набрати текст, тому перевірте через кілька хвилин, і я відредагую свою відповідь.
Cornstalks

1
@Mewzer: Ви зараз повинні знайти свої відповіді. Повідомте мене, чи є якісь інші запитання!
Cornstalks

2
@Mewzer: Я щойно додав замітку, яка може вас зацікавити. Зокрема, перевірте, чи немає фонових зображень onStop()після super.onStop(). Не перевіряйте наявність фонового режиму в onPause().
Cornstalks

186

GOOGLE SOLUTION - не хак, як попередні рішення. Використовуйте ProcessLifecycleOwner

Kotlin:

class ArchLifecycleApp : Application(), LifecycleObserver {

    override fun onCreate() {
        super.onCreate()
        ProcessLifecycleOwner.get().lifecycle.addObserver(this)
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    fun onAppBackgrounded() {
        //App in background
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    fun onAppForegrounded() {
        // App in foreground
    }

}


Java:

public class ArchLifecycleApp extends Application implements LifecycleObserver {

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

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    public void onAppBackgrounded() {
        //App in background
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    public void onAppForegrounded() {
        // App in foreground
    }
}

в app.gradle

dependencies {
    ...
    implementation "android.arch.lifecycle:extensions:1.1.0"

    //New Android X dependency is this - 
    implementation "androidx.lifecycle:lifecycle-extensions:2.0.0"

}

allprojects {
    repositories {
        ...
        google()
        jcenter()
        maven { url 'https://maven.google.com' }
    }
}

Більше про компоненти архітектури, пов'язані з життєвим циклом, ви можете прочитати тут - https://developer.android.com/topic/libraries/architecture/lifecycle


10
Це безумовно має бути правильною відповіддю! Це спрацювало як принадність: D
JaviOverflow

2
Це прекрасно працює, я також трохи змінив, щоб я міг легше отримати доступ до стану переднього плану / фонового режиму поза цим класом: companion object { private var foreground = false fun isForeground() : Boolean { return foreground } }тоді ви можете отримати стан переднього плану за допомогоюArchLifecycleApp.isForeground()
Jose Jet

2
О людино, це набагато краще, ніж моя стара відповідь. Майте +1 від мене. Я оновив свою відповідь, щоб вказати людям на ваші.
Cornstalks

2
Хоча це правильна відповідь, немає необхідності реалізовувати зворотні дзвінки, ви можете просто запитувати ProcessLifecycleOwner коли завгодно. Перевірте stackoverflow.com/a/52678290/6600000
Keivan Esbati

2
Як каже doc The LifecycleOwner for the whole application process. Note that if your application has multiple processes, this provider does not know about other processes. , це не працює для multiple processesдодатків, чи є якісь api, які ми можемо досягти елегантно?
acntwww

23

Починаючи бібліотеку підтримки версії 26, ви можете використовувати ProcessLifecycleOwner , просто додайте її до своєї залежності, як описано тут , наприклад:

dependencies {
    def lifecycle_version = "1.1.1"

    // ViewModel and LiveData
    implementation "android.arch.lifecycle:extensions:$lifecycle_version"
    // alternatively - Lifecycles only (no ViewModel or LiveData).
    //     Support library depends on this lightweight import
    implementation "android.arch.lifecycle:runtime:$lifecycle_version"
    annotationProcessor "android.arch.lifecycle:compiler:$lifecycle_version" // use kapt for Kotlin
}

А потім просто запитуйте, ProcessLifecycleOwnerколи вам потрібно стан додатків, приклади:

//Check if app is in background
ProcessLifecycleOwner.get().getLifecycle().getCurrentState() == Lifecycle.State.CREATED;

//Check if app is in foreground
ProcessLifecycleOwner.get().getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED);

2
Спасибі, це найкращий і найпростіший спосіб, який підходить в будь-якій частині коду, особливо при використанні fcm.
Mihae Kheel

якщо програма закрита повністю, що поверне перший метод?
Євгеній Мішустін

@EvgeniyMishustin, що залежить від поточного стану програми, але ви, як правило, побачили СТВОРЕНО, а потім ЗРУШЕНО, і після цього ви не отримаєте жодних нових подій.
Keivan Esbati

Тож де будь-яке твердження "ЯК", щоб побачити додаток IF на задньому плані (на передньому плані) ???
екшакінг

@ekashking просто помістив увесь вислів у if-пункт. Наприклад: якщо (ProcessLifecycleOwner.get (). GetLifecycle (). GetCurrentState (). IsAtLeast (Lifecycle.State.STARTED)) => Додаток на передньому плані
Keivan Esbati

20

З Android API 16 існує простий спосіб перевірити, чи додаток на першому плані. Це може бути не дурним, але жоден метод на Android не є надійним. Цей метод достатньо хороший для використання, коли ваша служба отримує оновлення від сервера і має вирішити, показувати сповіщення чи ні (тому що якщо інтерфейс користувача передній план, користувач помітить оновлення без повідомлення).

RunningAppProcessInfo myProcess = new RunningAppProcessInfo();
ActivityManager.getMyMemoryState(myProcess);
isInBackground = myProcess.importance != RunningAppProcessInfo.IMPORTANCE_FOREGROUND;

чи повинен цей код знаходитися всередині класу обслуговування чи іншого класу, наприклад класу програми? Дуже дякую.
Вуппі

.. де б ви не хотіли використовувати його, оскільки фінальний рядок - це лише булева гра, яку ви не можете перевірити.
AO_

Це та сама методика AWS Android SDK для push-сповіщень.
spakmad

Майте на увазі, що "Визначення фону з метою обмеження обслуговування відрізняється від визначення, яке використовує управління пам'яттю; додаток може бути у фоновому режимі, що стосується управління пам'яттю, але на передньому плані, що стосується його здатності запускати послуги.) " developer.android.com/about/versions/oreo/background.html (
ARLabs

Дякую, це спрацювало! Мені вдалося скористатися цим кодом, JobServiceщоб виявити, що служба працює у фоновому режимі.
Михайло Ософський

17

Відповідь Idolon - це схильність до помилок і набагато складніше, якщо все-таки повторюється тут, перевірте, чи додаток для Android на перший план чи ні? і тут Визначення поточної програми переднього плану з фонового завдання чи послуги

Існує набагато простіший підхід:

На базовій активності, яку розширюють усі види діяльності:

protected static boolean isVisible = false;

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


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

Кожен раз, коли вам потрібно перевірити, чи якась програма вашої програми не є на передньому плані, просто перевірте isVisible() ;

Щоб зрозуміти такий підхід, перевірте цю відповідь щодо життєвого циклу активності: Побічний життєвий цикл діяльності


3
Idolon's answer is error prone- на жаль, я маю з вами погодитися. На основі коментаря Діанні Хакборн у групах Google я оновив свою відповідь. Перевірте, будь ласка, на деталі.
Ідолон

2
Це не є надійним рішенням. Один з сценаріїв не є користувач розібрали панель повідомлень, а потім ні onPause, onStop, ні onResumeподія називається. То що робити тоді, якщо жодна з цих подій не буде звільнена ?!

Це привело мене до цього питання: stackoverflow.com/questions/33657102 / ...
Ruchir Baronia

На жаль, але цей код працює неправильно, коли діяльність починається, коли екран вимкнено. У цьому випадку onResume і onPause називаються створення isVisible = false.
CoolMind

@CoolMind Чи можете ви, будь ласка, пояснити, що є випадком використання, коли ви б запустили активність, перебуваючи у фоновому режимі?
neteinstein

11

Я спробував рекомендоване рішення, яке використовує Application.ActivityLifecycleCallbacks та багато інших, але вони не спрацювали, як очікувалося. Завдяки Саржу я придумав досить просте і зрозуміле рішення, яке описую нижче.

Ключовим рішенням є те, що ми розуміємо, що якщо у нас ActivityA і ActivityB, і ми називаємо ActivityB від ActivityA (а не дзвінки ActivityA.finish), тоді ActivityB onStart()буде викликаний перед ActivityA onStop().

Це також основна різниця між onStop()і onPause()що ніхто не зробив згадки в статтях , які я прочитав.

Отже, виходячи з поведінки життєвого циклу цієї діяльності, ви можете просто порахувати, скільки разів зробили onStart()та onPause()викликали у вашій програмі. Зауважте, що для кожної Activity вашої програми ви повинні переосмислити onStart()та onStop(), щоб збільшити / зменшити статичну змінну, що використовується для підрахунку. Нижче наведено код, що реалізує цю логіку. Зауважте, що я використовую розширений клас Application, тому не забувайте заявляти Manifest.xmlв тезі Application:, android:name=".Utilities"хоча він може бути реалізований і за допомогою простого спеціального класу.

public class Utilities extends Application
{
    private static int stateCounter;

    public void onCreate()
    {
        super.onCreate();
        stateCounter = 0;
    }

    /**
     * @return true if application is on background
     * */
    public static boolean isApplicationOnBackground()
    {
        return stateCounter == 0;
    }

    //to be called on each Activity onStart()
    public static void activityStarted()
    {
        stateCounter++;
    }

    //to be called on each Activity onStop()
    public static void activityStopped()
    {
        stateCounter--;
    }
}

Тепер на кожну діяльність нашої програми, ми повинні перевизначити onStart()і onStop()і збільшення / зменшення , як показано нижче:

@Override
public void onStart()
{
    super.onStart();
    Utilities.activityStarted();
}

@Override
public void onStop()
{
    Utilities.activityStopped();
    if(Utilities.isApplicationOnBackground())
    {
        //you should want to check here if your application is on background
    }
    super.onStop();
}

З цією логікою є 2 можливі випадки:

  1. stateCounter = 0 : Кількість зупинених дорівнює кількості розпочатих заходів, що означає, що програма працює на задньому плані.
  2. stateCounter > 0 : Кількість запущених перевищує кількість зупинених, це означає, що програма працює на передньому плані.

Зауважте: stateCounter < 0це означатиме, що більше зупинених заходів, а не розпочатих, що неможливо. Якщо ви зіткнулися з цим випадком, то це означає, що ви не збільшуєте / зменшуєте лічильник, як слід.

Ви готові йти. Вам слід перевірити, чи ваша програма знаходиться на тлі всередині onStop().


Я б переїхав if(Utilities.isApplicationOnBackground()) …до Utilities. Бо в іншому випадку на подію реагуватиме лише конкретна діяльність.
Відображати ім’я

10

Немає жодного способу, якщо ви самі відстежите це, щоб визначити, чи якась ваша діяльність видима чи ні. Можливо, вам слід подумати про те, щоб задати нове запитання StackOverflow, пояснивши, чого саме ви намагаєтеся досягти, користуючись досвідом користувача, тому ми можемо дати вам альтернативні ідеї щодо реалізації.


2
В android у нас є налаштування під назвою "Фонові дані". Цей параметр перетворює будь-яке з'єднання фонових даних, коли програма працює у фоновому режимі. Я хочу впровадити перемикач "Фонові дані" для своєї програми, тому коли жодна з моїх дій не бачна користувачеві, я хотів би, щоб моя служба перестала робити будь-яку передачу даних, але в момент, коли одна з моїх дій відновиться, я хотів би відновити передачу даних
cppdev

1
@cppdev: Сподіваємось, "передачу даних" проводить а Service. Якщо так, повідомте про свою діяльність, коли вони з’являються та зникають. Якщо Serviceвизначається відсутність видимих ​​дій, і це залишається таким чином деякий час, зупиніть передачу даних на наступній логічній зупинці. Так, для цього знадобиться код для кожної вашої діяльності, але зараз це неминучий AFAIK.
CommonsWare

1
Якщо ви хочете уникнути копіювання та вставки загального коду між усіма своїми діями, ви можете створити клас, який MyActivityClassуспадковується Activityта застосовує методи життєвого циклу, і зробити так, щоб усі ваші дії успадкували MyActivityClass. Це не спрацює для того PreferenceActivityчи іншого MapActivity(див. Це питання )
Гійом Брунері

@CommonsWare я намагався з OnPause () OnResume (), що він активний чи ні, але якщо моє додаток не переглядається на екрані перегляду, чи працює він у фоновому режимі, як перевірити, чи він активний чи ні
Manoj

@CommonsWare я намагався з OnPause () OnResume (), що він активний чи ні, але якщо моє додаток не переглядається на екрані перегляду, чи працює він у фоновому режимі, як перевірити, чи він активний чи ні
Manoj

5

Ви можете використовувати ComponentCallbacks2, щоб визначити, чи програма у фоновому режимі. BTW цей зворотний дзвінок доступний лише в API рівня 14 (сендвіч з морозивом) і вище.

Ви отримаєте виклик методу:

public abstract void onTrimMemory (int level)

якщо рівень рівний, ComponentCallbacks2.TRIM_MEMORY_UI_HIDDENто програма знаходиться у фоновому режимі.

Ви можете реалізувати цей інтерфейс до activity, serviceі т.д.

public class MainActivity extends AppCompatActivity 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) {
        // app is in background
     }
   }
}

1
Ви спробували свою відповідь, але не настільки надійні. зворотний виклик onTrimMemory не спрацьовує, коли екран заблокований, ні при натисканні кнопки "живлення", щоб заблокувати екран. Він також не завжди повертає TRIM_MEMORY_UI_HIDDEN, якщо ваша програма видима, і ви відкриєте інший додаток через повідомлення про рядок стану. Єдине надійне рішення - це реалізувати ActivityLifecycleCallbacks та налаштувати його на регістр використання.
velval

4

Спираючись на @Cornstalks відповідь, щоб включити пару корисних функцій.

Додаткові можливості:

  • запроваджено шаблон одиночної форми, щоб ви могли це зробити в будь-якому місці програми: AppLifecycleHandler.isApplicationVisible () та AppLifecycleHandler.isApplicationInForeground ()
  • додано обробку дублюючих подій (див. коментарі // вживати певних дій щодо зміни видимості та // вжити певних дій щодо зміни переднього плану)

App.java

public class App extends Application {
    @Override
    public void onCreate() {
        super.onCreate();

        registerActivityLifecycleCallbacks(AppLifecycleHandler.getInstance());
    }
}

AppLifecycleHandler.java

public class AppLifecycleHandler implements Application.ActivityLifecycleCallbacks {
    private int resumed;
    private int started;

    private final String DebugName = "AppLifecycleHandler";

    private boolean isVisible = false;
    private boolean isInForeground = false;

    private static AppLifecycleHandler instance;

    public static AppLifecycleHandler getInstance() {
        if (instance == null) {
            instance = new AppLifecycleHandler();
        }

        return instance;
    }

    private AppLifecycleHandler() {
    }

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

    @Override
    public void onActivityDestroyed(Activity activity) {
    }

    @Override
    public void onActivityResumed(Activity activity) {
        ++resumed;
        android.util.Log.w(DebugName, "onActivityResumed -> application is in foreground: " + (resumed > 0) + " (" + activity.getClass() + ")");
        setForeground((resumed > 0));
    }

    @Override
    public void onActivityPaused(Activity activity) {
        --resumed;
        android.util.Log.w(DebugName, "onActivityPaused -> application is in foreground: " + (resumed > 0) + " (" + activity.getClass() + ")");
        setForeground((resumed > 0));
    }

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

    @Override
    public void onActivityStarted(Activity activity) {
        ++started;
        android.util.Log.w(DebugName, "onActivityStarted -> application is visible: " + (started > 0) + " (" + activity.getClass() + ")");
        setVisible((started > 0));
    }

    @Override
    public void onActivityStopped(Activity activity) {
        --started;
        android.util.Log.w(DebugName, "onActivityStopped -> application is visible: " + (started > 0) + " (" + activity.getClass() + ")");
        setVisible((started > 0));
    }

    private void setVisible(boolean visible) {
        if (isVisible == visible) {
            // no change
            return;
        }

        // visibility changed
        isVisible = visible;
        android.util.Log.w(DebugName, "App Visiblility Changed -> application is visible: " + isVisible);

        // take some action on change of visibility
    }

    private void setForeground(boolean inForeground) {
        if (isInForeground == inForeground) {
            // no change
            return;
        }

        // in foreground changed
        isInForeground = inForeground;
        android.util.Log.w(DebugName, "App In Foreground Changed -> application is in foreground: " + isInForeground);

        // take some action on change of in foreground

    }

    public static boolean isApplicationVisible() {
        return AppLifecycleHandler.getInstance().started > 0;
    }

    public static boolean isApplicationInForeground() {
        return AppLifecycleHandler.getInstance().resumed > 0;
    }
}

3

Найкраще рішення, яке я придумав, використовує таймери.

Ви запустили таймер в onPause () і скасували той самий таймер в onResume (), є 1 екземпляр Таймера (зазвичай визначений у класі Application). Сам таймер встановлений для запуску Runnable через 2 секунди (або будь-який інтервал, який ви вважаєте за потрібний), коли таймер спрацьовує, ви встановлюєте прапор, який позначає програму як фонову.

У методі onResume () перед тим, як скасувати таймер, ви можете запитувати фоновий прапор для виконання будь-яких операцій запуску (наприклад, запуску завантажень або включення служб локації).

Це рішення дозволяє здійснити кілька дій на задній стеці, і не потребує дозволів на реалізацію.

Це рішення добре працює, якщо ви також використовуєте шину подій, оскільки ваш таймер може просто запустити подію, і різні частини вашої програми можуть відповісти відповідним чином.


Я починаю вважати, що це найкраще (хоча й невдале) рішення
dhaag23

Так, це найкраще рішення, з яким я теж впорався. Мені потрібно було зупинити сканування Bluetooth, коли додаток не було наперед, але я не міг просто використовувати паузу або зупиняти або знищувати, тому що я не хотів постійно зупинятись і починати, коли користувач пересувався через додаток.
CaptRespect

3

Якщо ви ввімкнули налаштування розробника "Не тримайте актуальності" - перевірити недостатньо лише кількість створених активів. Ви також повинні перевірити isSaveInstanceState . Мій спеціальний метод isApplicationRunning () перевіряє, чи працює програма Android:

Ось мій робочий код:

public class AppLifecycleService implements Application.ActivityLifecycleCallbacks {
    private int created;
    private boolean isSaveInstanceState;
    private static AppLifecycleService instance;

    private final static String TAG = AppLifecycleService.class.getName();

    public static AppLifecycleService getInstance() {
        if (instance == null) {
            instance = new AppLifecycleService();
        }
        return instance;
    }

    public static boolean isApplicationRunning() {
        boolean isApplicationRunning = true;
        if (getCountCreatedActvities() == 0 && !isSaveInstanceState()) {
            isApplicationRunning = false;
        }
        return isApplicationRunning;
    }

    public static boolean isSaveInstanceState() {
        return AppLifecycleService.getInstance().isSaveInstanceState;
    }

    public static int getCountCreatedActvities() {
        return AppLifecycleService.getInstance().created;
    }

    private AppLifecycleService() {
    }

    @Override
    public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
        this.isSaveInstanceState = true;
    }

    @Override
    public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
        ++created;
    }

    @Override
    public void onActivityDestroyed(Activity activity) {
        --created;
    }

    @Override
    public void onActivityResumed(Activity activity) {   }

    @Override
    public void onActivityPaused(Activity activity) { }


    @Override
    public void onActivityStarted(Activity activity) { }

    @Override
    public void onActivityStopped(Activity activity) { }        

}

3

Єдине правильне рішення:

MainActivity.java:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        MyApp.mainActivity = this;
        super.onCreate(savedInstanceState);
        ...
    }

MyApp.java:

public class MyApp extends Application implements LifecycleObserver {

    public static MainActivity mainActivity = null;

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

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    void onAppBackgrounded() {
        // app in background
        if (mainActivity != null) {
            ...
        }
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    void onAppForegrounded() {
        // app in foreground
        if (mainActivity != null) {
            ...
        }
    }

}

Я не бачу, як це рішення може дати мені відповідь на просте запитання в ІФ-заяві про мою діяльність (або фрагмент), чи є моя програма на задньому плані чи на передньому плані. Де заява "ІФ" ???
екшакінг

2

Щоб виправити те, що сказали CommonsWare та Key, ви можете, можливо, розширити клас програми та всі ваші дії викликати це у своїх методах onPause / onResume. Це дозволить вам дізнатися, які види діяльності видимі, але це, мабуть, впорається краще.

Чи можете ви детально зупинитися на тому, що ви маєте на увазі? Якщо ви говорите, що ви працюєте у фоновому режимі, ви маєте на увазі просто збереження програми у пам’яті, хоча вона наразі не є на екрані? Ви розглядали використання Служб як більш стійкий спосіб керувати додатком, коли він не зосереджений?


В android у нас є налаштування під назвою "Фонові дані". Цей параметр перетворює будь-яке з'єднання фонових даних, коли програма працює у фоновому режимі. Я хочу впровадити перемикач "Фонові дані" для свого додатка, тому коли жодна з моїх дій не бачна користувачеві, я хотів би, щоб моя служба перестала робити будь-яку передачу даних, але в момент, коли одна з моїх дій відновиться, я хотів би відновити передачу даних
cppdev

1
Applicationне має onPause()або onResume().
CommonsWare

1
@CommonsWare Ви маєте рацію, я мав на увазі кожну окрему Діяльність, звертаючись до Заявки під час їх паузи / резюме. Це в основному ідея, яку ви просто поділилися на коментар до своєї відповіді, хоча ви використовували Послуги, які, на мою думку, є розумнішим кроком.
День

2

Я зробив власну реалізацію ActivityLifecycleCallbacks. Я використовую SherlockActivity, але для нормального класу Activity може працювати.

По-перше, я створюю інтерфейс, у якому є всі методи для відстеження життєвого циклу діяльності:

public interface ActivityLifecycleCallbacks{
    public void onActivityStopped(Activity activity);
    public void onActivityStarted(Activity activity);
    public void onActivitySaveInstanceState(Activity activity, Bundle outState);
    public void onActivityResumed(Activity activity);
    public void onActivityPaused(Activity activity);
    public void onActivityDestroyed(Activity activity);
    public void onActivityCreated(Activity activity, Bundle savedInstanceState);
}

По-друге, я реалізував цей інтерфейс у класі мого додатка:

public class MyApplication extends Application implements my.package.ActivityLifecycleCallbacks{

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

    @Override
    public void onActivityStopped(Activity activity) {
        Log.i("Tracking Activity Stopped", activity.getLocalClassName());

    }

    @Override
    public void onActivityStarted(Activity activity) {
        Log.i("Tracking Activity Started", activity.getLocalClassName());

    }

    @Override
    public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
        Log.i("Tracking Activity SaveInstanceState", activity.getLocalClassName());
    }

    @Override
    public void onActivityResumed(Activity activity) {
        Log.i("Tracking Activity Resumed", activity.getLocalClassName());
    }

    @Override
    public void onActivityPaused(Activity activity) {
        Log.i("Tracking Activity Paused", activity.getLocalClassName());
    }

    @Override
    public void onActivityDestroyed(Activity activity) {
        Log.i("Tracking Activity Destroyed", activity.getLocalClassName());
    }

    @Override
    public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
        Log.i("Tracking Activity Created", activity.getLocalClassName());
    }
}

По-третє, я створюю клас, який поширюється на SherlockActivity:

public class MySherlockActivity extends SherlockActivity {

    protected MyApplication nMyApplication;

    protected void onCreate(Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        super.onCreate(savedInstanceState);
        nMyApplication = (MyApplication) getApplication();
        nMyApplication.onActivityCreated(this, savedInstanceState);
    }

    protected void onResume() {
        // TODO Auto-generated method stub
        nMyApplication.onActivityResumed(this);
        super.onResume();

    }

    @Override
    protected void onPause() {
        // TODO Auto-generated method stub
        nMyApplication.onActivityPaused(this);
        super.onPause();
    }

    @Override
    protected void onDestroy() {
        // TODO Auto-generated method stub
        nMyApplication.onActivityDestroyed(this);
        super.onDestroy();
    }

    @Override
    protected void onStart() {
        nMyApplication.onActivityStarted(this);
        super.onStart();
    }

    @Override
    protected void onStop() {
        nMyApplication.onActivityStopped(this);
        super.onStop();
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        nMyApplication.onActivitySaveInstanceState(this, outState);
        super.onSaveInstanceState(outState);
    }   
}

По-четверте, весь клас, який поширюється на SherlockActivity, я замінив на MySherlockActivity:

public class MainActivity extends MySherlockActivity{

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }

}

Тепер у logcat ви побачите журнали, запрограмовані в реалізації інтерфейсу, зробленого в MyApplication.


1

Діяльність призупиняється, коли діалог виходить над ним, тому всі рекомендовані рішення є напіврозчинами. Вам також потрібно створити гачки для діалогів.



1

Офіційні документи:

Система розрізняє програми переднього плану та фонові програми. (Визначення тла для обмежень обслуговування відрізняється від визначення, яке використовує управління пам'яттю; програма може бути у фоновому режимі, що стосується управління пам'яттю , але на передньому плані, що стосується її здатності запускати послуги.) Додаток - це вважається на передньому плані, якщо щось із наступного:

  1. Він має видиму активність, незалежно від того, запущена чи призупинена.
  2. У ньому є передня робота.
  3. Інший додаток переднього плану підключено до додатку, або прив’язавшись до однієї зі своїх послуг, або скориставшись одним із своїх постачальників вмісту. Наприклад, додаток стоїть на передньому плані, якщо інший додаток прив'язується до нього:
    • IME
    • Сервіс шпалер
    • Слухач сповіщення
    • Голосовий або текстовий сервіс

Якщо жодне з цих умов не відповідає дійсності, програма вважається такою, що знаходиться у фоновому режимі.


0

Ще одне рішення для цієї старої публікації (для тих, хто може допомогти):


<application android:name=".BaseApplication" ... >

public class BaseApplication extends Application {

    private class Status {
        public boolean isVisible = true;
        public boolean isFocused = true;
    }

    private Map<Activity, Status> activities;

    @Override
    public void onCreate() {
        activities = new HashMap<Activity, Status>();
        super.onCreate();
    }

    private boolean hasVisibleActivity() {
        for (Status status : activities.values())
            if (status.isVisible)
                return true;
        return false;
    }

    private boolean hasFocusedActivity() {
        for (Status status : activities.values())
            if (status.isFocused)
                return true;
        return false;
    }

    public void onActivityCreate(Activity activity, boolean isStarting) {
        if (isStarting && activities.isEmpty())
            onApplicationStart();
        activities.put(activity, new Status());
    }

    public void onActivityStart(Activity activity) {
        if (!hasVisibleActivity() && !hasFocusedActivity())
            onApplicationForeground();
        activities.get(activity).isVisible = true;
    }

    public void onActivityWindowFocusChanged(Activity activity, boolean hasFocus) {
        activities.get(activity).isFocused = hasFocus;
    }

    public void onActivityStop(Activity activity, boolean isFinishing) {
        activities.get(activity).isVisible = false;
        if (!isFinishing && !hasVisibleActivity() && !hasFocusedActivity())
            onApplicationBackground();
    }

    public void onActivityDestroy(Activity activity, boolean isFinishing) {
        activities.remove(activity);
        if(isFinishing && activities.isEmpty())
            onApplicationStop();
    }

    private void onApplicationStart() {Log.i(null, "Start");}
    private void onApplicationBackground() {Log.i(null, "Background");}
    private void onApplicationForeground() {Log.i(null, "Foreground");}
    private void onApplicationStop() {Log.i(null, "Stop");}

}

public class MyActivity extends BaseActivity {...}

public class BaseActivity extends Activity {

    private BaseApplication application;

    @Override
    protected void onCreate(Bundle state) {
        application = (BaseApplication) getApplication();
        application.onActivityCreate(this, state == null);
        super.onCreate(state);
    }

    @Override
    protected void onStart() {
        application.onActivityStart(this);
        super.onStart();
    }

    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        application.onActivityWindowFocusChanged(this, hasFocus);
        super.onWindowFocusChanged(hasFocus);
    }

    @Override
    protected void onStop() {
        application.onActivityStop(this, isFinishing());
        super.onStop();
    }

    @Override
    protected void onDestroy() {
        application.onActivityDestroy(this, isFinishing());
        super.onDestroy();
    }

}

0

Дивіться коментар у функції onActivityDestroyed.

Працює з цільовою версією SDK версії 14>:

import android.app.Activity;
import android.app.Application;
import android.os.Bundle;
import android.util.Log;

public class AppLifecycleHandler implements Application.ActivityLifecycleCallbacks {

    public static int active = 0;

    @Override
    public void onActivityStopped(Activity activity) {
        Log.i("Tracking Activity Stopped", activity.getLocalClassName());
        active--;
    }

    @Override
    public void onActivityStarted(Activity activity) {
        Log.i("Tracking Activity Started", activity.getLocalClassName());
        active++;
    }

    @Override
    public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
        Log.i("Tracking Activity SaveInstanceState", activity.getLocalClassName());
    }

    @Override
    public void onActivityResumed(Activity activity) {
        Log.i("Tracking Activity Resumed", activity.getLocalClassName());
        active++;
    }

    @Override
    public void onActivityPaused(Activity activity) {
        Log.i("Tracking Activity Paused", activity.getLocalClassName());
        active--;
    }

    @Override
    public void onActivityDestroyed(Activity activity) {
        Log.i("Tracking Activity Destroyed", activity.getLocalClassName());
        active--;

        // if active var here ever becomes zero, the app is closed or in background
        if(active == 0){
            ...
        }

    }

    @Override
    public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
        Log.i("Tracking Activity Created", activity.getLocalClassName());
        active++;
    }
}

0

Ви повинні використовувати спільний параметр, щоб зберігати майно та діяти на ньому, використовуючи службові обов'язки, пов’язані з вашою діяльністю. Якщо ви використовуєте лише прив'язку (тобто ніколи не використовуєте startService), ваша служба запускатиметься лише тоді, коли ви прив'язуєтесь до неї (прив'яжіть onResume і unbind onPause), що змусить її працювати лише на передньому плані, і якщо ви хочете працювати над фон, ви можете скористатися звичайною послугою стартової зупинки.


0

Я думаю, це питання повинно бути більш зрозумілим. Коли? Де? Яку конкретну ситуацію ви хочете зрозуміти, якщо ваша програма знаходиться у фоновому режимі?

Я просто ввожу своє рішення.
Це я роблю, використовуючи поле "важливість" RunningAppProcessInfoкласу в onStopметоді кожної діяльності в моєму додатку, чого можна просто досягти, надавши BaseActivityдля інших дій розширення, яке реалізує onStopметод, щоб перевірити значення "важливості". Ось код:

public static boolean isAppRunning(Context context) {
    ActivityManager activityManager = (ActivityManager) context
        .getSystemService(Context.ACTIVITY_SERVICE);
    List<RunningAppProcessInfo> appProcesses = activityManager
        .getRunningAppProcesses();
    for (RunningAppProcessInfo appProcess : appProcesses) {
        if (appProcess.processName.equals(context.getPackageName())) {
            if (appProcess.importance != RunningAppProcessInfo.IMPORTANCE_PERCEPTIBLE) {
                return true;
            } 
        }
    }
    return false;
}

Це не рекомендується рішення, як сказано у відповіді @ Idolon.
CoolMind

0

Рекомендую прочитати цю сторінку: http://developer.android.com/reference/android/app/Activity.html

Коротше кажучи, ваша активність більше не помітна після onStop()виклику.


3
У моїй заявці є близько 10 заходів. Тож я хочу знати, чи жодна з них не є видимою для користувача. Загалом, я хочу знати, чи працює моя програма в цілому у фоновому режимі
cppdev

Тож ви відстежуєте всі 10-іш. Або, як запропонував CommonsWare, поясніть, що ви намагаєтеся зробити.
Ключ

3
Це не правильно. Ваша активність відображається до onStop; між onPauseі onStopце видно , але не на передньому плані .
nickgrim

@nickgrim: що невірно? Я заявив, що активність більше не видно після onStop()виклику, яка узгоджується з тим, що ви написали.
Ключ

@Key: Ви спочатку казали, поки onPauseне подзвонили: нещодавня редакція виправила вас.
nickgrim


0

На мою думку, багато відповідей вносять велике навантаження коду і приносять багато складності та нечитабельності.

Коли люди запитують SO, як спілкуватися між a Serviceі a Activity, я зазвичай радимо використовувати LocalBroadcastManager .


Чому?

Ну, цитуючи документи:

  • Ви знаєте, що дані, які ви транслюєте, не залишатимуть ваш додаток, тому не потрібно турбуватися про витік приватних даних.

  • Неможливо, щоб інші програми надсилали ці трансляції до вашої програми, тому вам не потрібно турбуватися про наявність отворів у безпеці, які вони можуть використовувати.

  • Це ефективніше, ніж надсилання глобальної трансляції через систему.

Не в документах:

  • Для цього не потрібні зовнішні бібліотеки
  • Код мінімальний
  • Це швидко здійснити і зрозуміти
  • Немає власних реалізованих зворотних викликів / ультра-однотонних / внутрішньопроцесорних моделей ...
  • Відсутність сильних посилань на Activity, Application...

Опис

Отже, ви хочете перевірити, чи на Activityданий момент якесь із цих видання є на першому плані. Зазвичай ви робите це у Serviceвашому, або вашомуApplication класі класі.

Це означає, що ваші Activityоб’єкти стають відправником сигналу (я вмикаюсь / вимикаюсь). ServiceЗ іншого боку, ваш стаєReceiver .

Є два моменти, в яких вашActivity розповідає, чи йде вона на передньому плані чи на задньому плані (так, лише два ... не 6).

Коли Activityвиходить на перший план, onResume()метод запускається (його також називають після onCreate()).

Коли Activityйде в спину, onPause()називається.

Це моменти, в які ви Activityповинні надсилати сигнал вашому, Serviceщоб описати його стан.

У випадку, коли це декілька Activity, Activityспочатку пам’ятайте, що «ан» переходить на другий план, потім на другий план виходить інший.

Тож ситуація була б така: *

Activity1 -- send --> Signal:OFF
Activity2 -- send --> Signal:ON

Service/ ApplicationБуде просто продовжувати слухати музику цих сигналів і діяти відповідним чином .


Код (TLDR)

Ви Serviceповинні реалізувати BroadcastReceiverдля прослуховування сигналів.

this.localBroadcastReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        // received data if Activity is on / off
    }
}

public static final IntentFilter SIGNAL_FILTER = new IntentFilter("com.you.yourapp.MY_SIGNAL") 

Зареєструйте ReceiverвService::onCreate()

@Override
protected void onCreate() {
    LocalBroadcastManager.getInstance(getApplicationContext()).registerReceiver(this.localBroadcastReceiver, SIGNAL_FILTER);
}

Скасуйте його в Service::onDestroy()

@Override
protected void onDestroy() {
    // I'm dead, no need to listen to anything anymore.
    LocalBroadcastManager.getInstance(getApplicationContext()).unregisterReceiver(this.localBroadcastReceiver);
}

Тепер ваші Activityповинні повідомити про свою державу.

В Activity::onResume()

Intent intent = new Intent();
intent.setAction(SomeActivity.SIGNAL_FILTER); // put ON boolean in intent    
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent);

В Activity::onPause()

Intent intent = new Intent();
intent.setAction(SomeActivity.SIGNAL_FILTER); // put OFF boolean in intent    
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent);

Дуже-дуже поширена ситуація

Розробник: Я хочу надіслати дані з моїх Serviceі оновити Activity. Як перевірити, чи Activityстоїть на передньому плані?

Зазвичай не потрібно перевіряти, чи Activityстоїть на передньому плані чи ні. Просто надсилайте дані за допомогою LocalBroadcastManagerсвого Service. Якщо Activityввімкнено, воно відповість і діятиме.

У цій дуже поширеній ситуації Serviceперетворювач стає відправником, а Activityреалізує BroadcastReceiver.

Отже, створіть Receiverу своєму Activity. Зареєструйте його onResume()та скасуйте його onPause(). Не потрібно використовувати інші методи життєвого циклу .

Визначте Receiverповедінку в onReceive()(оновіть ListView, зробіть це, зробіть це, ...).

Таким чином Activityволя слухатиме лише, якщо вона на передньому плані, і нічого не відбудеться, якщо вона знаходиться ззаду або знищена.

У випадку декількох, Activityвідповідь, що Activityбуде включено (якщо вони також реалізують Receiver).

Якщо всі знаходяться на задньому плані, ніхто не відгукнеться і сигнал просто загубиться.

Надішліть дані Serviceчерез via Intent(див. Код вище), вказавши ідентифікатор сигналу.


  • За винятком підтримки для багатьох вікон . Це може бути складним (будь ласка, протестуйте його, якщо потрібно) ...

0
fun isAppInForeground(): Boolean {
    val activityManager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager ?: return false

    val appProcesses = activityManager.runningAppProcesses ?: return false

    val packageName = packageName
    for (appProcess in appProcesses) {
        if (appProcess.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND && appProcess.processName == packageName) {
            return true
        }
    }

    return false
}

0

Жодна з відповідей не відповідала конкретному випадку, якщо вам цікаво знати, чи є специфічна активність у форварді та чи ви SDK без прямого доступу до програми. Для мене я був у фоновому режимі, щойно отримав натиснене повідомлення про нове повідомлення в чаті і хочу лише відобразити системне сповіщення, якщо екран чату не на передньому плані.

Використовуючи те, ActivityLifecycleCallbacksщо було рекомендовано в інших відповідях, я створив невеликий утиліт-клас, який містить логіку щодо того, MyActivityперебуває на передньому плані чи ні.

class MyActivityMonitor(context: Context) : Application.ActivityLifecycleCallbacks {

private var isMyActivityInForeground = false

init {
    (context.applicationContext as Application).registerActivityLifecycleCallbacks(this)
}

fun isMyActivityForeground() = isMyActivityInForeground

override fun onActivityPaused(activity: Activity?) {
    if (activity is MyActivity) {
        isMyActivityInForeground = false
    }
}

override fun onActivityResumed(activity: Activity?) {
    if (activity is MyActivity) {
        isMyActivityInForeground = true
    }
}

}


-1

У своїх заходах onResume і onPause я пишу isVisible boolean до SharedPrefences.

    SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
    Editor editor = sharedPrefs.edit();
    editor.putBoolean("visible", false);
    editor.commit();

І читайте його в іншому місці, коли потрібно,

    // Show a Toast Notification if App is not visible (ie in background. Not running, etc) 
    SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context);
    if(!sharedPrefs.getBoolean("visible", true)){...}

Можливо, не елегантно, але це працює для мене ...


-1

Відповісти на це може бути пізно, але якщо хтось приходить в гості, то ось таке рішення, яке я пропоную. Причина (-ла) додаток хоче дізнатися, що стан перебуває у фоновому режимі або виходить на перший план, може бути багато, декілька - 1. Показувати тости та сповіщення, коли користувач перебуває в BG. 2. Щоб виконати деякі завдання, коли вперше користувач надходить з BG, наприклад, опитування, перемальовування тощо.

Рішення Ідолона та інших піклується про першу частину, а не про другу. Якщо у вашому додатку є кілька дій, і користувач перемикається між ними, то до моменту, коли ви будете проводити другу активність, видимий прапор буде помилковим. Тому його не можна використовувати детерміновано.

Я зробив щось, що було запропоновано CommonsWare: "Якщо Служба визначить, що ніяких дій не видно, і це залишається таким чином протягом деякої кількості часу , зупиніть передачу даних на наступній логічній зупинці".

Рядкий жирний шрифт важливий, і це може бути використане для досягнення другого пункту. Отож, що я роблю, коли я отримую onActivityPaused (), не змінюйте видиме на хибне безпосередньо, натомість майте таймер 3 секунди (це максимум, що слід запустити наступну діяльність), і якщо немає onActivityResumed ( ) зателефонуйте протягом наступних 3 секунд, змініть видимий на хибний. Аналогічно в onActivityResumed (), якщо є таймер, то я його скасовую. Підводячи підсумок, видиме стає isAppInBackground.

На жаль, не вдалося скопіювати і вставити код ...


-3

Я хотів би рекомендувати вам скористатися іншим способом цього.

Я думаю, ви хочете показати екран запуску, коли програма запускається, якщо вона вже запущена в бекенді, не показуйте її.

Ваша програма може постійно записувати поточний час у певний файл. Поки ваша програма запускається, перевірте останню позначку часу, якщо current_time-last_time> часовий діапазон, вказаний для написання останнього часу, це означає, що ваша програма зупинена, або вбита системою, або самим користувачем.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.