Доступ в Интернет в длительной фоновой задаче

В моем приложении для Windows Phone 10 у меня есть длительная фоновая задача, запускаемая RfcommConnectionTrigger. Эта задача будет выполняться, пока мое устройство Bluetooth Rfcomm подключено.

Теперь я хочу достичь следующего:

  • Устройство Bluetooth отправляет некоторые данные в приложение на телефоне (работает)
  • Телефон ищет значение с помощью HTTP-запроса (не работает)
  • Телефон отправляет результат обратно к устройству Bluetooth (работающему)

Проблема с №2 в том, что как только я блокирую устройство, все HTTP-запросы по какой-то причине перестают работать. Я просто использую System.Net.Http.HttpClient для выполнения запроса, там ничего особенного не происходит. В частности, исключение создается в HttpClient.SendAsync .

Как обойти это ограничение?


Я нашел решение. Кажется, это полностью недокументированная «функция» — вам нужно добавить условие SystemConditionType.InternetAvailable к вашей фоновой задаче, чтобы разрешить ей использовать Интернет, когда устройство заблокировано. Я надеюсь, что это не означает, что когда телефон теряет доступ к Интернету, фоновая задача прекращается.

Это также означает, что теперь телефон должен быть подключен к Интернету, чтобы запустите фоновую задачу, которая все еще раздражает, но я думаю, что смогу с этим жить.



Проверка, работает ли приложение Android в фоновом режиме

Под фоном я подразумеваю, что ни одна из действий приложения в настоящее время не видна пользователю?


Есть несколько способов определить, работает ли ваше приложение в фоновом режиме, но только один из них является полностью надежным:

  1. Правильное решение (кредиты принадлежат Дэну, CommonsWare и NeTeInStEiN)
    Самостоятельно отслеживайте видимость вашего приложения, используя Activity.onPause , Activity .onResume . Сохраните статус «видимости» в каком-то другом классе. Хороший выбор — это ваша собственная реализация Application или Service (есть также несколько вариантов этого решения, если вы хотите проверить видимость активности из сервис).

    Пример
    Реализовать пользовательское приложение класс (обратите внимание на статический метод isActivityVisible () ):

      открытый класс MyApplication расширяет приложение {public static boolean isActivityVisible () {return activityVisible;  } public static void activityResumed () {activityVisible = true;  } public static void activityPaused () {activityVisible = false;  } private static boolean activityVisible;}  

    Зарегистрируйте класс своего приложения в AndroidManifest.xml :

        

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

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

    Обновление
    ActivityLifecycleCallbacks были добавлены в API уровня 14 (Android 4.0). Вы можете использовать их для отслеживания активности ваше приложение в настоящее время видно пользователю. Подробнее см. в ответе Cornstalks ниже.

  2. Не тот
    Я предлагал следующее решение :

    Вы можете обнаружить текущее переднее/фоновое приложение с помощью ActivityManager.getRunningAppProcesses () , который возвращает список RunningAppProcessInfo код> записи. Чтобы определить, находится ли ваше приложение на переднем плане, проверьте поле RunningAppProcessInfo.importance на соответствие RunningAppProcessInfo.IMPORTANCE_FOREGROUND , пока RunningAppProcessInfo.processName совпадает с именем вашего пакета приложения.

    Также, если вы вызовете ActivityManager.getRunningAppProcesses () из потока пользовательского интерфейса вашего приложения, он вернет важность IMPORTANCE_FOREGROUND для вашей задачи no независимо от того, находится ли он на самом деле на переднем плане или нет. Вызовите его в фоновом потоке (например, через AsyncTask ), и он вернет правильные результаты.

    Хотя это решение может работать (и действительно работает большую часть времени), я настоятельно рекомендую воздержаться от его использования. И вот почему. Как писала Дайан Хакборн:

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

    Да, в памяти есть список этих вещей. Однако он отключен в другом процессе, управляемом потоками, работающими отдельно от вашего, и не является чем-то, на что вы можете рассчитывать: (а) вовремя увидеть, чтобы принять правильное решение, или (б) иметь согласованную картину к тому времени, когда вы вернетесь. Кроме того, решение о том, к какому «следующему» действию следует перейти, всегда принимается в той точке, где должно произойти переключение, и только в этой точной точке (где состояние активности на короткое время заблокировано для переключения), мы на самом деле точно знаю, что будет дальше.

    И реализация и глобальное поведение здесь не гарантированно останутся такими же в будущем.

    Мне жаль, что я не прочитал это до того, как я отправлю ответ на SO, но, надеюсь, еще не поздно признать свою ошибку.

  3. Другое неправильное решение
    Библиотека Droid-Fu, упомянутая в одном из ответов, использует ActivityManager.getRunningTasks для своих isApplicationBaughtToBackground . См. Комментарий Дайанны выше и не используйте этот метод.

22


Ответ user1269737 — правильный (одобренный Google/Android) способ сделать это. Прочтите их ответ и поставьте им +1.

Я оставлю свой исходный ответ здесь для потомков. Это было лучшее из доступных еще в 2012 году, но теперь Android имеет соответствующую поддержку.

