Program Tip

Android 앱이 포 그라운드에서 실행 중인지 어떻게 알 수 있습니까?

programtip 2020. 9. 25. 23:30
반응형

Android 앱이 포 그라운드에서 실행 중인지 어떻게 알 수 있습니까?


c2dm에 의해 트리거되는 내 Android 앱에서 상태 표시 줄 알림을 수행하고 있습니다. 앱이 실행중인 경우 알림을 표시하고 싶지 않습니다. 앱이 실행 중이고 포 그라운드에 있는지 어떻게 확인합니까?


같은 전역 변수 만들고에서 값을 private boolean mIsInForegroundMode;할당합니다 .falseonPause()trueonResume()

샘플 코드 :

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
    }

}

그리고 위 코드에서 메서드 를 실행할 수 있도록 manifest.xml 파일에 GET_TASKS권한 을 추가하는 것을 잊지 마십시오 .getRunningTasks()

<uses-permission android:name="android.permission.GET_TASKS" />

p / s :이 방법에 동의하는 경우이 권한은 이제 더 이상 사용되지 않습니다.


이것은 꽤 오래된 게시물이지만 여전히 관련성이 높습니다. 위의 허용 된 솔루션은 작동 할 수 있지만 잘못되었습니다. Dianne Hackborn은 다음과 같이 썼습니다.

이러한 API는 애플리케이션이 UI 흐름을 기반으로하는 것이 아니라 사용자에게 실행중인 앱이나 작업 관리자를 표시하는 등의 작업을 수행하기위한 것입니다.

예, 이러한 것들을 위해 기억에 보관되는 목록이 있습니다. 그러나 그것은 다른 프로세스에서 꺼져 있으며, 당신과 별도로 실행되는 스레드에 의해 관리되며 (a) 올바른 결정을 내리기 위해 제 시간에 보거나 (b) 돌아올 때까지 일관된 그림을 가질 수 없습니다. 또한 이동할 "다음"활동에 대한 결정은 항상 전환이 발생하는 지점에서 이루어지며 정확한 지점 (활동 상태가 전환을 수행하기 위해 잠깐 잠긴 지점)까지는 아닙니다. 실제로 다음 일이 무엇인지 알고 있습니다.

그리고 여기에서 구현 및 글로벌 동작이 미래에도 동일하게 유지된다는 보장은 없습니다.

올바른 해결책은 다음을 구현하는 것입니다. ActivityLifeCycleCallbacks .

기본적으로 애플리케이션 클래스가 필요하며 핸들러는 앱에서 활동의 상태를 식별하기 위해 설정 될 수 있습니다.


Vinay가 말했듯이 아마도 가장 좋은 솔루션 (최신 안드로이드 버전, 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" />

매니페스트에.


Gadenkan의 솔루션 버전을 약간 정리했습니다 . 모든 활동 또는 모든 활동에 대한 기본 클래스를 넣으십시오.

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 년 2 월)

그 주 getRunningTasks()API 레벨 21에서 더 이상 사용되지 !

현재이 LOLLIPOP방법은 타사 응용 프로그램에서 더 이상 사용할 수 없습니다. 문서 중심의 최신 정보가 도입됨에 따라 사람 정보가 발신자에게 유출 될 수 있습니다. 이전 버전과의 호환성을 위해 최소한 호출자 자신의 작업과 민감하지 않은 것으로 알려진 집과 같은 다른 작업과 같은 데이터의 작은 하위 집합을 계속 반환합니다.

그래서 제가 이전에 쓴 내용은 훨씬 더 관련이 있습니다.

대부분의 경우 더 나은 솔루션을 찾을 수 있습니다. 예를 들어, 모든 활동에 대한 BaseActivity 에서 onPause()onResume()에서 무언가를 수행 합니다.

(우리의 경우 포 그라운드에 있지 않은 경우 오프라인 경고 활동이 시작되는 것을 원하지 않았으므로 onPause()BaseActivity Subscription에서 "went offline"신호를 수신 하는 RxJava의 구독을 취소합니다 .)


Gadenkan의 답장에 이어 이와 같은 것이 필요했기 때문에 내 앱이 포 그라운드에서 실행되고 있지 않은지 알 수 있었지만 앱 전체에 걸쳐서 애플리케이션 전체에서 플래그를 설정 / 설정 해제 할 필요가 없었습니다.

