Programing

백그라운드에서 Android 애플리케이션이 실행 중인지 확인

lottogame 2020. 3. 13. 08:13
반응형

백그라운드에서 Android 애플리케이션이 실행 중인지 확인


배경에 따르면, 현재 응용 프로그램의 활동이 사용자에게 보이지 않는 것을 의미합니까?


애플리케이션이 백그라운드에서 실행 중인지 여부를 감지하는 몇 가지 방법이 있지만 그 중 하나만 완전히 신뢰할 수 있습니다.

  1. 올바른 솔루션 (신용은 Dan , CommonsWareNeTeInStEiN으로 이동 ) , 메소드를
    사용하여 애플리케이션의 가시성을 추적하십시오 . "가시성"상태를 다른 클래스에 저장하십시오. 올바른 선택은 또는의 자체 구현입니다 ( 서비스에서 활동 가시성을 확인하려는 경우이 솔루션 의 일부 변형 도 있음 ). 사용자 정의 구현 클래스 (음 정적 방법) :Activity.onPauseActivity.onResumeApplicationService
     

    ApplicationisActivityVisible()

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

    추가 onPauseonResume모든에 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의 답변을 확인하십시오.


  2. 내가 다음 해결책을 제안하는 데 잘못 사용했습니다.

    레코드 ActivityManager.getRunningAppProcesses()목록을 리턴하는 현재 포 그라운드 / 백그라운드 애플리케이션을 감지 할 수 있습니다 RunningAppProcessInfo. 응용 프로그램이 포 그라운드에 있는지 확인 RunningAppProcessInfo.importance하려면 RunningAppProcessInfo.IMPORTANCE_FOREGROUNDwhile RunningAppProcessInfo.processName과 동일한 지 확인 하십시오. 응용 프로그램 패키지 이름과 같습니다.

    또한 ActivityManager.getRunningAppProcesses()응용 프로그램 UI 스레드에서 호출하면 IMPORTANCE_FOREGROUND실제로 포 그라운드에 있는지 여부에 관계없이 작업의 중요도 반환합니다 . 백그라운드 스레드에서 (예 :을 통해 AsyncTask) 호출하면 올바른 결과가 반환됩니다.

    이 솔루션은 작동하지만 실제로는 대부분 작동하지만 사용하지 않는 것이 좋습니다. 그리고 여기에 이유가 있습니다. Dianne Hackborn은 다음과 같이 썼습니다 .

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

    그렇습니다. 이런 것들에 대한 목록은 기억에 남아 있습니다. 그러나 다른 프로세스에서는 꺼져 있으며 사용자와 별도로 실행되는 스레드로 관리되며 (a) 올바른 결정을 내릴 시간을 보거나 (b) 돌아올 때까지 일관된 그림을 가질 수는 없습니다. 또한 "다음"활동에 대한 결정은 항상 스위치가 발생하는 지점에서 이루어지며, 정확한 시점 (활동 상태가 잠깐 동안 스위치를 수행하기 위해 잠긴 지점)이 될 때까지는 수행되지 않습니다. 실제로 다음 일이 무엇인지 확실히 알고 있어야합니다.

    그리고 여기에서의 구현과 글로벌 행동이 미래에도 동일하게 유지되는 것은 아닙니다.

    나는 SO에 대한 답변을 게시하기 전에 이것을 읽었기를 바란다. 그러나 내 오류를 인정하기에는 너무 늦지 않았 으면 좋겠다.

  3. 답변 중 하나에서 언급 된 또 다른 잘못된 솔루션
    Droid-Fu 라이브러리는 ActivityManager.getRunningTasks해당 isApplicationBroughtToBackground방법에 사용 합니다 . 위의 Dianne의 의견을 참조하고 해당 방법을 사용하지 마십시오.


이 답변을 사용하지 마십시오

user1269737의 답변은 올바른 (Google / Android 승인) 방법 입니다. 그들의 답변을 읽고 +1하십시오.

나는 후손을 위해 원래의 대답을 여기에 남겨 둘 것이다. 이것은 2012 년에 가장 잘 사용되었지만 이제는 Android가이를 올바르게 지원합니다.

원래 답변

The key is using ActivityLifecycleCallbacks (note that this requires Android API level 14 (Android 4.0)). Just check if the number of stopped activities is equal to the number of started activities. If they're equal, your application is being backgrounded. If there are more started activities, your application is still visible. If there are more resumed than paused activities, your application is not only visible, but it's also in the foreground. There are 3 main states that your activity can be in, then: visible and in the foreground, visible but not in the foreground, and not visible and not in the foreground (i.e. in the background).

