Проверить андроид приложение на переднем плане или нет?
Я получил много ответов на этот вопрос. Но это все об одном действии. Как проверить, работает ли все приложение на переднем плане или нет?
15 ответов
Я не понимаю, что вы хотите, но вы можете обнаружить в настоящее время передний план / фоновое приложение с ActivityManager.getRunningAppProcesses()
вызов.
Что-то вроде,
class ForegroundCheckTask extends AsyncTask<Context, Void, Boolean> {
@Override
protected Boolean doInBackground(Context... params) {
final Context context = params[0].getApplicationContext();
return isAppOnForeground(context);
}
private boolean isAppOnForeground(Context context) {
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
List<RunningAppProcessInfo> appProcesses = activityManager.getRunningAppProcesses();
if (appProcesses == null) {
return false;
}
final String packageName = context.getPackageName();
for (RunningAppProcessInfo appProcess : appProcesses) {
if (appProcess.importance == RunningAppProcessInfo.IMPORTANCE_FOREGROUND && appProcess.processName.equals(packageName)) {
return true;
}
}
return false;
}
}
// Use like this:
boolean foregroud = new ForegroundCheckTask().execute(context).get();
Также дайте мне знать, если я неправильно понимаю..
ОБНОВЛЕНИЕ: посмотрите на этот SO вопрос. Определение текущей приоритетной заявки из фоновой задачи или службы для получения дополнительной информации.
Спасибо..
Самый приятный и не осуждаемый способ, который я нашел до сих пор, заключается в следующем:
@Override
public boolean foregrounded() {
ActivityManager.RunningAppProcessInfo appProcessInfo = new ActivityManager.RunningAppProcessInfo();
ActivityManager.getMyMemoryState(appProcessInfo);
return (appProcessInfo.importance == IMPORTANCE_FOREGROUND || appProcessInfo.importance == IMPORTANCE_VISIBLE)
}
Работает только с SDK 16+.
РЕДАКТИРОВАТЬ:
Я удалил следующий код из решения:
KeyguardManager km = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
// App is foreground, but screen is locked, so show notification
return km.inKeyguardRestrictedInputMode();
так как это делает не получать уведомления, если экран заблокирован. Я взглянул на рамки, и цель этого не совсем ясна. Я бы удалил это. Достаточно проверить состояние информации о процессе:-)
Ответ @user370305 подвержен ошибкам и не приветствуется разработчиками ОС Android (см. https://groups.google.com/forum/#!msg/android-developers/zH-2bovZSLg/L2YM8Z1N-HwJ).
Существует гораздо более простой подход:
На BaseActivity, что все действия расширяются:
protected static boolean isVisible = false;
@Override
public void onResume() {
super.onResume();
setVisible(true);
}
@Override
public void onPause() {
super.onPause();
setVisible(false);
}
Всякий раз, когда вам нужно проверить, находится ли какое-либо из ваших действий приложения на переднем плане, просто проверьте isVisible();
Чтобы понять этот подход, проверьте ответ жизненного цикла параллельной деятельности: Жизненный цикл активности
С помощью новых расширений Android Architecture of Lifecycle мы можем достичь этого с максимальной легкостью.
Просто убедитесь, что вы извлекаете эту зависимость в свой файл build.gradle:
dependencies {
implementation "android.arch.lifecycle:extensions:1.1.0"
}
Затем в вашем классе приложения используйте это:
class ArchLifecycleApp : Application(), LifecycleObserver {
override fun onCreate() {
super.onCreate()
ProcessLifecycleOwner.get().lifecycle.addObserver(this)
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun onAppBackgrounded() {
Log.d("MyApp", "App in background")
}
@OnLifecycleEvent(Lifecycle.Event.ON_START)
fun onAppForegrounded() {
Log.d("MyApp", "App in foreground")
}
}
В конце обновите файл AndroidManifest.xml:
<application
android:name=".ArchLifecycleApp"
//Your extra code
....>
</application>
Теперь каждый раз, когда приложение переходит на передний план или в фон, мы будем получать журналы, связанные с двумя объявленными методами.
Библиотека компонентов архитектуры Android позволяет использовать ProcessLifecycleOwner для настройки прослушивателя всего процесса приложения для событий onStart и onStop. Чтобы сделать это, заставьте свой класс приложения реализовать интерфейс LifecycleObserver и добавьте некоторые аннотации для onStop и onStart в ваши методы переднего плана и фона.
class ArchLifecycleApp : Application(), LifecycleObserver {
override fun onCreate() {
super.onCreate()
ProcessLifecycleOwner.get().lifecycle.addObserver(this)
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun onAppBackgrounded() {
Log.d("Awww", "App in background")
}
@OnLifecycleEvent(Lifecycle.Event.ON_START)
fun onAppForegrounded() {
Log.d("Yeeey", "App in foreground")
}
}
Я пытался с фильтром пакетов от запуска процесса. но это очень странно. Вместо этого я попробую новое решение, и это отлично работает. Я проверял много раз и получаю идеальный результат через этот модуль.
private int numRunningActivities = 0;
public void onCreate() {
super.onCreate();
this.registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
@Override
public void onActivityStarted(Activity activity) {
numRunningActivities++;
if (numRunningActivities == 1) {
LogUtils.d("APPLICATION", "APP IN FOREGROUND");
}
}
@Override
public void onActivityStopped(Activity activity) {
numRunningActivities--;
if (numRunningActivities == 0) {
Log.e("", "App is in BACKGROUND")
}
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
@Override
public void onActivityResumed(Activity activity) {
}
@Override
public void onActivityPaused(Activity activity) {
}
@Override
public void onActivityDestroyed(Activity activity) {
}
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
}
});
}
Проверьте, находится ли приложение в фоновом режиме или на переднем плане. Этот метод вернет true, если приложение находится в фоновом режиме.
Сначала добавьте разрешение GET_TASKS в ваш AndroidManifest.xml
private boolean isAppIsInBackground(Context context) {
boolean isInBackground = true;
ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT_WATCH) {
List<ActivityManager.RunningAppProcessInfo> runningProcesses = am.getRunningAppProcesses();
for (ActivityManager.RunningAppProcessInfo processInfo : runningProcesses) {
if (processInfo.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
for (String activeProcess : processInfo.pkgList) {
if (activeProcess.equals(context.getPackageName())) {
isInBackground = false;
}
}
}
}
} else {
List<ActivityManager.RunningTaskInfo> taskInfo = am.getRunningTasks(1);
ComponentName componentInfo = taskInfo.get(0).topActivity;
if (componentInfo.getPackageName().equals(context.getPackageName())) {
isInBackground = false;
}
}
return isInBackground;
}
Я нашел простое решение для этого, создав базовый класс активности, вы должны расширить все свои классы активности из этого:
public class BaseActivity extends ActionBarActivity {
@Override
protected void onResume() {
ApplicationStateChecker.view_resumed(this);
super.onResume();
}
@Override
protected void onStop() {
ApplicationStateChecker.view_stopped(this);
super.onStop();
}
@Override
protected void onPause() {
ApplicationStateChecker.view_paused(this);
super.onPause();
}
}
Класс ApplicationStateChecker:
public class ApplicationStateChecker {
private static final String _pause_string = "paused";
private static final String _resume_string = "resumed";
private static String _view_lastState;
private static boolean _from_background = true;
public static void view_paused(Activity activity){
_view_lastState = _pause_string;
}
public static void view_stopped(Activity activity){
if ( _view_lastState.equals(_pause_string) ){
//if stop called and last event was pause then app is brought to background
_from_background = true;
} //if
}
public static void view_resumed(Activity activity){
if ( _from_background ) {
//Do your stuff here , app is brought to foreground
} //if
_from_background = false;
_view_lastState = _resume_string;
}
Ни одно из решений, основанных на getRunningTasks(), не работает в последних версиях Android, метод getRunningTasks() устарел на уровне API 21. Даже если он все еще используется, он не возвращает достаточно информации, чтобы определить, находится ли приложение на переднем плане.
Вместо этого расширьте класс Application и используйте Application.ActivityLifecycleCallbacks для отслеживания состояния видимости приложения.
public class MyApplication extends Application {
static final String APP_STATE_FOREGROUND = "com.xxx.appstate.FOREGROUND";
static final String APP_STATE_BACKGROUND = "com.xxx.appstate.BACKGROUND";
private static int m_foreground = -1;
private Handler m_handler = new Handler();
private Runnable m_guard;
public static boolean isForeground() {
return m_foreground == 1;
}
@Override
public void onCreate() {
super.onCreate();
registerActivityLifecycleCallbacks(new Application.ActivityLifecycleCallbacks() {
@Override
public void onActivityCreated(Activity activity, Bundle bundle) {
}
@Override
public void onActivityStarted(Activity activity) {
}
@Override
public void onActivityResumed(Activity activity) {
if(m_guard != null) {
m_handler.removeCallbacks(m_guard);
m_guard = null;
}
if(m_foreground == 1)
return;
m_foreground = 1;
sendBroadcast(new Intent(APP_STATE_FOREGROUND));
}
@Override
public void onActivityPaused(Activity activity) {
if(m_foreground == 0)
return;
/*
* Use a 400ms guard to protect against jitter
* when switching between two activities
* in the same app
*/
m_guard = new Runnable() {
@Override
public void run() {
if(m_foreground == 1) {
m_foreground = 0;
sendBroadcast(new Intent(APP_STATE_BACKGROUND));
}
}
};
m_handler.postDelayed(m_guard, 400);
}
@Override
public void onActivityStopped(Activity activity) {
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {
}
@Override
public void onActivityDestroyed(Activity activity) {
}
});
}
}
Использование защитного таймера 400 мс исключает ложное обнаружение фонового состояния при переключении между действиями в одном приложении. Состояние фона / переднего плана может быть запрошено в любое время, используя:
MyApplication.isForeground();
Класс также может прослушивать широковещательные события, если он заинтересован в переходах состояний:
private static IntentFilter m_appStateFilter;
static {
m_appStateFilter = new IntentFilter();
m_appStateFilter.addAction(MyApplication.APP_STATE_FOREGROUND);
m_appStateFilter.addAction(MyApplication.APP_STATE_BACKGROUND);
}
private BroadcastReceiver m_appStateReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(MyApplication.APP_STATE_FOREGROUND)) {
/* application entered foreground */
} else if (action.equals(MyApplication.APP_STATE_BACKGROUND)) {
/* application entered background */
}
}
};
registerReceiver(m_appStateReceiver, m_appStateFilter);
Для этого нет глобального обратного вызова, но для каждого действия это onStop(). Вам не нужно связываться с атомным Int. Просто имейте глобальный int с количеством запущенных действий, в каждом действии увеличивайте его в onStart() и уменьшайте в onStop().
public class BaseActivity extends ActionBarActivity {
public static int count = 0;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
protected void onStart() {
super.onStart();
count = count + 1;
Log.d(TAG, "onStart" + count);
if (count == 1) {
Toast.makeText(getApplicationContext(), "online", Toast.LENGTH_SHORT).show();
}
}
protected void onStop() {
super.onStop();
count = count - 1;
if (count == 0) {
Toast.makeText(getApplicationContext(), "offline", Toast.LENGTH_SHORT).show();
}
}
}
Ответ cesards правильный, но только для API > 15. Для более низких версий API я решил использовать getRunningTasks()
метод:
private boolean isAppInForeground(Context context)
{
if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)
{
ActivityManager am = (ActivityManager) context.getSystemService(ACTIVITY_SERVICE);
ActivityManager.RunningTaskInfo foregroundTaskInfo = am.getRunningTasks(1).get(0);
String foregroundTaskPackageName = foregroundTaskInfo.topActivity.getPackageName();
return foregroundTaskPackageName.toLowerCase().equals(context.getPackageName().toLowerCase());
}
else
{
ActivityManager.RunningAppProcessInfo appProcessInfo = new ActivityManager.RunningAppProcessInfo();
ActivityManager.getMyMemoryState(appProcessInfo);
if (appProcessInfo.importance == IMPORTANCE_FOREGROUND || appProcessInfo.importance == IMPORTANCE_VISIBLE)
{
return true;
}
KeyguardManager km = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);
// App is foreground, but screen is locked, so show notification
return km.inKeyguardRestrictedInputMode();
}
}
Пожалуйста, дайте мне знать, если это работает для всех вас.
Начиная с Android 19, вы можете зарегистрировать обратный вызов жизненного цикла приложения в onCreate() вашего класса приложений, например:
@Override
public void onCreate() {
super.onCreate();
registerActivityLifecycleCallbacks(new AppLifecycleCallback());
}
AppLifecycleCallback выглядит следующим образом:
class AppLifecycleCallback implements Application.ActivityLifecycleCallbacks {
private int numStarted = 0;
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
}
@Override
public void onActivityStarted(Activity activity) {
if (numStarted == 0) {
//app went to foreground
}
numStarted++;
}
@Override
public void onActivityResumed(Activity activity) {
}
@Override
public void onActivityPaused(Activity activity) {
}
@Override
public void onActivityStopped(Activity activity) {
numStarted--;
if (numStarted == 0) {
// app went to background
}
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
@Override
public void onActivityDestroyed(Activity activity) {
}
}
Попробуйте ActivityLifecycleCallbacks в вашем классе приложения.
Ниже решение работает от уровня API 14+
Backgrounding ComponentCallbacks2 - просмотр документации не на 100% ясен в том, как бы вы это использовали. Однако присмотритесь поближе, и вы заметите, что метод onTrimMemory передает флаг. Эти флаги, как правило, связаны с доступностью памяти, но нас интересует TRIM_MEMORY_UI_HIDDEN. Проверяя, скрыт ли пользовательский интерфейс, мы можем предположить, что приложение теперь находится в фоновом режиме. Не совсем очевидно, но это должно работать.
Foregrounding ActivityLifecycleCallbacks - мы можем использовать это для обнаружения переднего плана путем переопределения onActivityResumed и отслеживания текущего состояния приложения (Foreground/Background).
Создайте наш интерфейс, который будет реализован с помощью пользовательского класса Application.
interface LifecycleDelegate {
fun onAppBackgrounded()
fun onAppForegrounded()
}
Создайте класс, который будет реализовывать ActivityLifecycleCallbacks и ComponentCallbacks2 и переопределить методы onActivityResumed и onTrimMemory
// Take an instance of our lifecycleHandler as a constructor parameter
class AppLifecycleHandler(private val lifecycleDelegate: LifecycleDelegate)
: Application.ActivityLifecycleCallbacks, ComponentCallbacks2 // <-- Implement these
{
private var appInForeground = false
// Override from Application.ActivityLifecycleCallbacks
override fun onActivityResumed(p0: Activity?) {
if (!appInForeground) {
appInForeground = true
lifecycleDelegate.onAppForegrounded()
}
}
// Override from ComponentCallbacks2
override fun onTrimMemory(level: Int) {
if (level == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
// lifecycleDelegate instance was passed in on the constructor
lifecycleDelegate.onAppBackgrounded()
}
}
}
Теперь все, что нам нужно сделать, - это заставить наш собственный класс Application реализовать наш интерфейс LifecycleDelegate и зарегистрироваться.
class App : Application(), LifeCycleDelegate {
override fun onCreate() {
super.onCreate()
val lifeCycleHandler = AppLifecycleHandler(this)
registerLifecycleHandler(lifeCycleHandler)
}
override fun onAppBackgrounded() {
Log.d("Awww", "App in background")
}
override fun onAppForegrounded() {
Log.d("Yeeey", "App in foreground")
}
private fun registerLifecycleHandler(lifeCycleHandler: AppLifecycleHandler) {
registerActivityLifecycleCallbacks(lifeCycleHandler)
registerComponentCallbacks(lifeCycleHandler)
}
}
В манифесте установите CustomApplicationClass
<application
android:name=".App"
Ниже обновленное решение для последней версии Android SDK.
String PackageName = context.getPackageName();
ActivityManager manager = (ActivityManager) context.getSystemService(ACTIVITY_SERVICE);
ComponentName componentInfo;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
{
List<ActivityManager.AppTask> tasks = manager.getAppTasks();
componentInfo = tasks.get(0).getTaskInfo().topActivity;
}
else
{
List<ActivityManager.RunningTaskInfo> tasks = manager.getRunningTasks(1);
componentInfo = tasks.get(0).topActivity;
}
if (componentInfo.getPackageName().equals(PackageName))
return true;
return false;
Надеюсь, это поможет, спасибо.