Исходный ответ

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

В этом методе действительно хорошо то, что у него нет асинхронных проблем, которые есть у getRunningTasks () , но вам также не нужно изменять каждое Activity в вашем приложении, чтобы установить/отключить что-либо в onResumed () / onPaused () . Это всего лишь несколько автономных строк кода, которые работают во всем вашем приложении. Кроме того, не требуются и фанковые разрешения.

MyLifecycleHandler.java:

  реализует открытый класс MyLifecycleHandler  ActivityLifecycleCallbacks {//Здесь я использую четыре отдельные переменные.  Вы, конечно, можете просто использовать два и//увеличивать/уменьшать их, вместо того, чтобы использовать четыре и увеличивать их все.  частный интервал возобновлен;  частный интервал приостановлен;  частный int запущен;  частный интервал остановлен;  @Override public void onActivityCreated (действие действия, Bundle savedInstanceState) {} @Override public void onActivityDestroyed (действие действия) {} @Override public void onActivityResumed (действие действия) {++ возобновлено;  } @Override public void onActivityPaused (активность действия) {++ paused;  android.util.Log. w ("тест", "приложение на переднем плане:" + (возобновлено> приостановлено));  } @Override public void onActivitySaveInstanceState (действие действия, пакет outState) {} @Override public void onActivityStarted (действие действия) {++ начато;  } @Override public void onActivityStopped (действие активности) {++ остановлено;  android.util.Log.w ("тест", "приложение видно:" + (запущено> остановлено));  }//Если вам нужна статическая функция, которую вы можете использовать для проверки того, является ли ваше приложение//передним планом/фоном, вы можете использовать следующее:/*//Замените четыре указанные выше переменные этими четырьмя частными статическими int resumed;  частный статический интервал приостановлен;  частный статический int запущен;  закрытый статический int остановлен; //И эти две публичные статические функции public static boolean isApplicationVisible () {return start> Stop;  } общедоступное статическое логическое значение isApplicationInForeground () {возврат возобновлено> приостановлено;  } */}  

MyApplication.java:

 //Не забывайте  чтобы добавить его в свой манифест, выполнив// 

@Mewzer задал несколько хороших вопросов об этом методе, на которые я хотел бы ответить в этом ответе для всех:

onStop () не вызывается в ситуациях нехватки памяти; это проблема здесь?

Нет. В документации для onStop () говорится:

Обратите внимание, что этот метод никогда не может быть вызван в ситуациях с нехваткой памяти, когда в системе нет достаточно памяти, чтобы поддерживать выполнение процесса вашего действия после вызова его метода onPause ().

Ключевым моментом здесь является «поддерживать выполнение процесса вашего действия . .. «Если когда-либо будет достигнута эта ситуация с нехваткой памяти, ваш процесс фактически будет убит (а не только ваша деятельность). Это означает, что этот метод проверки фонового состояния по-прежнему действителен, потому что а) вы все равно не можете проверить фоновое состояние, если ваш процесс убит, и б) если ваш процесс запускается снова (поскольку создается новое действие), член переменные (статические или нет) для MyLifecycleHandler будут сброшены на 0 .

Работает ли это для изменения конфигурации?

По умолчанию нет. Вы должны явно установить configChanges = Ориентация | Screenize ( | с чем угодно еще) в вашем файле манифеста и обрабатывать изменения конфигурации, иначе ваша активность будет уничтожен и воссоздан. Если вы не установите это значение, методы вашей активности будут вызываться в следующем порядке: onCreate -> onStart -> onResume -> (теперь повернуть) -> onPause -> onStop -> onDestroy -> onCreate -> onStart -> onResume . Как видите, перекрытия нет (обычно два действия перекрываются очень ненадолго при переключении между ними, как работает этот метод фонового обнаружения). Чтобы обойти это, вы должны установить configChanges так, чтобы ваша активность не была уничтожена. К счастью, мне пришлось установить configChanges уже во всех моих проектах, потому что для всей моей деятельности было нежелательно разрушаться при повороте/изменении размера экрана, поэтому я никогда не обнаружил, что это проблематично. (спасибо dpimka за то, что освежили мою память об этом и исправили меня!)

Одно примечание:

Когда я сказал «фон» здесь, в этом ответе, я имел в виду «ваше приложение больше не отображается». Действия Android могут быть видны, но не на переднем плане (например, если есть прозрачное наложение уведомлений). Вот почему я обновил этот ответ, чтобы отразить это.

Важно знать, что у Android есть странный момент неопределенности при переключении действий, когда ничего не находится на переднем плане . По этой причине, если вы проверите, находится ли ваше приложение на переднем плане при переключении между действиями (в том же приложении), вам сообщат, что вы не на переднем плане (даже если ваше приложение все еще является активным приложением и отображается ).

Вы можете проверить, находится ли ваше приложение на переднем плане, с помощью метода onPause () Activity после super.onPause () . Просто запомните странное состояние неопределенности, о котором я только что говорил.

Вы можете проверить, отображается ли ваше приложение (т. Е. Если оно не в фоновом режиме) в вашем Activity ‘ s onStop () метод после super.onStop () .

23