이 방법에 대한 정말 좋은 점은 비동기 문제가되지 않는다는 것입니다 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 () 메소드를 호출 한 후 활동 프로세스를 계속 실행할 수있는 메모리가 부족한 메모리가 부족한 상황에서는이 메소드를 호출 할 수 없습니다.

여기서 핵심은 "활동의 프로세스를 계속 실행하는 것"입니다. 메모리 부족 상황에 도달하면 프로세스가 실제로 종료됩니다 (활동 만이 아니라). 이는 a) 프로세스가 종료 된 경우 어쨌든 백그라운드를 확인할 수 없으며 b) 프로세스가 다시 시작되는 경우 (새 활동이 작성 되었기 때문에) 에 대한 변수 (정적이든 아니든) MyLifecycleHandler는로 재설정됩니다 0.

구성 변경에도 적용됩니까?

기본적으로 아니요. 매니페스트 파일에서 명시 적으로 설정하고 configChanges=orientation|screensize( |다른 것을 사용하여) 구성 변경을 처리해야합니다. 그렇지 않으면 활동이 파괴되어 다시 작성됩니다. 이를 설정하지 않으면 다음과 같은 순서로 활동 메소드가 호출됩니다 onCreate -> onStart -> onResume -> (now rotate) -> onPause -> onStop -> onDestroy -> onCreate -> onStart -> onResume. 보시다시피 겹침이 없습니다 (일반적으로 두 활동을 전환 할 때 두 활동이 아주 짧게 겹칩니다. 이는 백그라운드 감지 방법이 작동하는 방식입니다). 이 문제를 해결하려면 configChanges활동이 파괴되지 않도록 설정해야합니다 . 다행히도 설정해야했습니다configChanges전체 활동이 화면 회전 / 크기 조정에서 파괴되는 것이 바람직하지 않았기 때문에 이미 모든 프로젝트에 이미 있었기 때문에 문제가되지 않았습니다. (내 기억을 상쾌하게하고 나를 고쳐 주신 dpimka에 감사드립니다!)

참고 사항 :

이 답변에서 "배경"이라고 말하면 "앱이 더 이상 표시되지 않습니다."라는 의미입니다. 안 드 로이드 활동은 아직 보이지 않지만 (예 : 투명한 알림 오버레이가있는 경우) 볼 수는 없습니다. 그렇기 때문에이 답변을 업데이트했습니다.

포 그라운드에 아무것도없는 곳에서 활동을 전환 할 때 Android에 이상한 순간이 있다는 것을 아는 것이 중요합니다 . 이러한 이유로, 동일한 앱에서 액티비티간에 전환 할 때 애플리케이션이 포 그라운드에 있는지 확인하면 포 그라운드에 있지 않다는 메시지가 표시됩니다 (앱이 여전히 활성 앱이고 표시됨) ).

당신의 앱이 포 그라운드에있는 경우 확인할 수 있습니다 ActivityonPause()방법 super.onPause() . 내가 방금 말한 이상한 림보 상태를 기억하십시오.

이후ActivityonStop()메소드 에서 앱이 표시되는지 (즉, 백그라운드에없는 경우) 확인할 수 있습니다 . super.onStop()


GOOGLE SOLUTION- 이전 솔루션처럼 해킹이 아닙니다. 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
    }
}

app.gradle에서

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)를 참조하십시오.


Idolon의 대답은 오류가 발생하기 쉽고 여기에서 안드로이드 응용 프로그램이 포 그라운드에 있는지 여부를 확인하는 것이 훨씬 더 복잡 합니까? 여기 백그라운드 작업이나 서비스에서 현재 전경 응용 프로그램을 결정

훨씬 더 간단한 접근 방식이 있습니다.

모든 활동이 확장 되는 BaseActivity에서 :

protected static boolean isVisible = false;

 @Override
 public void onResume()
 {
     super.onResume();
     setVisible(true);
 }


 @Override
 public void onPause()
 {
     super.onPause();
     setVisible(false);
 }

응용 프로그램 활동이 포 그라운드에 있는지 확인 해야 할 때마다 확인하십시오isVisible() .

이 방법은 나란히 활동의 라이프 사이클이 답변을 확인 이해하려면 다음 작업을 나란히 라이프 사이클


