Под фоном я подразумеваю, что ни одно из действий приложения в настоящее время не видно пользователю?
Под фоном я подразумеваю, что ни одно из действий приложения в настоящее время не видно пользователю?
Ответы:
Есть несколько способов определить, работает ли ваше приложение в фоновом режиме, но только один из них является полностью надежным:
Правильное решение (кредиты идут к Дэну , 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). Вы можете использовать их, чтобы отслеживать, видна ли активность вашего приложения в данный момент для пользователя. Проверьте ответ Cornstalks ниже для деталей.
Неправильное, которое
я использовал, чтобы предложить следующее решение:
Вы можете обнаружить в настоящее время передний план / фоновое приложение с
ActivityManager.getRunningAppProcesses()
которое возвращает списокRunningAppProcessInfo
записей. Чтобы определить, находится ли ваше приложение на переднем плане, проверьтеRunningAppProcessInfo.importance
поле на равенствоRunningAppProcessInfo.IMPORTANCE_FOREGROUND
while иRunningAppProcessInfo.processName
имя пакета вашего приложения.Кроме того, если вы позвоните
ActivityManager.getRunningAppProcesses()
из потока пользовательского интерфейса приложения, он вернет значениеIMPORTANCE_FOREGROUND
для вашей задачи независимо от того, находится ли он на переднем плане или нет. Вызовите его в фоновом потоке (например, черезAsyncTask
), и он вернет правильные результаты.
Хотя это решение может работать (и оно действительно работает большую часть времени), я настоятельно рекомендую воздержаться от его использования. И вот почему. Как написала Дайан Хэкборн :
Эти API-интерфейсы предназначены не для того, чтобы приложения основывали свой поток пользовательского интерфейса, а для того, чтобы показывать пользователю запущенные приложения, диспетчер задач или тому подобное.
Да, в памяти хранится список этих вещей. Тем не менее, он отключен в другом процессе, управляемом потоками, работающими отдельно от вас, и вы не можете рассчитывать на то, что (a) увидите вовремя для принятия правильного решения или (b) получите непротиворечивую картину к тому времени, когда вы вернетесь. Кроме того, решение о том, к какому «следующему» действию перейти, всегда принимается в момент, когда должно произойти переключение, и только в той точной точке (где состояние действия кратко блокируется для выполнения переключения), что мы на самом деле точно знаю, что будет дальше.
И реализация и глобальное поведение здесь не гарантируется, что останутся такими же в будущем.
Хотелось бы, чтобы я прочитал это до того, как опубликовал ответ на SO, но, надеюсь, еще не поздно признать мою ошибку.
Другое неправильное решение, которое библиотека
Droid-Fu упоминает в одном из ответов, использует ActivityManager.getRunningTasks
для своего isApplicationBroughtToBackground
метода. Посмотрите комментарий Дайанны выше и тоже не используйте этот метод.
OnStop
запросу isActivityVisible
.
Ответ user1269737 - правильный (одобренный Google / Android) способ сделать это . Идите, прочитайте их ответ и дайте им +1.
Я оставлю свой оригинальный ответ здесь ради потомков. Это было лучшее из доступных в 2012 году, но теперь Android имеет соответствующую поддержку для этого.
Ключ использует ActivityLifecycleCallbacks
(обратите внимание, что для этого требуется Android API уровня 14 (Android 4.0)). Просто проверьте, равно ли количество остановленных действий количеству запущенных действий. Если они равны, ваша заявка в фоновом режиме. Если есть еще запущенные действия, ваше приложение все еще отображается. Если действия возобновлены, а не приостановлены, приложение не только отображается, но и на переднем плане. Существуют 3 основных состояния, в которых ваша деятельность может находиться, затем: видимая и на переднем плане, видимая, но не на переднем плане, и не видимая и не на переднем плане (то есть на заднем плане).
Отличная вещь в этом методе состоит в том, что он не имеет асинхронных проблем 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 ().
Ключевым моментом здесь является «поддерживать процесс вашей активности …». Если эта ситуация с нехваткой памяти когда-либо будет достигнута, ваш процесс фактически будет уничтожен (а не только ваша активность). Это означает, что этот метод проверки на фоновую целостность все еще действителен, потому что а) вы все равно не можете проверить фоновую обработку, если ваш процесс убит, и б) если ваш процесс запускается снова (потому что создается новое действие), член переменные (статические или нет) для 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 - не хак, как предыдущие решения. Используйте 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
}
}
Ява:
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
}
}
в приложении
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
чтобы обнаружить, что служба работает в фоновом режиме.
Ответ Идолона подвержен ошибкам и гораздо более сложный, хотя повторять здесь проверить приложение для Android на переднем плане или нет? и тут Определение текущего приоритетного приложения из фоновой задачи или службы
Существует гораздо более простой подход:
На BaseActivity, что все действия расширяются:
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 и многие другие, но они не сработали, как ожидалось. Благодаря Sarge я нашел довольно простое и понятное решение, которое я описываю ниже.
Ключом решения является тот факт, что если у нас есть 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, чтобы определить, находится ли приложение в фоновом режиме. Кстати, этот обратный вызов доступен только в API Level 14 (Ice Cream Sandwich) и выше.
Вы получите вызов метода:
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 экземпляр Timer (обычно определяется в классе 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, вы можете расширить класс Application и сделать так, чтобы все ваши действия вызывали его в своих методах 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.
Система различает фоновые и фоновые приложения. (Определение фона для целей ограничения услуг отличается от определения, используемого управлением памятью; приложение может находиться в фоновом режиме в том, что касается управления памятью , но на переднем плане - в отношении его способности запускать службы.) считается находящимся на переднем плане, если выполняется любое из следующих условий:
Если ни одно из этих условий не выполняется, приложение считается в фоновом режиме.
Другое решение для этого старого поста (для тех, которые могут помочь):
<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
через канал 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
}
Ни один из ответов не вполне соответствовал конкретному случаю, если вы выяснили, находится ли конкретная активность в forground и если вы SDK без прямого доступа к Приложению. Для меня я был в фоновом потоке, только что получил push-уведомление о новом сообщении чата и хочу отображать системное уведомление, только если экран чата не находится на переднем плане.
Используя это 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 для 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, например, опрос, перерисовка и т. Д.
Решение от Idolon и других заботится о первой части, но не о второй. Если в вашем приложении есть несколько действий, и пользователь переключается между ними, то к тому времени, когда вы выполняете второе действие, видимый флаг станет ложным. Поэтому его нельзя использовать детерминистически.
Я сделал что-то, что было предложено CommonsWare: «Если Служба определяет, что никаких видимых действий нет, и она остается такой в течение некоторого времени , остановите передачу данных на следующей логической остановке».
Строка, выделенная жирным шрифтом, важна, и ее можно использовать для достижения второго пункта. Так что я делаю, как только я получаю onActivityPaused (), я не изменяю видимое на false напрямую, вместо этого имею таймер в 3 секунды (то есть максимум, что должно быть запущено следующее действие), и если нет onActivityResumed ( ) в течение следующих 3 секунд измените видимость на ложь. Аналогично в onActivityResumed (), если есть таймер, я отменяю его. Подводя итог, видимым становится isAppInBackground.
Извините не могу скопировать и вставить код ...
Я хотел бы рекомендовать вам использовать другой способ сделать это.
Я предполагаю, что вы хотите показать начальный экран во время запуска программы, если она уже запущена в бэкэнде, не показывайте ее.
Ваше приложение может непрерывно записывать текущее время в определенный файл. Когда ваше приложение запускается, проверьте последнюю метку времени, если current_time-last_time> диапазон времени, указанный вами для записи последнего времени, это означает, что ваше приложение остановлено, либо уничтожено системой, либо самим пользователем.