Gadenkan의 코드는 머리에 거의 맞았지만 내 스타일이 아니고 더 깔끔 할 수 있다고 느꼈기 때문에 내 앱에서는 이것으로 압축되었습니다.

if (!context.getPackageName().equalsIgnoreCase(((ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE)).getRunningTasks(1).get(0).topActivity.getPackageName()))
{
// App is not in the foreground
}

(참고 : 확인이 다른 방식으로 작동하도록하려면!를 제거하면됩니다.)

이 접근 방식을 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));
}

활동을 부울로 매핑하여 응용 프로그램이 포 그라운드인지 백그라운드인지 확인하는 더 간단하고 정확한 방법을 찾았습니다.

여기 에서 전체 요점을 확인 하십시오.


이것은 활동이 시작될 때와 앱이 포 그라운드 또는 백그라운드에 있는지 확인하려는 위치에서 특정 작업을 수행하려는 경우에만 유용합니다.

활동 관리자를 사용하는 대신 코드를 통해 수행 할 수있는 간단한 트릭이 있습니다. 활동주기를 자세히 살펴보면 두 활동과 전경에서 배경으로의 흐름은 다음과 같습니다. A와 B가 두 가지 활동이라고 가정합니다.

A에서 B로 전환시 : 1. A의 onPause () 호출 2. B의 onResume () 호출 3. B가 완전히 재개되면 A의 onStop () 호출

앱이 백그라운드로 전환 될 때 : 1. A의 onPause () 호출 2. A의 onStop () 호출

활동에 플래그를 넣기 만하면 백그라운드 이벤트를 감지 할 수 있습니다.

추상 활동을 만들고 다른 활동에서 확장하여 백그라운드 이벤트가 필요한 곳에 다른 모든 활동에 대한 코드를 복사하여 붙여 넣을 필요가 없습니다.

추상 활동에서 isAppInBackground 플래그를 만듭니다.

onCreate () 메서드에서 : isAppInBackground = false;

onPause () 메서드에서 : isAppInBackground = false;

onStop () 메서드에서 : isAppInBackground = true;

isAppInBackground가 true이면 onResume () 만 확인하면됩니다. n 플래그를 확인한 후 다시 isAppInBackground = false를 설정합니다.

두 활동 간의 전환의 경우 첫 번째의 onSTop ()이 항상 두 번째 활동이 재개 된 후에 호출되므로 플래그가 true가되지 않으며 앱이 백그라운드에있을 때 onPause 직후 활동의 onStop ()이 호출되므로 플래그는 다음과 같은 경우 true가됩니다. 나중에 앱을 엽니 다.

이 접근 방식에는 시나리오가 하나 더 있습니다. 앱 화면이 이미 열려 있고 모바일을 유휴 상태로두면 잠시 후 모바일이 절전 모드로 전환되고 모바일 잠금을 해제하면 백그라운드 이벤트에서 처리됩니다.


다음은 내가 사용하는 방법 (및 지원 방법)입니다.

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;
    }
}

다양한 답변과 의견을 바탕으로 도우미 클래스에 추가 할 수있는 인라인 버전이 있습니다.

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"/>

다음은 @ 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;
    }
}

지원 라이브러리 버전 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
}

, Now you can query ProcessLifecycleOwner whenever you want to check app state, for example to check if app is running in foreground you just have to do this:

 boolean isAppInForeground = ProcessLifecycleOwner.get().getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED);
 if(!isAppInForeground)
    //Show Notification in status bar

There is no global callback for this, but for each activity it is onStop(). You don't need to mess with an atomic int. Just have a global int with the number of started activities, in every activity increment it in onStart() and decrement it in onStop().

Follow this


     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;
     }

The previous approaches mentioned here are not optimal. The task based approach requires a permission that might not be desired and "Boolean" approach is prone to concurrent modification mess ups.

The approach I use and which (I believe) works quite well in most cases:

Have a "MainApplication" class to track activity count in 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();
        }
    }
}

그리고 다른 활동이 확장 할 기본 활동 클래스를 만듭니다.

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();
    }
}

참고 URL : https://stackoverflow.com/questions/5504632/how-can-i-tell-if-android-app-is-running-in-the-foreground

반응형