지원 라이브러리 버전 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부터 앱이 포 그라운드에 있는지 확인하는 간단한 방법이 있습니다. 완벽하지는 않지만 안드로이드의 방법은 절대 아닙니다. 이 방법은 서비스가 서버에서 업데이트를 수신 할 때 사용하기에 충분하며 UI를 포 그라운드로 표시 할 경우 사용자가 알림없이 업데이트를 알 수 있기 때문에 알림을 표시할지 여부를 결정해야합니다.

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

Application.ActivityLifecycleCallbacks 및 기타 여러 가지 를 사용하는 권장 솔루션을 시도했지만 예상대로 작동하지 않았습니다. Sarge 덕분에 아래에서 설명하는 매우 쉽고 간단한 솔루션을 생각해 냈습니다.

그들은 솔루션의 핵심 우리가 ActivityA 및 ActivityB을 가지고 우리가 ActivityA에서 ActivityB를 호출하는 경우 (그리고 전화를하지 않는 것이 이해의 사실이다 ActivityA.finish, 다음 ActivityB의)가 onStart()호출됩니다 전에 ActivityA onStop().

그것은 또한의 주요 차이 onStop()onPause()그 누구도 내가 읽은 기사에서 언급을하지 않았다.

따라서이 활동의 라이프 사이클 동작에 따라, 당신은 단순히 한 횟수를 셀 수 onStart()onPause()프로그램에서 호출되었다. 참고 Activity 프로그램의, 당신은 오버라이드 (override) 할 필요가 onStart()onStop()순서는, 증가 / 감소 정적 변수는 계산에 사용합니다. 아래는이 논리를 구현하는 코드입니다. 내가 확장하는 클래스를 사용하고 Application있으므로 간단한 사용자 정의 클래스를 사용하여 구현할 수도 있지만 Manifest.xmlApplication 태그 내 에서 선언하는 것을 잊지 마십시오 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--;
    }
}

이제 우리 프로그램의 각 활동에, 우리는 오버라이드 (override) 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();
}

이 논리를 사용하면 가능한 두 가지 경우가 있습니다.

  1. stateCounter = 0 : 중지 된 수는 시작된 활동 수와 같습니다. 즉, 애플리케이션이 백그라운드에서 실행 중임을 의미합니다.
  2. stateCounter > 0 : 시작 횟수가 중지 횟수보다 많으므로 애플리케이션이 포 그라운드에서 실행 중임을 의미합니다.

참고 : stateCounter < 0시작하기보다는 중지 된 활동이 더 많으므로 불가능합니다. 이 경우에는 카운터를 증가 / 감소시키지 않는 것입니다.

당신은 갈 준비가되었습니다. 애플리케이션이 백그라운드에 있는지 확인해야합니다 onStop().


활동을 볼 수 있는지 여부를 판단 할 수있는 방법은 없습니다. 아마도 새로운 StackOverflow 질문을하여 사용자 경험을 통해 무엇을 달성하려고하는지 설명해야하므로 대체 구현 아이디어를 제공 할 수 있습니다.


ComponentCallbacks2사용 하여 앱이 백그라운드에 있는지 감지 할 수 있습니다 . BTW이 콜백은 API 레벨 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를 기반으로 유용한 몇 가지 기능이 포함되어 있습니다.

추가 기능 :

  • AppLifecycleHandler.isApplicationVisible () 및 AppLifecycleHandler.isApplicationInForeground () 응용 프로그램의 어느 곳에서나 할 수 있도록 싱글 톤 패턴을 도입했습니다.
  • 중복 이벤트 처리 추가 (댓글 참조 // 가시성 변경에 대한 조치를 취하고 // 전경 변경에 대한 조치를 취함)

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 ()에서 동일한 타이머를 취소하면 Timer의 인스턴스가 1 개 있습니다 (일반적으로 Application 클래스에 정의 됨). 타이머 자체는 2 초 후에 (또는 적절하다고 생각되는 간격으로) Runnable을 실행하도록 설정되며, 타이머가 실행될 때 응용 프로그램을 백그라운드로 표시하는 플래그를 설정합니다.

타이머를 취소하기 전에 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) { }        

}

CommonsWare 및 Key가 말한 것을 피기 백하기 위해 Application 클래스를 확장하고 모든 활동이 onPause / onResume 메소드에서 호출되도록 할 수 있습니다. 이를 통해 어떤 활동이 보이는지 알 수 있지만 더 잘 처리 할 수 ​​있습니다.

