Я маю на увазі, я не маю на увазі, що жодна з дій програми зараз не бачна користувачеві?
Я маю на увазі, я не маю на увазі, що жодна з дій програми зараз не бачна користувачеві?
Відповіді:
Є кілька способів виявити, чи працює ваша програма у фоновому режимі, але лише один із них є повністю надійним:
Правильне рішення (кредити йдуть до Дену , 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). Ви можете використовувати їх, щоб відстежувати, чи активність вашої програми наразі бачна користувачеві. Перевірте відповідь кукурудзяні стебла нижче для деталей.
Неправильний, з якого
я пропонував таке рішення:
Ви можете виявити на даний момент передній план / фонову програму, за допомогою
ActivityManager.getRunningAppProcesses()
якої повертається списокRunningAppProcessInfo
записів. Щоб визначити, чи є ваша програма на передньому плані,RunningAppProcessInfo.importance
поле перевірки рівності доRunningAppProcessInfo.IMPORTANCE_FOREGROUND
часуRunningAppProcessInfo.processName
дорівнює назві пакету додатків.Крім того, якщо ви зателефонуєте
ActivityManager.getRunningAppProcesses()
з інтерфейсу інтерфейсу програми, він поверне значенняIMPORTANCE_FOREGROUND
для вашої задачі, незалежно від того, перебуває він на передньому плані чи ні. Зателефонуйте у фонову нитку (наприклад, черезAsyncTask
), і вона поверне правильні результати.
Хоча це рішення може працювати (і воно справді працює більшу частину часу), я настійно рекомендую утриматися від його використання. І ось чому. Як писала Діанна Хакборн :
Ці API не існують для додатків, щоб базувати свій потік інтерфейсу, але для того, щоб показати користувачеві запущені програми чи менеджер завдань тощо.
Так, є список, який зберігається в пам'яті для цих речей. Однак він вимикається в іншому процесі, керованому потоками, що працюють окремо від вашого, і не те, на що ви можете розраховувати (а) побачити вчасно прийняти правильне рішення або (б) мати послідовну картину до моменту повернення. Плюс рішення про те, що "наступна" діяльність потрібно перейти, завжди робиться в тому місці, де має відбутися перемикання, і це не до того моменту, коли стан активності коротко заблокований, щоб зробити перемикач. насправді точно знаю, що буде далі.
І впровадження та глобальна поведінка тут не гарантують, що вони залишаться однаковими і надалі.
Я хотів би, щоб я прочитав це перед тим, як розмістити відповідь на ПЗ, але, сподіваюся, не пізно визнати свою помилку.
Ще одне неправильне рішення бібліотека
Droid-Fu, згадана в одній з відповідей, використовує ActivityManager.getRunningTasks
для свого isApplicationBroughtToBackground
методу. Дивіться коментар Діанні вище і також не використовуйте цей метод.
OnStop
прохання до isActivityVisible
.
відповідь 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()
.
onStop()
після super.onStop()
. Не перевіряйте наявність фонового режиму в onPause()
.
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
companion object { private var foreground = false fun isForeground() : Boolean { return foreground } }
тоді ви можете отримати стан переднього плану за допомогоюArchLifecycleApp.isForeground()
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, які ми можемо досягти елегантно?
Починаючи бібліотеку підтримки версії 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);
З Android API 16 існує простий спосіб перевірити, чи додаток на першому плані. Це може бути не дурним, але жоден метод на Android не є надійним. Цей метод достатньо хороший для використання, коли ваша служба отримує оновлення від сервера і має вирішити, показувати сповіщення чи ні (тому що якщо інтерфейс користувача передній план, користувач помітить оновлення без повідомлення).
RunningAppProcessInfo myProcess = new RunningAppProcessInfo();
ActivityManager.getMyMemoryState(myProcess);
isInBackground = myProcess.importance != RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
JobService
щоб виявити, що служба працює у фоновому режимі.
Відповідь Idolon - це схильність до помилок і набагато складніше, якщо все-таки повторюється тут, перевірте, чи додаток для Android на перший план чи ні? і тут Визначення поточної програми переднього плану з фонового завдання чи послуги
Існує набагато простіший підхід:
На базовій активності, яку розширюють усі види діяльності:
protected static boolean isVisible = false;
@Override
public void onResume()
{
super.onResume();
setVisible(true);
}
@Override
public void onPause()
{
super.onPause();
setVisible(false);
}
Кожен раз, коли вам потрібно перевірити, чи якась програма вашої програми не є на передньому плані, просто перевірте isVisible()
;
Щоб зрозуміти такий підхід, перевірте цю відповідь щодо життєвого циклу активності: Побічний життєвий цикл діяльності
Idolon's answer is error prone
- на жаль, я маю з вами погодитися. На основі коментаря Діанні Хакборн у групах Google я оновив свою відповідь. Перевірте, будь ласка, на деталі.
onPause
, onStop
, ні onResume
подія називається. То що робити тоді, якщо жодна з цих подій не буде звільнена ?!
Я спробував рекомендоване рішення, яке використовує Application.ActivityLifecycleCallbacks та багато інших, але вони не спрацювали, як очікувалося. Завдяки Саржу я придумав досить просте і зрозуміле рішення, яке описую нижче.
Ключовим рішенням є те, що ми розуміємо, що якщо у нас ActivityA і ActivityB, і ми називаємо ActivityB від ActivityA (а не дзвінки
ActivityA.finish
), тоді ActivityBonStart()
буде викликаний перед ActivityAonStop()
.
Це також основна різниця між 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 можливі випадки:
stateCounter = 0
: Кількість зупинених дорівнює кількості розпочатих заходів, що означає, що програма працює на задньому плані.stateCounter > 0
: Кількість запущених перевищує кількість зупинених, це означає, що програма працює на передньому плані.Зауважте: stateCounter < 0
це означатиме, що більше зупинених заходів, а не розпочатих, що неможливо. Якщо ви зіткнулися з цим випадком, то це означає, що ви не збільшуєте / зменшуєте лічильник, як слід.
Ви готові йти. Вам слід перевірити, чи ваша програма знаходиться на тлі всередині onStop()
.
if(Utilities.isApplicationOnBackground()) …
до Utilities
. Бо в іншому випадку на подію реагуватиме лише конкретна діяльність.
Немає жодного способу, якщо ви самі відстежите це, щоб визначити, чи якась ваша діяльність видима чи ні. Можливо, вам слід подумати про те, щоб задати нове запитання StackOverflow, пояснивши, чого саме ви намагаєтеся досягти, користуючись досвідом користувача, тому ми можемо дати вам альтернативні ідеї щодо реалізації.
Service
. Якщо так, повідомте про свою діяльність, коли вони з’являються та зникають. Якщо Service
визначається відсутність видимих дій, і це залишається таким чином деякий час, зупиніть передачу даних на наступній логічній зупинці. Так, для цього знадобиться код для кожної вашої діяльності, але зараз це неминучий AFAIK.
MyActivityClass
успадковується Activity
та застосовує методи життєвого циклу, і зробити так, щоб усі ваші дії успадкували MyActivityClass
. Це не спрацює для того PreferenceActivity
чи іншого MapActivity
(див. Це питання )
Ви можете використовувати 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
}
}
}
Спираючись на @Cornstalks відповідь, щоб включити пару корисних функцій.
Додаткові можливості:
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;
}
}
Найкраще рішення, яке я придумав, використовує таймери.
Ви запустили таймер в onPause () і скасували той самий таймер в onResume (), є 1 екземпляр Таймера (зазвичай визначений у класі Application). Сам таймер встановлений для запуску Runnable через 2 секунди (або будь-який інтервал, який ви вважаєте за потрібний), коли таймер спрацьовує, ви встановлюєте прапор, який позначає програму як фонову.
У методі onResume () перед тим, як скасувати таймер, ви можете запитувати фоновий прапор для виконання будь-яких операцій запуску (наприклад, запуску завантажень або включення служб локації).
Це рішення дозволяє здійснити кілька дій на задній стеці, і не потребує дозволів на реалізацію.
Це рішення добре працює, якщо ви також використовуєте шину подій, оскільки ваш таймер може просто запустити подію, і різні частини вашої програми можуть відповісти відповідним чином.
Якщо ви ввімкнули налаштування розробника "Не тримайте актуальності" - перевірити недостатньо лише кількість створених активів. Ви також повинні перевірити 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) { }
}
Єдине правильне рішення:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
MyApp.mainActivity = this;
super.onCreate(savedInstanceState);
...
}
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) {
...
}
}
}
Щоб виправити те, що сказали CommonsWare та Key, ви можете, можливо, розширити клас програми та всі ваші дії викликати це у своїх методах onPause / onResume. Це дозволить вам дізнатися, які види діяльності видимі, але це, мабуть, впорається краще.
Чи можете ви детально зупинитися на тому, що ви маєте на увазі? Якщо ви говорите, що ви працюєте у фоновому режимі, ви маєте на увазі просто збереження програми у пам’яті, хоча вона наразі не є на екрані? Ви розглядали використання Служб як більш стійкий спосіб керувати додатком, коли він не зосереджений?
Application
не має onPause()
або onResume()
.
Я зробив власну реалізацію 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.
Оскільки це вже не згадується, я запропоную читачам вивчити ProcessLifecycleOwner, доступний через компоненти компонентів Android Architecture
Система розрізняє програми переднього плану та фонові програми. (Визначення тла для обмежень обслуговування відрізняється від визначення, яке використовує управління пам'яттю; програма може бути у фоновому режимі, що стосується управління пам'яттю , але на передньому плані, що стосується її здатності запускати послуги.) Додаток - це вважається на передньому плані, якщо щось із наступного:
Якщо жодне з цих умов не відповідає дійсності, програма вважається такою, що знаходиться у фоновому режимі.
Ще одне рішення для цієї старої публікації (для тих, хто може допомогти):
<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();
}
}
Дивіться коментар у функції 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++;
}
}
Ви повинні використовувати спільний параметр, щоб зберігати майно та діяти на ньому, використовуючи службові обов'язки, пов’язані з вашою діяльністю. Якщо ви використовуєте лише прив'язку (тобто ніколи не використовуєте startService), ваша служба запускатиметься лише тоді, коли ви прив'язуєтесь до неї (прив'яжіть onResume і unbind onPause), що змусить її працювати лише на передньому плані, і якщо ви хочете працювати над фон, ви можете скористатися звичайною послугою стартової зупинки.
Я думаю, це питання повинно бути більш зрозумілим. Коли? Де? Яку конкретну ситуацію ви хочете зрозуміти, якщо ваша програма знаходиться у фоновому режимі?
Я просто ввожу своє рішення.
Це я роблю, використовуючи поле "важливість" 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;
}
Рекомендую прочитати цю сторінку: http://developer.android.com/reference/android/app/Activity.html
Коротше кажучи, ваша активність більше не помітна після onStop()
виклику.
onStop
; між onPause
і onStop
це видно , але не на передньому плані .
onStop()
виклику, яка узгоджується з тим, що ви написали.
onPause
не подзвонили: нещодавня редакція виправила вас.
На мою думку, багато відповідей вносять велике навантаження коду і приносять багато складності та нечитабельності.
Коли люди запитують 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
(див. Код вище), вказавши ідентифікатор сигналу.
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
}
Жодна з відповідей не відповідала конкретному випадку, якщо вам цікаво знати, чи є специфічна активність у форварді та чи ви 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
}
}
}
У своїх заходах 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. Показувати тости та сповіщення, коли користувач перебуває в BG. 2. Щоб виконати деякі завдання, коли вперше користувач надходить з BG, наприклад, опитування, перемальовування тощо.
Рішення Ідолона та інших піклується про першу частину, а не про другу. Якщо у вашому додатку є кілька дій, і користувач перемикається між ними, то до моменту, коли ви будете проводити другу активність, видимий прапор буде помилковим. Тому його не можна використовувати детерміновано.
Я зробив щось, що було запропоновано CommonsWare: "Якщо Служба визначить, що ніяких дій не видно, і це залишається таким чином протягом деякої кількості часу , зупиніть передачу даних на наступній логічній зупинці".
Рядкий жирний шрифт важливий, і це може бути використане для досягнення другого пункту. Отож, що я роблю, коли я отримую onActivityPaused (), не змінюйте видиме на хибне безпосередньо, натомість майте таймер 3 секунди (це максимум, що слід запустити наступну діяльність), і якщо немає onActivityResumed ( ) зателефонуйте протягом наступних 3 секунд, змініть видимий на хибний. Аналогічно в onActivityResumed (), якщо є таймер, то я його скасовую. Підводячи підсумок, видиме стає isAppInBackground.
На жаль, не вдалося скопіювати і вставити код ...
Я хотів би рекомендувати вам скористатися іншим способом цього.
Я думаю, ви хочете показати екран запуску, коли програма запускається, якщо вона вже запущена в бекенді, не показуйте її.
Ваша програма може постійно записувати поточний час у певний файл. Поки ваша програма запускається, перевірте останню позначку часу, якщо current_time-last_time> часовий діапазон, вказаний для написання останнього часу, це означає, що ваша програма зупинена, або вбита системою, або самим користувачем.