Я делаю уведомление в строке состояния в своем приложении для Android, которое запускается c2dm. Я не хочу отображать уведомление, если приложение запущено. Как определить, запущено ли приложение и находится ли оно на переднем плане?
android
android-activity
Эндрю Томас
источник
источник
Ответы:
Создайте глобальную переменную, например,
private boolean mIsInForegroundMode;
и присвойтеfalse
значение вonPause()
иtrue
значение вonResume()
.Образец кода:
private boolean mIsInForegroundMode; @Override protected void onPause() { super.onPause(); mIsInForegroundMode = false; } @Override protected void onResume() { super.onResume(); mIsInForegroundMode = true; } // Some function. public boolean isInForeground() { return mIsInForegroundMode; }
источник
Кроме того, вы можете проверить,
ActivityManager
какие задачи выполняются поgetRunningTasks
методу. Затем проверьте первую задачу (задача на переднем плане) в возвращенном Списке задач, если это ваша задача.Вот пример кода:
public Notification buildNotification(String arg0, Map<String, String> arg1) { ActivityManager activityManager = (ActivityManager) appContext.getSystemService(Context.ACTIVITY_SERVICE); List<RunningTaskInfo> services = activityManager .getRunningTasks(Integer.MAX_VALUE); boolean isActivityFound = false; if (services.get(0).topActivity.getPackageName().toString() .equalsIgnoreCase(appContext.getPackageName().toString())) { isActivityFound = true; } if (isActivityFound) { return null; } else { // write your code to build a notification. // return the notification you built here } }
И не забудьте добавить
GET_TASKS
разрешение в файл manifest.xml , чтобы иметь возможность запускатьgetRunningTasks()
метод в приведенном выше коде:<uses-permission android:name="android.permission.GET_TASKS" />
p / s: Если вы согласны с этим, обратите внимание, что это разрешение теперь устарело.
источник
toString()
строки, возвращаемой функцией ,getPackageName()
является избыточным. Кроме того, поскольку нас интересует только первая задача, которую возвращаетgetRunningTasks()
, мы можем передать1
вместоInteger.MAX_VALUE
.Это довольно старый пост, но все еще актуальный. Принятое выше решение может работать, но неверно. Как писала Дайан Хакборн:
Эти API предназначены не для того, чтобы приложения могли основывать свой поток пользовательского интерфейса, а для того, чтобы делать такие вещи, как показывать пользователю запущенные приложения или диспетчер задач или что-то подобное.
Да, в памяти есть список этих вещей. Однако он отключен в другом процессе, управляемом потоками, работающими отдельно от вашего, и не является чем-то, на что вы можете рассчитывать (а) вовремя увидеть, чтобы принять правильное решение, или (б) иметь согласованную картину к тому времени, когда вы вернетесь. Кроме того, решение о том, к какому «следующему» действию следует перейти, всегда принимается в той точке, где должно произойти переключение, и только в этой точной точке (где состояние активности на короткое время заблокировано для переключения), мы на самом деле знаю по таким, что будет дальше.
И реализация и глобальное поведение здесь не гарантируется в будущем.
Правильное решение - реализовать: ActivityLifeCycleCallbacks .
Для этого в основном нужен класс приложения, и там можно установить обработчик для определения состояния ваших действий в приложении.
источник
onPause
иonResume
метода, использованного в принятом ответе?Как говорит Виней, вероятно, лучшим решением (для поддержки новых версий Android, 14+) является использование
ActivityLifecycleCallbacks
вApplication
реализации класса.package com.telcel.contenedor.appdelegate; import android.app.Activity; import android.app.Application.ActivityLifecycleCallbacks; import android.os.Bundle; /** Determines global app lifecycle states. * * The following is the reference of activities states: * * The <b>visible</b> lifetime of an activity happens between a call to onStart() * until a corresponding call to onStop(). During this time the user can see the * activity on-screen, though it may not be in the foreground and interacting with * the user. The onStart() and onStop() methods can be called multiple times, as * the activity becomes visible and hidden to the user. * * The <b>foreground</b> lifetime of an activity happens between a call to onResume() * until a corresponding call to onPause(). During this time the activity is in front * of all other activities and interacting with the user. An activity can frequently * go between the resumed and paused states -- for example when the device goes to * sleep, when an activity result is delivered, when a new intent is delivered -- * so the code in these methods should be fairly lightweight. * * */ public class ApplicationLifecycleManager implements ActivityLifecycleCallbacks { /** Manages the state of opened vs closed activities, should be 0 or 1. * It will be 2 if this value is checked between activity B onStart() and * activity A onStop(). * It could be greater if the top activities are not fullscreen or have * transparent backgrounds. */ private static int visibleActivityCount = 0; /** Manages the state of opened vs closed activities, should be 0 or 1 * because only one can be in foreground at a time. It will be 2 if this * value is checked between activity B onResume() and activity A onPause(). */ private static int foregroundActivityCount = 0; /** Returns true if app has foreground */ public static boolean isAppInForeground(){ return foregroundActivityCount > 0; } /** Returns true if any activity of app is visible (or device is sleep when * an activity was visible) */ public static boolean isAppVisible(){ return visibleActivityCount > 0; } public void onActivityCreated(Activity activity, Bundle bundle) { } public void onActivityDestroyed(Activity activity) { } public void onActivityResumed(Activity activity) { foregroundActivityCount ++; } public void onActivityPaused(Activity activity) { foregroundActivityCount --; } public void onActivitySaveInstanceState(Activity activity, Bundle outState) { } public void onActivityStarted(Activity activity) { visibleActivityCount ++; } public void onActivityStopped(Activity activity) { visibleActivityCount --; } }
И в
onCreate()
методе применения :registerActivityLifecycleCallbacks(new ApplicationLifecycleManager());
Тогда
ApplicationLifecycleManager.isAppVisible()
илиApplicationLifecycleManager.isAppInForeground()
будет использоваться, чтобы узнать желаемое состояние.источник
Начиная с API 16, это можно сделать так:
static boolean shouldShowNotification(Context context) { RunningAppProcessInfo myProcess = new RunningAppProcessInfo(); ActivityManager.getMyMemoryState(myProcess); if (myProcess.importance != RunningAppProcessInfo.IMPORTANCE_FOREGROUND) return true; KeyguardManager km = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE); // app is in foreground, but if screen is locked show notification anyway return km.inKeyguardRestrictedInputMode(); }
источник
К вашему сведению, если вы используете решение Gadenkan (что отлично !!), не забудьте добавить
<uses-permission android:name="android.permission.GET_TASKS" />
в манифест.
источник
Немного доработанная версия решения Гаденкана . Поместите в него любое Activity или, возможно, базовый класс для всех ваших Activity.
protected boolean isRunningInForeground() { ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE); List<ActivityManager.RunningTaskInfo> tasks = manager.getRunningTasks(1); if (tasks.isEmpty()) { return false; } String topActivityName = tasks.get(0).topActivity.getPackageName(); return topActivityName.equalsIgnoreCase(getPackageName()); }
Чтобы иметь возможность звонить
getRunningTasks()
, вам необходимо добавить это в свойAndroidManifest.xml
:<uses-permission android:name="android.permission.GET_TASKS"/>
Обратите внимание на то, что
ActivityManager.getRunningTasks()
говорит Javadoc:Обновление (февраль 2015 г.)
Обратите внимание, что
getRunningTasks()
это устарело на уровне API 21 !Так что то, что я написал ранее, даже более актуально:
Во многих случаях вы, вероятно, сможете найти лучшее решение. Например, выполнение чего-либо в
onPause()
иonResume()
, возможно, в BaseActivity для всех ваших действий.(В нашем случае мы не хотели запускать офлайн-оповещение, если мы не на переднем плане, поэтому в BaseActivity
onPause()
мы просто отказываемся от подписки на RxJava,Subscription
прослушивая сигнал «перешел в автономный режим».)источник
После ответа Гаденкана мне нужно было что-то вроде этого, чтобы я мог определить, работает ли мое приложение на переднем плане, но мне нужно было что-то, что было бы для всего приложения и не требовало от меня установки / снятия флагов во всем моем приложении.
Код Гаденкана в значительной степени попал в точку, но он был не в моем стиле, и мне казалось, что он может быть более аккуратным, поэтому в моем приложении он сократился до этого.
if (!context.getPackageName().equalsIgnoreCase(((ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE)).getRunningTasks(1).get(0).topActivity.getPackageName())) { // App is not in the foreground }
(Примечание: вы можете просто удалить!, Если хотите, чтобы проверка работала наоборот)
Хотя при таком подходе нужно
GET_TASKS
разрешение.источник
Начиная с версии 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
делать запросы всякий раз, когда хотите проверить состояние приложения, например, чтобы проверить, запущено ли приложение на переднем плане, вам просто нужно сделать это:boolean isAppInForeground = ProcessLifecycleOwner.get().getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED); if(!isAppInForeground) //Show Notification in status bar
источник
implementation 'androidx.lifecycle:lifecycle-process:2.2.0'
в своем проекте gradle.Основываясь на различных ответах и комментариях, вот более встроенная версия, которую вы можете добавить во вспомогательный класс:
public static boolean isAppInForeground(Context context) { List<RunningTaskInfo> task = ((ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE)) .getRunningTasks(1); if (task.isEmpty()) { return false; } return task .get(0) .topActivity .getPackageName() .equalsIgnoreCase(context.getPackageName()); }
Как упоминалось в других ответах, вам необходимо добавить следующее разрешение к вашему
AndroidManifest.xml
.<uses-permission android:name="android.permission.GET_TASKS"/>
источник
Я хотел бы добавить, что более безопасный способ сделать это, чем проверка того, находится ли ваше приложение в фоновом режиме перед созданием уведомления, - это просто отключить и включить широковещательный приемник onPause () и onResume () соответственно.
Этот метод дает вам больше контроля над реальной логикой приложения и вряд ли изменится в будущем.
@Override protected void onPause() { unregisterReceiver(mHandleMessageReceiver); super.onPause(); } @Override protected void onResume() { super.onResume(); registerReceiver(mHandleMessageReceiver, new IntentFilter(DISPLAY_MESSAGE_ACTION)); }
источник
Я нашел более простой и точный способ проверить, находится ли приложение на переднем плане или в фоновом режиме, сопоставив действия с логическими значениями.
Проверьте всю суть здесь
источник
Вот код красивого простого решения, описанного выше @ user2690455. Хотя это выглядит немного многословно, в целом вы увидите, что на самом деле он довольно легкий
В моем случае мы также используем AppCompatActivity, поэтому мне пришлось иметь 2 базовых класса.
public class BaseActivity extends Activity { /** * Let field be set only in base class * All callers must use accessors, * and then it's not up to them to manage state. * * Making it static since .. * 1. It needs to be used across two base classes * 2. It's a singleton state in the app */ private static boolean IS_APP_IN_BACKGROUND = false; @Override protected void onResume() { super.onResume(); BaseActivity.onResumeAppTracking(this); BaseActivity.setAppInBackgroundFalse(); } @Override protected void onStop() { super.onStop(); BaseActivity.setAppInBackgroundTrue(); } @Override protected void onPause() { super.onPause(); BaseActivity.setAppInBackgroundFalse(); } protected static void onResumeAppTracking(Activity activity) { if (BaseActivity.isAppInBackground()) { // do requirements for returning app to foreground } } protected static void setAppInBackgroundFalse() { IS_APP_IN_BACKGROUND = false; } protected static void setAppInBackgroundTrue() { IS_APP_IN_BACKGROUND = true; } protected static boolean isAppInBackground() { return IS_APP_IN_BACKGROUND; } }
источник
Это полезно только в том случае, если вы хотите выполнить какое-либо действие только тогда, когда ваше действие начинается и где вы хотите проверить, находится ли приложение на переднем плане или в фоновом режиме.
Вместо использования диспетчера действий есть простой трюк, который можно выполнить с помощью кода. Если вы внимательно понаблюдаете за циклом активности, переход между двумя действиями и от переднего плана к фону выглядит следующим образом. Предположим, что A и B - два вида деятельности.
При переходе от A к B: 1. вызывается onPause () для A 2. вызывается onResume () для B 3. onStop () для A вызывается, когда B полностью возобновляется
Когда приложение переходит в фоновый режим: 1. вызывается onPause () из A 2. вызывается onStop () из A
Вы можете обнаружить фоновое событие, просто установив флажок в Activity.
Сделайте абстрактное действие и расширьте его из других ваших действий, чтобы вам не пришлось копировать и вставлять код для всех других действий, где вам нужно фоновое событие.
В абстрактной активности создать флаг isAppInBackground.
В методе onCreate (): isAppInBackground = false;
В методе onPause (): isAppInBackground = false;
В методе onStop (): isAppInBackground = true;
Вам просто нужно проверить свой onResume (), если isAppInBackground истинно. n после того, как вы проверите свой флаг, снова установите isAppInBackground = false
Для перехода между двумя действиями, поскольку onSTop () первого всегда будет вызываться после возобновления второго действия, флаг никогда не будет истинным, а когда приложение находится в фоновом режиме, onStop () активности будет вызываться сразу после onPause, и, следовательно, флаг будет истинным, когда вы откроете приложение позже.
Однако в этом подходе есть еще один сценарий. Если какой-либо экран вашего приложения уже открыт, и вы переводите мобильный телефон в режим ожидания, то через некоторое время мобильный телефон перейдет в спящий режим, и когда вы разблокируете мобильный телефон, он будет обработан в фоновом режиме.
источник
Вот метод, который я использую (и поддерживающий метод):
private boolean checkIfAppIsRunningInForeground() { ActivityManager activityManager = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE); for(ActivityManager.RunningAppProcessInfo appProcessInfo : activityManager.getRunningAppProcesses()) { if(appProcessInfo.processName.contains(this.getPackageName())) { return checkIfAppIsRunningInForegroundByAppImportance(appProcessInfo.importance); } } return false; } private boolean checkIfAppIsRunningInForegroundByAppImportance(int appImportance) { switch (appImportance) { //user is aware of app case ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND: case ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE: return true; //user is not aware of app case ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND: case ActivityManager.RunningAppProcessInfo.IMPORTANCE_EMPTY: case ActivityManager.RunningAppProcessInfo.IMPORTANCE_PERCEPTIBLE: case ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE: default: return false; } }
источник
Для этого нет глобального обратного вызова, но для каждого действия он onStop (). Вам не нужно связываться с атомарным int. Просто имейте глобальный int с количеством запущенных действий, в каждом действии увеличивайте его в onStart () и уменьшайте его в onStop ().
Следуй за этим
источник
public static boolean isAppRunning(Context context) { // check with the first task(task in the foreground) // in the returned list of tasks ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); List<RunningTaskInfo> services = activityManager.getRunningTasks(Integer.MAX_VALUE); if (services.get(0).topActivity.getPackageName().toString().equalsIgnoreCase(context.getPackageName().toString())) { return true; } return false; }
источник
Упомянутые здесь предыдущие подходы не оптимальны. Подход, основанный на задачах, требует разрешения, которое может быть нежелательным, а «логический» подход предрасположен к ошибкам одновременной модификации.
Подход, который я использую и который (как мне кажется) работает достаточно хорошо в большинстве случаев:
Создайте класс MainApplication для отслеживания количества действий в AtomicInteger :
import android.app.Application; import java.util.concurrent.atomic.AtomicInteger; public class MainApplication extends Application { static class ActivityCounter { private static AtomicInteger ACTIVITY_COUNT = new AtomicInteger(0); public static boolean isAppActive() { return ACTIVITY_COUNT.get() > 0; } public static void activityStarted() { ACTIVITY_COUNT.incrementAndGet(); } public static void activityStopped() { ACTIVITY_COUNT.decrementAndGet(); } } }
И создайте базовый класс Activity, который будут расширять другие действия:
import android.app.Activity; import android.support.annotation.CallSuper; public class TestActivity extends Activity { @Override @CallSuper protected void onStart() { MainApplication.ActivityCounter.activityStarted(); super.onStart(); } @Override @CallSuper protected void onStop() { MainApplication.ActivityCounter.activityStopped(); super.onStop(); } }
источник