당신이 생각하고있는 것을 정확하게 설명 할 수 있습니까? 백그라운드에서 실행한다고 말하면 현재 화면에 나타나지 않아도 응용 프로그램이 여전히 메모리에 남아 있다는 의미입니까? 서비스가 초점이 맞지 않을 때 앱을 관리하기위한보다 지속적인 방법으로 서비스를 사용해 보셨습니까?


ActivityLifecycleCallbacks를 직접 구현했습니다. SherlockActivity를 사용하고 있지만 일반적인 활동 클래스에서는 작동 할 수 있습니다.

먼저 활동 수명주기를 추적하기위한 모든 방법이있는 인터페이스를 만들고 있습니다.

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에서 만든 인터페이스 구현에 프로그래밍 된 로그가 표시됩니다.


대화 상자가 위에 오면 활동이 일시 중지되므로 모든 권장 솔루션이 반 솔루션입니다. 대화 상자에 대한 후크도 작성해야합니다.


아직 언급되지 않았으므로 독자는 Android 아키텍처 구성 요소를 통해 사용 가능한 ProcessLifecycleOwner 를 탐색하도록 제안합니다.


공식 문서 :

이 시스템은 포 그라운드 앱과 백그라운드 앱을 구분합니다. (서비스 제한을위한 배경 정의는 메모리 관리에서 사용되는 정의와는 다릅니다. 앱은 메모리 관리 와 관련하여 백그라운드에있을 수 있지만 서비스를 시작하는 기능과 관련하여 포 그라운드에있을 수 있습니다.) 다음 중 하나에 해당하면 포 그라운드에있는 것으로 간주됩니다.

  1. 활동 시작 또는 일시 중지 여부에 관계없이 활동이 표시됩니다.
  2. 전경 서비스가 있습니다.
  3. 다른 포 그라운드 앱은 서비스 중 하나에 바인딩하거나 콘텐츠 공급자 중 하나를 사용하여 앱에 연결됩니다. 예를 들어 다른 앱이 앱에 바인딩 된 경우 앱이 포 그라운드에 있습니다.
    • IME
    • 벽지 서비스
    • 알림 리스너
    • 음성 또는 문자 서비스

이러한 조건 중 어느 것도 해당되지 않으면 앱이 백그라운드에있는 것으로 간주됩니다.


유일한 해결책은 다음과 같습니다.

MainActivity.java :

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        MyApp.mainActivity = this;
        super.onCreate(savedInstanceState);
        ...
    }

MyApp.java :

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) {
            ...
        }
    }

}

이 오래된 게시물에 대한 또 다른 해결책 (도움이되는 사람들을위한) :


<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 및 바인딩 해제 onPause에 바인딩 할 때), 작업을 수행하려는 경우에만 실행됩니다. 정기적 인 시작 중지 서비스를 사용할 수 있습니다.


나는이 질문이 더 분명해야한다고 생각합니다. 언제? 어디? 앱이 백그라운드에있는 경우 확인하려는 특정 상황은 무엇입니까?

방금 내 방식으로 솔루션을 소개합니다. 내 응용 프로그램 의 모든 활동 방법에서 클래스의
"중요도"필드를 사용 하여이 작업을 수행합니다. "중요도"의 값을 확인하는 방법을 구현하는 다른 활동을 확장 하여 간단히 얻을 수 있습니다 . 코드는 다음과 같습니다.RunningAppProcessInfoonStopBaseActivityonStop

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()호출 된 후에는 더 이상 활동이 표시되지 않습니다 .


getApplicationState (). isInForeground () 사용은 어떻습니까?


제 생각에는 많은 답변이 많은 양의 코드를 도입하고 많은 복잡성과 가독성을 가져옵니다.

사람들이 SO Service와 a 사이의 통신 방법을 물으면 Activity일반적으로 LocalBroadcastManager 를 사용하는 것이 좋습니다 .


왜?

글쎄, 문서를 인용함으로써 :

  • 브로드 캐스트하는 데이터가 앱을 떠나지 않으므로 개인 데이터 유출에 대해 걱정할 필요가 없습니다.

  • 다른 응용 프로그램에서 이러한 브로드 캐스트를 앱으로 보낼 수 없으므로 악용 할 수있는 보안 허점에 대해 걱정할 필요가 없습니다.

  • 시스템을 통해 글로벌 브로드 캐스트를 전송하는 것보다 효율적입니다.

문서에 없습니다.

  • 외부 라이브러리가 필요하지 않습니다
  • 코드는 최소한입니다
  • 구현하고 이해하는 것이 빠릅니다.
  • 맞춤형 자체 구현 콜백 / 초 단일 / 프로세스 내 패턴 없음 ...
  • 아니 강한 참조Activity, Application...