РЕШЕНИЕ GOOGLE — не взломать, как и предыдущие решения. Используйте ProcessLifecycleOwner

Kotlin:

  class ArchLifecycleApp: Application (), LifecycleObserver {переопределите fun onCreate (  ) {super.onCreate () ProcessLifecycleOwner.get (). lifecycle.addObserver (this)} @OnLifecycleEvent (Lifecycle.Event.ON_STOP) fun onAppBackgounded () {//Приложение в фоновом режиме} @OnLifecycleEvent (Lifecycle.Event.ON_START) весело  onAppForegounded () {//Приложение на переднем плане}}  

Java:

  открытый класс ArchLifecycleApp расширяет Приложение реализует LifecycleObserver {@Override public void onCreate () {super.onCreate ();  ProcessLifecycleOwner.get (). GetLifecycle (). AddObserver (это);  } @OnLifecycleEvent (Lifecycle.Event. ON_STOP) public void onAppBackgounded () {//Приложение в фоновом режиме} @OnLifecycleEvent (Lifecycle.Event.ON_START) public void onAppForegounded () {//Приложение на переднем плане}}  

in app.gradle

  dependencies {... implementation "android.arch.lifecycle: extensions: 1.1.0"//Новая зависимость Android X такова - реализация "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

16


Начиная с версии 26 библиотеки поддержки, вы можете использовать ProcessLifecycleOwner, просто добавьте его в свою зависимость, как описано здесь, например :

  зависимости {def lifecycle_version = "1.1.1  «//Реализация ViewModel и LiveData» android.arch.lifecycle: extensions: $ lifecycle_version//альтернативно - только жизненные циклы (без ViewModel или LiveData). //Библиотека поддержки зависит от этой облегченной реализации импорта "android.arch.lifecycle: runtime: $ lifecycle_version" annotationProcessor "android.arch.lifecycle: compiler: $ lifecycle_version"//используйте kapt для Kotlin}  

А затем просто запрашивайте ProcessLifecycleOwner всякий раз, когда вы хотите узнать состояние приложения, примеры:

 //Проверяем, находится ли приложение в backgroundProcessLifecycleOwner  .get (). getLifecycle (). getCurrentState () == Lifecycle.State.CREATED;//Проверяем, находится ли приложение в foregroundProcessLifecycleOwner.get (). getLifecycle (). getCurrentState (). isAtLeast (Lifecycle.State.STARTED);   

6


Начиная с Android API 16, есть простой способ проверить, находится ли приложение на переднем плане. Это может быть ненадежным, но никакие методы на Android не являются надежными. Этот метод достаточно хорош для использования, когда ваша служба получает обновление с сервера и должна решить, показывать ли уведомление или нет (потому что, если пользовательский интерфейс находится на переднем плане, пользователь заметит обновление без уведомления).

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

6


Ответ Idolon подвержен ошибкам и намного более сложен, хотя здесь повторяется проверка приложения Android на переднем плане или нет? и здесь Определение текущего приложения переднего плана из фоновой задачи или службы

Существует гораздо более простой подход:

На BaseActivity все действия расширить:

  protected static boolean isVisible = false;  @Override public void onResume () {super.onResume ();  setVisible (правда);  } @Override public void onPause () {super.onPause ();  setVisible (ложь);  }  

Всякий раз, когда вам нужно проверить , находится ли какое-либо из действий вашего приложения на переднем плане, просто проверьте isVisible () ;

Чтобы понять этот подход, проверьте этот ответ жизненного цикла параллельных действий: Жизненный цикл параллельных действий

9


Я попробовал рекомендованное решение, в котором используются 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" , хотя это также может быть реализовано с использованием простого настраиваемого класса.

  открытый класс Utilities расширяет Application {private static int stateCounter;  public void onCreate () {super. onCreate ();  stateCounter = 0;  }/** * @return true, если приложение работает в фоновом режиме * */public static boolean isApplicationOnBackground () {return stateCounter == 0;  }//вызывается для каждого действия onStart () public static void activityStarted () {stateCounter ++;  }//вызывается для каждого действия onStop () public static void activityStopped () {stateCounter--;  }}  

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

  @Overridepublic void onStart () {super.onStart ();  Utilities.activityStarted ();} @ Overridepublic void onStop () {Utilities.activityStopped ();  if (Utilities.isApplicationOnBackground ()) {//здесь вы должны проверить, работает ли ваше приложение в фоновом режиме} super.onStop ();}  

В этой логике есть 2 возможных случая:

  1. stateCounter = 0 : количество остановленных равно количеству запущенных действий, что означает, что приложение работает в фоновом режиме.
  2. stateCounter> 0 : количество запущенных больше, чем количество остановленных, что означает, что приложение работает на переднем плане.

Примечание: stateCounter будет означать, что число остановленных действий больше, чем запущено, что невозможно. Если вы столкнулись с этим случаем, это означает, что вы не увеличиваете/уменьшаете счетчик, как должны.

Вы готовы к работе. Вы должны проверить, находится ли ваше приложение в фоновом режиме внутри onStop () .

1


Нет никакого способа, за исключением того, что вы отслеживаете это самостоятельно, чтобы определить, видны ли какие-либо из ваших действий или нет. Возможно, вам стоит подумать о том, чтобы задать новый вопрос StackOverflow, объяснив, чего вы пытаетесь достичь с помощью взаимодействия с пользователем, чтобы мы могли предложить вам альтернативные идеи реализации.

6


Вы можете использовать ComponentCallbacks2, чтобы определить, находится ли приложение в фоновом режиме. Кстати, этот обратный вызов доступен только на уровне API 14 (Ice Cream Sandwich) и выше.

Вы получите вызов метода:

public abstract void onTrimMemory (int level)

, если уровень ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN , то приложение в фоновом режиме.

Вы можете реализовать этот интерфейс для activity , service и т. д..

  открытый класс MainActivity расширяет AppCompatActivity, реализует ComponentCallbacks2 {@Override public void onConfigurationChanged (final Configuration newConfig) {} @Override public void onLowMemory () {} @Override public void onTrimMemory (окончательный  int level) {if (level == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {//приложение в фоновом режиме}}}  

1


Основываясь на ответе @Cornstalks, чтобы включить пару полезных функций.

Дополнительные возможности:

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

App.java

  public cla  ss Приложение расширяет приложение {@Override public void onCreate () {super.onCreate ();  registerActivityLifecycleCallbacks (AppLifecycleHandler.getInstance ());  }}  

AppLifecycleHandler.java

  открытый класс AppLifecycleHandler реализует Application.ActivityLifecycleCallbacks {private int resumed;  частный int запущен;  частный финал String DebugName = "AppLifecycleHandler";  частное логическое значение isVisible = false;  частное логическое значение isInForeground = false;  частный статический экземпляр AppLifecycleHandler;  общедоступный статический AppLifecycleHandler getInstance () {если (экземпляр == ноль) {экземпляр = новый AppLifecycleHandler ();  } возвратный экземпляр;  } private AppLifecycleHandler () {} @Override public void onActivityCreated (действие действия, Bundle savedInstanceState) {} @Override public void onActivityDestroyed (действие действия) {} @Override public void onActivityResumed (действие действия) {++ возобновлено;  android.util.Log.w (DebugName, "onActivityResumed -> приложение находится на переднем плане:" + (возобновлено> 0) + "(" + activity.getClass () + ")");  setForeground ((возобновлено> 0));  } @Override public void onActivityPaused (активность активности) {--resumed;  android.util.Log.w (DebugName, "onActivityPaused -> приложение находится на переднем плане:" + (возобновлено> 0) + "(" + activity.getClass () + ")");  setForeground ((возобновлено> 0));  } @Override public void onActivitySaveInstanceState (действие действия, пакет outState) {} @Override public void onActivityStarted (действие действия) {++ начато;  android.util.Log.w (DebugName, "onActivityStarted -> приложение отображается:" + (запущено> 0) + "(" + activity.getClass () + ")");  setVisible ((началось> 0));  } @Override public void onActivityStopped (активность активности) {--started;  android.util.Log.w (DebugName, "onActivityStopped -> приложение отображается:" + (запущено> 0) + "(" + activity. getClass () + ")");  setVisible ((началось> 0));  } private void setVisible (boolean visible) {if (isVisible == visible) {//без изменений return;  }//видимость изменена isVisible = visible;  android.util.Log.w (DebugName, «Видимость приложения изменена -> приложение отображается:» + isVisible); //предпринимаем некоторые действия при изменении видимости} private void setForeground (boolean inForeground) {if (isInForeground == inForeground) {//без изменений return;  }//передний план изменен isInForeground = inForeground;  android.util.Log.w (DebugName, «Приложение на переднем плане изменено -> приложение на переднем плане:» + isInForeground); //предпринимаем некоторые действия при изменении состояния переднего плана} public static boolean isApplicationVisible () {return AppLifecycleHandler.getInstance (). start> 0;  } общедоступное статическое логическое значение isApplicationInForeground () {return AppLifecycleHandler.getInstance (). возобновлено> 0;  }}  


Лучшее решение, которое я придумал, использует таймеры.

Вы запустили таймер в onPause () и отменили тот же таймер в onResume (), есть 1 экземпляр Timer (обычно определенный в классе Application). Сам таймер настроен на запуск Runnable через 2 секунды (или любой другой интервал, который вы считаете подходящим), когда таймер срабатывает, вы устанавливаете флаг, отмечающий приложение как находящееся в фоновом режиме.

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

Это решение позволяет вам выполнять несколько действий на задний стек и не требует каких-либо разрешений для реализации.

Это решение хорошо работает, если вы также используете шину событий, поскольку ваш таймер может просто запускать событие, и различные части вашего приложения могут реагировать соответственно.

2


Если вы включите настройки разработчика «Не keep actvities »- проверять только количество созданных активностей недостаточно. Вы должны также проверить isSaveInstanceState . Мой собственный метод isApplicationRunning () проверяет, запущено ли приложение для Android:

Вот мой рабочий код:

   открытый класс AppLifecycleService реализует Application.ActivityLifecycleCallbacks {private int created;  частное логическое значение isSaveInstanceState;  частный статический экземпляр AppLifecycleService;  приватная конечная статическая строка TAG = AppLifecycleService.class. getName ();  общедоступный статический AppLifecycleService getInstance () {if (instance == null) {instance = new AppLifecycleService ();  } возвратный экземпляр;  } общедоступное статическое логическое значение isApplicationRunning () {логическое значение isApplicationRunning = true;  если (getCountCreatedActvities () == 0 &&! isSaveInstanceState ()) {isApplicationRunning = false;  } return isApplicationRunning;  } общедоступное статическое логическое значение 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 (действие активности, 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 (активность действий) {} @Override public void onActivityStopped (активность действий) {}}   


Чтобы совмещать то, что сказали CommonsWare и Key, вы могли бы, возможно, расширите класс Application и сделайте так, чтобы все ваши действия вызывали это в своих методах onPause/onResume. Это позволит вам узнать, какие действия видны, но с этим, вероятно, можно было бы справиться лучше.

Не могли бы вы уточнить, что именно вы имеете в виду? Когда вы говорите, что работает в фоновом режиме, вы имеете в виду, что ваше приложение все еще находится в памяти, даже если оно в данный момент не отображается на экране? Приходилось ли вам использовать службы как более надежный способ управления вашим приложением, когда оно не в фокусе?

3


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

Сначала я создаю интерфейс, в котором есть все методы для отслеживания жизненного цикла действий:

   открытый интерфейс ActivityLifecycleCallbacks {public void onActivityStopped (активность активности);  public void onActivityStarted (активность активности);  public void onActivitySaveInstanceState (активность активности, Bundle outState);  public void onActivityResumed (активность активности);  public void onActivityPaused (активность активности);  public void onActivityDestroyed (активность активности);  public void onActivityCreated (Activity activity, Bundle savedInstanceState);}  

Во-вторых, я реализовал этот интерфейс в классе своего приложения:

   открытый класс MyApplication расширяет Приложение реализует my.package.ActivityLifecycleCallbacks {@Override public void onCreate () {super. onCreate ();  } @Override public void onActivityStopped (Activity activity) {Log.i («Отслеживание активности остановлено», activity.getLocalClassName ());  } @Override public void onActivityStarted (Activity activity) {Log.i («Отслеживание активности начато», activity.getLocalClassName ());  } @Override public void onActivitySaveInstanceState (Activity activity, Bundle outState) {Log.i («Отслеживание активности SaveInstanceState», activity.getLocalClassName ());  } @Override public void onActivityResumed (Activity activity) {Log.i («Отслеживание активности возобновлено», activity.getLocalClassName ());  } @Override public void onActivityPaused (Activity activity) {Log.i («Отслеживание активности приостановлено», activity.getLocalClassName ());  } @Override public void onActivityDestroyed (Activity activity) {Log.i («Отслеживание активности уничтожено», activity.getLocalClassName ());  } @Override public void onActivityCreated (Activity activity, Bundle savedInstanceState) {Log.i («Отслеживание созданной активности», activity.getLocalClassName ());  }}  

В-третьих, я создаю класс, который расширяется от SherlockActivity:

  открытый класс MySherlockActivity расширяет SherlockActivity {protected MyApplication  nMyApplication;  protected void onCreate (Bundle savedInstanceState) {//TODO Заглушка автоматически сгенерированного метода super.onCreate (savedInstanceState);  nMyApplication = (MyApplication) getApplication ();  nMyApplication.onActivityCreated (это, savedInstanceState);  } protected void onResume () {//TODO Заглушка автоматически сгенерированного метода nMyApplication.onActivityResumed (this);  super.onResume ();  } @Override protected void onPause () {//TODO Заглушка автоматически сгенерированного метода nMyApplication.onActivityPaused (this);  super.onPause ();  } @Override protected void onDestroy () {//ЗАДАНИЕ Автоматически сгенерированная заглушка метода nMyApplication.onActivityDestroyed (this);  super.onDestroy ();  } @Override protected void onStart () {nMyApplication.onActivityStarted (это);  super.onStart ();  } @Override protected void onStop () {nMyApplication.onActivityStopped (это);  super.onStop ();  } @Override protected void onSaveInstanceState (Bundle outState) {nMyApplication.onActivitySaveInstanceState (это, outState);  super.onSaveInstanceState (outState);  }}  

В-четвертых, все классы, которые являются наследниками SherlockActivity, я заменил на MySherlockActivity:

  открытый класс MainActivity расширяет MySherlockActivity {@  Переопределить защищенную пустоту onCreate (Bundle savedInstanceState) {super.onCreate (savedInstanceState);  setContentView (R.layout.main);  }}  

Теперь в logcat вы увидите журналы, запрограммированные в реализации интерфейса, созданной в MyApplication.


Официальные документы:

Система различает приложения переднего плана и фоновые.. (Определение фона для целей ограничения службы отличается от определения, используемого для управления памятью; приложение может находиться в фоновом режиме в отношении управления памятью , но на переднем плане в отношении его возможность запускать службы.) Приложение считается находящимся на переднем плане, если выполняется одно из следующих условий:

  1. Оно имеет видимую активность, независимо от того, запущена она или приостановлена .
  2. У него есть служба переднего плана.
  3. Другое приложение переднего плана подключается к приложению либо путем привязки к одной из его служб, либо путем использования одной из своих контент-провайдеры. Например, приложение находится на переднем плане, если другое приложение привязывается к его:
    • IME
    • Служба обоев
    • Прослушиватель уведомлений
    • Голосовая или текстовая служба

Если ни одно из этих условий не выполняется, приложение считается работающим в фоновом режиме .


Единственное правильное решение:

MainActivity.java:

  открытый класс MainActivity расширяет AppCompatActivity {@Override protected void onCreate (Bundle savedInstanceState) {MyApp.mainActivity = this;  super.onCreate (savedInstanceState);  ...}  

MyApp.java:

  открытый класс MyApp extends Приложение реализует LifecycleObserver {public static MainActivity mainActivity = null;  @Override public void onCreate () {super.onCreate ();  ProcessLifecycleOwner.get (). GetLifecycle (). AddObserver (это);  } @OnLifecycleEvent (Lifecycle.Event.ON_STOP) void onAppBackgounded () {//приложение в фоновом режиме if (mainActivity! = Null) {...}} @OnLifecycleEvent (Lifecycle.Event.ON_START) void onAppForegounded () {//приложение  на переднем плане if (mainActivity! = null) {...}}}  

1


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


Поскольку это не уже не упомянуто, я предлагаю читателям изучить ProcessLifecycleOwner, доступный через компоненты архитектуры Android


Другое решение для этого старого сообщения (для тех, кто может помочь):


    

  открытый класс BaseApplication расширяет приложение {частный класс Status {public boolean isVisible = true;  публичное логическое значение isFocused = true;  } приватные действия Map ;  @Override public void onCreate () {activity = new HashMap  ();  super.onCreate ();  } private boolean hasVisibleActivity () {для (Статус статуса: activity.values ​​()) if (status.isVisible) return true;  вернуть ложь;  } частное логическое значение hasFocusedActivity () {for (Статус статуса: activity.values ​​()) if (status.isFocused) return true;  вернуть ложь;  } public void onActivityCreate (действие активности, логическое isStarting) {если (isStarting && activity.isEmpty ()) onApplicationStart ();  activity.put (активность, новый статус ());  } public void onActivityStart (активность активности) {если (! hasVisibleActivity () &&! hasFocusedActivity ()) onApplicationForeground ();  activity.get (активность) .isVisible = true;  } public void onActivityWindowFocusChanged (Activity activity, логическое hasFocus) {activity.get (activity) .isFocused = hasFocus;  } public void onActivityStop (активность, логическое isFinishing) {activity.get (активность) .isVisible = false;  если (! isFinishing &&! hasVisibleActivity () &&! hasFocusedActivity ()) onApplicationBackground ();  } public void onActivityDestroy (Activity activity, логическое isFinishing) {activity.remove (activity);  если (isFinishing && activity.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 ");}}  

  открытый класс MyActivity  расширяет BaseActivity {...}  

  открытый класс BaseActivity расширяет Activity {частное приложение BaseApplication;  @Override protected void onCreate (состояние пакета) {application = (BaseApplication) getApplication ();  application.onActivityCreate (это, состояние == null);  super.onCreate (состояние);  } @Override protected void onStart () {application.onActivityStart (это);  super.onStart ();  } @Override public void onWindowFocusChanged (логическое hasFocus) {application.onActivityWindowFocusChanged (this, hasFocus);  super.onWindowFocusChanged (hasFocus);  } @Override protected void onStop () {application.onActivityStop (это, isFinishing ());  super.onStop ();  } @Override protected void onDestroy () {application.onActivityDestroy (это, isFinishing ());  super.onDestroy ();  }}  


См. комментарий в функции onActivityDestroyed.

Работает с целевой версией SDK 14>:

  import android.app.Activity; import android.  .app.Application; импорт android.os.Bundle; импорт android.util. Журнал; открытый класс AppLifecycleHandler реализует Application.ActivityLifecycleCallbacks {public static int active = 0;  @Override public void onActivityStopped (активность активности) {Log.i ("Отслеживание активности остановлено", activity.getLocalClassName ());  активный -;  } @Override public void onActivityStarted (Activity activity) {Log.i («Отслеживание активности начато», activity.getLocalClassName ());  активный ++;  } @Override public void onActivitySaveInstanceState (Activity activity, Bundle outState) {Log.i («Отслеживание активности SaveInstanceState», activity.getLocalClassName ());  } @Override public void onActivityResumed (Activity activity) {Log.i («Отслеживание активности возобновлено», activity.getLocalClassName ());  активный ++;  } @Override public void onActivityPaused (Activity activity) {Log.i («Отслеживание активности приостановлено», activity.getLocalClassName ());  активный -;  } @Override public void onActivityDestroyed (Activity activity) {Log.i («Отслеживание активности уничтожено», activity.getLocalClassName ());  активный -; //если здесь active var становится равным нулю, приложение закрывается или работает в фоновом режиме if (active == 0) {...}} @Override public void onActivityCreated (Activity activity, Bundle savedInstanceState) {Log.i ("Отслеживание активности  Создано », activity.getLocalClassName ());  активный ++;  }}  


Вы должны использовать общие предпочтения для хранить собственность и действовать в соответствии с ней, используя привязку службы из ваших действий. Если вы используете только привязку (это никогда не используется startService), тогда ваша служба будет работать только тогда, когда вы привяжете к ней (привязать onResume и отвязать onPause), что заставит ее работать только на переднем плане, и если вы действительно хотите работать над фон вы можете использовать обычную службу запуска и остановки.


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

Я просто представляю свое решение по-своему.
Я добиваюсь этого, используя поле «важность» класса RunningAppProcessInfo в каждом действии onStop в моем приложении, которого можно просто достичь, предоставив BaseActivity для расширения других действий, который реализует onStop метод проверки значения «важности». Вот код:

  общедоступное статическое логическое значение isAppRunning (Контекст контекста) {ActivityManager activityManager = (ActivityManager) context .getSystemService (Context.ACTIVITY_SERVICE);  Список  appProcesses = activityManager .getRunningAppProcesses ();  для (RunningAppProcessInfo appProcess: appProcesses) {if (appProcess.processName.equals (context.getPackageName ())) {if (appProcess.importance! = RunningAppProcessInfo. IMPORTANCE_PERCEPTIBLE) {return true;  }}} return false;}  

1


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

Короче говоря, ваша активность не дольше отображается после вызова onStop () .

5


А как насчет использования getApplicationState (). isInForeground ()?


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

Когда люди спрашивают об SO как взаимодействовать между Service и Activity , я обычно советую использовать LocalBroadcastMa nager.


Почему?

Ну, цитируя документы:

  • Вы знаете, что данные, которые вы транслируете, не покидают ваше приложение, поэтому не нужно беспокоиться об утечке личных данных.

  • Другие приложения не могут отправлять эти широковещательные сообщения в ваше приложение, поэтому вам не нужно беспокоиться о наличии дыр в безопасности, которые они могут использовать.

  • Это более эффективно, чем отправка глобальной трансляции через систему.

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

  • Не требует внешних библиотек
  • Код минимален
  • Быстро реализовать и понять
  • Никаких настраиваемых самореализуемых обратных вызовов/ultra-singleton/intra-processpattern вообще …
  • Нет сильных ссылок на Активность , Приложение , …

Описание

Итак, вы хотите проверить, есть ли в A Активность сейчас на переднем плане. Обычно вы делаете это в Service или в своем классе Application .

Это означает, что ваша Activity объекты становятся отправителями сигнала (я включен/я выключен). Ваш Сервис , с другой стороны, становится Receiver .

Есть два моменты, когда ваша Activity сообщает вам, идет ли она на переднем плане или в фоновом (да, только два … не 6).

Когда

# code> Activity переходит на передний план, запускается метод onResume () (также вызывается после onCreate () ).

Когда Activity идет сзади, вызывается onPause () .

Это моменты, когда ваша Activity должна отправлять сигнал вашей Service для описания своего состояния.

В случае нескольких действий Activity помните, что действие Activity сначала переходит в фоновый режим, а затем появляется другое. передний план.

Таким образом, ситуация будет такой: *

  Activity1 - send -> Signal: OFFActivity2 - send -> Signal  : ON  

Service / Application просто продолжит прослушивание этих сигналов и будет действовать соответственно.


Код (TL; DR)

Ваша Служба должна реализовывать BroadcastReceiver для прослушивания сигналов.

  this.localBroadcastReceiver = new BroadcastReceiver () {@Override public void onReceive (Контекст контекста, намерение намерения)  {//получены данные, если активность включена/выключена}} public static final IntentFilter SIGNAL_FILTER = new IntentFilter ("com.you.yourapp.MY_SIGNAL")  

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

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

Отмените регистрацию в Service :: onDestroy ()

  @Overrideprotected void onDestroy () {//Я мертв, мне больше не нужно ничего слушать.  LocalBroadcastManager.getInstance (getApplicationContext ()). UnregisterReceiver (this.localBroadcastReceiver);}  

Теперь ваши Activity должны сообщать свое состояние. /p>

В Activity::onResume()

  Intent intent = new Intent (); intent.setAction (  SomeActivity.SIGNAL_FILTER); //помещаем логическое значение ON в намерение LocalBroadcastManager.getInstance (getApplicationContext ()). sendBroadcast (intent);  

В Activity :: onPause ()

  Намерение намерение = новое намерение (); intent.setAction (SomeActivity.SIGNAL_FILTER); //выключить логическое значение в намерении LocalBroadcastManager.getInstance (getApplicationContext ()). sendBroadcast (intent);  

Очень, очень распространенная ситуация

Разработчик: я хочу отправить данные из моей службы и обновить Activity . Как проверить, находится ли Activity на переднем плане?

Обычно нет необходимости проверять, есть ли Activity находится на переднем плане или нет. Просто отправьте данные через LocalBroadcastManager из своей службы . Если действие Activity включено, оно будет реагировать и действовать.

В этой очень распространенной ситуации Service становится отправитель, а Activity реализует BroadcastReceiver .

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

Определите поведение Receiver в onReceive () (обновите ListView, сделайте это, сделайте то, …).

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

В случае нескольких активностей Activity , какой бы Activity ни был включен, ответит (если они также реализуют Receiver ).

Если все в фоновом режиме, никто не ответит, и сигнал просто потеряется.

Отправьте данные из службы через Intent (см. код выше), указав идентификатор сигнала.


  • Кроме для многооконной поддержки. Это может быть сложно (при необходимости проверьте) …

  fun isAppInForeground (): Boolean {val activityManager = getSystemService (Context.ACTIVITY_SERVICE) as ActivityManager?: вернуть 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 без прямого доступа к приложению. Для меня я был в фоновом потоке, только что получил push-уведомление о новом сообщении чата, и хочу отображать системное уведомление только в том случае, если экран чата не находится на переднем плане.

Используя ActivityLifecycleCallbacks , рекомендованный в других ответах, я создал небольшой служебный класс, в котором содержится логика того, является ли MyActivity находится на переднем плане или нет.

  class MyActivityMonitor (context: Context): Application.ActivityLifecycleCallbacks {private var isMyActivityInForeground = falseinit {(context.applicationContext как приложение). registerActivityLifecycleCallbacks (this)} fun isMyActivityForeground () = isMyActivityInForegroundoverride fun onActivityPaused (activity: Activity?) {if (activity is MyActivity) {isMyActivityInForeground = false}} переопределить fun onActivityResumed (activity: Activity?) {if (activity isMoregroundyactivities)  = true}}  

}


Этот код будет проверять передний план и background в любом состоянии:

Код Java :

  частное статическое логическое значение isApplicationForeground (контекст контекста) {KeyguardManager keyguardManager = (KeyguardManager) context.getSystemService (Context.KEYGUARD_SERVICE);  если (keyguardManager.isKeyguardLocked ()) {вернуть ложь;  } int myPid = Process.myPid ();  ActivityManager activityManager = (ActivityManager) context.getSystemService (Context.ACTIVITY_SERVICE);  Список  список;  if ((list = activityManager.getRunningAppProcesses ())! = null) {для (ActivityManager.RunningAppProcessInfo aList: list) {ActivityManager.RunningAppProcessInfo информация;  if ((info = aList) .pid == myPid) {return info.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;  }}} return false;}  

Код Kotlin:

  частное развлечение isApplicationForeground (  context: Context): Boolean {val keyguardManager = context.getSystemService (Context.KEYGUARD_SERVICE) as KeyguardManager if (keyguardManager.isKeyguardLocked) {return false} val myPid = Process.myPid () val activityManager = context.getSystemSERVICE_  ActivityManager var list: List  if (activityManager.runningAppProcesses.also {list = it}! = Null) {for (aList in list) {var info: ActivityManager.RunningAppProcessInfo if (aList.also {info = it}  .pid == myPid) {return info.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND}}} return false}  


В своих действиях onResume и onPause я пишу логическое значение isVisible для SharedPrefences.

  SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences (это);  Редактор editor = sharedPrefs.edit ();  editor.putBoolean ("видимый", ложь);  editor.commit ();  

И при необходимости прочтите его в другом месте с помощью,

 //Показывать всплывающее уведомление, если приложение  не отображается (т. е. в фоновом режиме. Не работает и т. д.) SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences (context);  if (! sharedPrefs.getBoolean ("visible", true)) {...}  

Может быть, не элегантно, но у меня это работает …


Возможно, уже слишком поздно отвечать, но если кто-то придет в гости, тогда вот решение, которое я предлагаю. Причины, по которым приложение хочет знать, что оно находится в фоновом режиме или выходит на передний план, может быть много, некоторые из них: 1. Показывать тосты и уведомления, когда пользователь находится в BG. 2. Для выполнения некоторых задач впервые пользователь переходит из BG, например, опрос, перерисовка и т. Д.

Решение от Idolon и других требует заботится о первой части, но не о второй. Если в вашем приложении есть несколько действий, и пользователь переключается между ними, то к тому времени, когда вы находитесь во втором действии, видимый флаг будет ложным. Таким образом, его нельзя использовать детерминированно.

Я сделал то, что было предложено CommonsWare: «Если Служба определяет, что нет видимых действий, , и он остается таким для некоторой суммы времени , остановите передачу данных в следующей логической точке остановки «.

Строка, выделенная жирным шрифтом, важна, и ее можно использовать для достижения второго пункта. Итак, что я делаю, это как только я получаю onActivityPaused (), не меняю видимое значение на false напрямую, вместо этого имею таймер на 3 секунды (это максимум, на который должно быть запущено следующее действие), и если нет onActivityResumed ( ) вызовите в следующие 3 секунды, измените visible на false. Аналогично в onActivityResumed (), если есть таймер, я его отменяю. Подводя итог, видимое становится isAppInBackground.

Извините, не могу скопировать-вставить код …

Оцените статью
clickpad.ru
Добавить комментарий