기술

따라서 Activity현재 전경 에있는 것이 있는지 확인하고 싶습니다 . 당신은 보통 Service또는 Application수업 에서 그렇게합니다 .

즉, Activity객체가 신호를 보내는 사람이됩니다 (켜짐 / 꺼짐). 여러분 Service, 다른 한편으로는,이된다 Receiver.

전경이나 배경으로 가고 있는지 알려주 두 가지 순간이 있습니다 Activity(6이 아닌 2 개만 가능).

(가) 때 Activity전경에 이동의 onResume()방법은 (또한 후에 호출 트리거됩니다 onCreate()).

(가) 때 Activity뒤쪽에 간다, onPause()라고합니다.

이것은 상태를 설명 Activity하기 Service위해 신호를 보내야 할 순간입니다 .

여러 개의 경우 Activityan 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하면 전경에있는 경우에만 청취하고 뒤쪽에 있거나 파괴 된 경우 아무 일도 일어나지 않습니다.

여러 개의 경우 켜져있는 ActivityActivity하나라도 응답합니다 (또한 구현 한 경우 Receiver).

모두 배경에 있으면 아무도 응답하지 않고 신호가 손실됩니다.

신호 ID를 지정하여 Servicevia 에서 데이터를 전송하십시오 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
}

특정 활동이 진행 중인지 여부와 애플리케이션에 직접 액세스 할 수없는 SDK 인 경우 특정 사례에 맞는 답변이 없습니다. 나를 위해 새로운 대화 메시지에 대한 푸시 알림을받은 백그라운드 스레드에 있었고 채팅 화면이 전경에없는 경우에만 시스템 알림을 표시하려고합니다.

ActivityLifecycleCallbacks다른 답변에서 권장 되는 것을 사용하여 MyActivityForeground 에 있는지 여부에 대한 논리를 포함하는 작은 유틸리티 클래스를 만들었습니다 .

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)){...}

아마 우아하지는 않지만 그것은 나를 위해 작동합니다 ...


대답하기에는 너무 늦을 수도 있지만 누군가가 방문하면 여기에 제안하는 해결책이 있습니다. 앱이 백그라운드에 있거나 전경에 오는 상태를 알고 싶어하는 이유는 많을 수 있습니다. 사용자가 BG에있을 때 토스트 및 알림을 표시합니다. 2. 설문 조사, 다시 그리기 등과 같이 사용자가 BG에서 처음 왔을 때 일부 작업을 수행하려면

Idolon 등의 솔루션은 첫 번째 부분을 처리하지만 두 번째 부분은 처리하지 않습니다. 앱에 여러 활동이 있고 사용자가 다른 활동을 전환하는 경우 두 번째 활동을 할 때 표시되는 플래그가 거짓입니다. 따라서 결정적으로 사용할 수 없습니다.

CommonsWare가 제안한 것을 수행했습니다. "서비스에 가시적 인 활동이 없다고 판단하고 일정 시간 동안 그 방식으로 유지되는 경우 다음 논리적 중지 지점에서 데이터 전송을 중지하십시오."

굵은 선은 중요하며 두 번째 항목을 달성하는 데 사용할 수 있습니다. 그래서 내가하는 일은 일단 onActivityPaused ()를 얻고 가시성을 직접 false로 변경하지 않고 대신 3 초의 타이머 (다음 액티비티가 시작되어야하는 최대 값)를 가지며 onActivityResumed ( ) 다음 3 초 후에 호출하고 표시를 false로 변경하십시오. 마찬가지로 onActivityResumed ()에서 타이머가 있으면 취소합니다. 요약하면 보이는 것은 isAppInBackground입니다.

코드를 복사하여 붙여 넣을 수 없습니다 ...


다른 방법으로이 작업을 수행하는 것이 좋습니다.

프로그램이 시작되는 동안 시작 화면을 표시하고 싶습니다. 이미 백엔드에서 실행중인 경우 표시하지 마십시오.

응용 프로그램은 현재 시간을 특정 파일에 지속적으로 쓸 수 있습니다. 응용 프로그램이 시작되는 동안 current_time-last_time> 최근 시간을 쓰기 위해 지정한 시간 범위 인 경우 마지막 타임 스탬프를 확인하십시오. 이는 시스템 또는 사용자가 응용 프로그램을 종료했음을 의미합니다.

참고 URL : https://stackoverflow.com/questions/3667022/checking-if-an-android-application-is-running-in-the-background

반응형