Android 앱이 백그라운드로 이동하고 전경으로 돌아 오는시기를 감지하는 방법


382

일정 시간이 지나면 전경으로 돌아올 때 특정 작업을 수행하는 앱을 작성하려고합니다. 앱이 백그라운드로 전송되거나 포 그라운드로 가져 오는 시점을 감지하는 방법이 있습니까?


2
명확하지 않은 것처럼 보이기 때문에 유스 케이스를 질문에 추가 할 수 있으므로 주어진 답변에서 다루지 않습니다. 응용 프로그램은 다른 응용 프로그램 (예 : 갤러리)을 시작할 수 있습니다.이 응용 프로그램은 여전히 ​​동일한 스택에 있으며 응용 프로그램 화면 중 하나로 표시되며 홈 버튼을 누릅니다. 앱 수명주기 (또는 메모리 관리)에 의존하는 방법으로는이를 감지 할 수 없습니다. 홈을 누를 때가 아니라 외부 활동이 나타나면 바로 배경 상태를 트리거합니다.
Dennis K

이것은 당신이 찾고있는 답변입니다. stackoverflow.com/a/42679191/2352699
Fred Porciúncula

답변:


98

onPause()onResume()응용 프로그램을 다시 배경과 전경하게 될 때 방법이라고합니다. 그러나 응용 프로그램이 처음 시작될 때와 종료되기 전에 호출됩니다. 활동 에서 더 많은 것을 읽을 수 있습니다 .

백그라운드 또는 포 그라운드에있는 동안 응용 프로그램 상태를 얻는 직접적인 방법은 없지만이 문제에 직면하여 onWindowFocusChangedonStop .

자세한 내용은 여기를 확인하십시오. Android : Android 앱이 백그라운드로 이동하고 getRunningTasks 또는 getRunningAppProcesses없이 전경으로 돌아 오는시기를 감지하는 솔루션 입니다.


173
그러나 이러한 접근 방식은 다른 앱에서 지적한 것처럼 오 탐지를 유발합니다. 이러한 방법은 동일한 앱에서 활동간에 전환 할 때도 호출되기 때문입니다.
John Lehmann

9
그것보다 더 나쁘다. 나는 그것을 시도하고 때로는 전화가 잠겨있는 동안 onResume이 호출됩니다. 설명서에 onResume 정의가 표시되면 다음을 확인할 수 있습니다. onResume은 사용자의 활동이 사용자에게 표시되는 가장 좋은 지표가 아님을 명심하십시오. 키 가드와 같은 시스템 창이 앞에있을 수 있습니다. onWindowFocusChanged (boolean)를 사용하여 활동이 사용자에게 표시되는지 확인하십시오 (예 : 게임 재개). developer.android.com/reference/android/app/…
J-Rou

2
링크에 게시 된 솔루션은 onResume / onPause를 사용하지 않고 onBackPressed, onStop, onStart 및 onWindowsFocusChanged의 조합을 사용합니다. 그것은 나를 위해 일했고, 다소 복잡한 UI 계층 구조 (서랍, 동적
뷰 페이지

18
onPause 및 onResume은 활동에 따라 다릅니다. 신청이 아닙니다. 앱을 백그라운드에 놓고 다시 시작하면 백그라운드로 가기 전에 있던 특정 활동을 다시 시작합니다. 즉, 모든 애플리케이션 활동의 백그라운드에서 재개 할 때 수행하려는 모든 것을 구현해야합니다. 원래 질문은 활동이 아닌 응용 프로그램에 대한 "onResume"과 같은 것을 찾고 있다고 생각합니다.
SysHex

4
나는 일반적인 요구에 적합한 API가 제공되지 않는다고 믿을 수 없다. 처음에는 onUserLeaveHint ()가 잘라낼 것이라고 생각했지만 사용자가 응용 프로그램을 종료하는지 여부를 알 수 없습니다
atsakiridis

197

2018 : Android는 라이프 사이클 구성 요소를 통해이를 기본적으로 지원합니다.

2018 년 3 월 업데이트 : 이제 더 나은 솔루션이 있습니다. ProcessLifecycleOwner를 참조하십시오 . 새로운 아키텍처 구성 요소 1.1.0 (현재는 최신 버전)을 사용해야하지만 특별히 위해 설계되었습니다.

이 답변 에는 간단한 샘플 있지만 샘플 앱블로그 게시물을 작성했습니다. 을 작성했습니다.

2014 년에 이것을 쓴 이후로 다른 솔루션이 생겼습니다. 일부는 효과가 있었고 일부는 효과가 있다고 생각되었습니다 되었지만 (내를 포함하여!) 결함이 있었고 커뮤니티 (Android)로서 우리는 그 결과에 따라 생활하는 법을 배우고 특별한 경우에 대한 해결책을 썼습니다.

하나의 코드 스 니펫이 원하는 솔루션이라고 가정하지 마십시오. 더 나은 방법은 무엇을하고 왜 그렇게하는지 이해하는 것입니다.

MemoryBoss클래스는 실제로 여기에서 작성된대로 사용되지 않았습니다. 작동하는 의사 코드 일뿐입니다.

새로운 아키텍처 구성 요소를 사용하지 않아야 할 정당한 이유가없는 한 (특히 오래된 오래된 API를 대상으로하는 경우 일부가있는 경우) 계속 사용하십시오. 그들은 완벽하지는 않지만 둘 다 아닙니다 ComponentCallbacks2.

업데이트 / 참고 사항 (2015 년 11 월) : 사람들은 두 가지 의견을 제시했습니다. 먼저 문서 에서 정확한 값을 확인해서는 안되기 때문에 >=대신 사용해야합니다 . 이것은 당신이 경우 것이 대부분의 경우 미세하지만 곰 염두에두고 있습니다 하고 신경 뭔가를 앱이 배경에 갔을 때, 사용 ==해야합니다 도 (활동 라이프 사이클 콜백과 같은) 다른 솔루션과 결합하거나, 원하는 효과를 얻지 못할 수 있습니다. 예 (그리고 이것은 나에게 일어났다)는 당신이 잠그고 싶다면==백그라운드로 갈 때 암호 화면이있는 앱 (예 : 친숙한 경우 1Password와 같이), 메모리가 부족하고 갑자기 테스트하는 경우 실수로 앱을 잠글 수 있습니다 >= TRIM_MEMORY.Android에서 LOW MEMORY전화를 걸기 때문에 당신보다 더 높습니다. 테스트 방법 / 내용에주의하십시오.

또한 일부 사람들은 돌아올 때 감지하는 방법에 대해 물었습니다.

내가 생각할 수있는 가장 간단한 방법은 아래에 설명되어 있지만 일부 사람들은 익숙하지 않기 때문에 여기에 의사 코드를 추가하고 있습니다. 당신 YourApplicationMemoryBoss클래스 가 있다고 가정하면 class BaseActivity extends Activity(하나가 없다면 클래스 를 만들어야합니다).

@Override
protected void onStart() {
    super.onStart();

    if (mApplication.wasInBackground()) {
        // HERE YOU CALL THE CODE YOU WANT TO HAPPEN ONLY ONCE WHEN YOUR APP WAS RESUMED FROM BACKGROUND
        mApplication.setWasInBackground(false);
    }
}

대화 상자가 활동을 일시 중지 할 수 있으므로 전체 화면 대화 상자가 표시 된 경우 응용 프로그램이 "배경으로 갔다"고 생각하지 않기를 원하지만 마일리지가 다를 수 있기 때문에 onStart를 권장합니다.

그리고 그게 전부입니다. 은 if 블록의 코드는 것이다 번만 실행되는 다른 활동으로 이동하더라도, 새 (또한 extends BaseActivity보고는) wasInBackground이며 false,이 코드를 실행하지 않도록 할 때까지 onMemoryTrimmed호출되고 플래그가 다시 true로 설정되어 .

희망이 도움이됩니다.

업데이트 / 참고 사항 (2015 년 4 월) :이 코드에 대한 모든 복사 및 붙여 넣기 전에 100 % 신뢰할 수 없으며 최상의 결과를 얻으려면 다른 방법과 결합 해야하는 몇 가지 인스턴스를 발견했습니다 . 특히, 거기에 이 개 알려진 경우onTrimMemory 콜 다시 실행되도록 보장되지는 :

  1. 앱이 표시되어있는 동안 휴대 전화가 화면을 잠그면 (예 : 기기가 nn 분 후에 잠김) 잠금 화면이 맨 위에 있기 때문에이 콜백은 호출되지 않습니다 (또는 항상 그런 것은 아닙니다).

  2. 장치의 메모리가 부족하고 메모리가 부족한 경우 운영 체제는이 호출을 무시하고보다 중요한 수준으로 바로 넘어갑니다.

이제 앱이 백그라운드로 이동 한 시점을 아는 것이 얼마나 중요한지에 따라 활동 수명주기 등을 추적하면서이 솔루션을 함께 확장해야 할 수도 있고 그렇지 않을 수도 있습니다.

위의 사항을 명심하고 좋은 품질 보증 팀을 확보하십시오.)

업데이트 종료

늦었을 수도 있지만 Ice Cream Sandwich (API 14) 및 Above에 신뢰할 수있는 방법이 있습니다.

앱에 더 이상 UI가 표시되지 않으면 콜백이 트리거됩니다. 사용자 정의 클래스에서 구현할 수있는 콜백을 ComponentCallbacks2 라고합니다 (예, 둘). 이 콜백은 API 레벨 14 (Ice Cream Sandwich) 이상 에서만 사용할 수 있습니다 .

기본적으로 메소드를 호출합니다.

public abstract void onTrimMemory (int level)

레벨은 구체적으로 20 이상입니다

public static final int TRIM_MEMORY_UI_HIDDEN

나는 이것을 테스트 해왔으며 항상 작동합니다. 레벨 20은 앱이 더 이상 표시되지 않기 때문에 일부 리소스를 해제하려는 "추천"일뿐입니다.

공식 문서를 인용하려면 :

onTrimMemory (int) 레벨 : 프로세스에 사용자 인터페이스가 표시되어 더 이상 그렇게하지 않습니다. 이 시점에서 메모리를보다 잘 관리 할 수 ​​있도록 UI를 통한 많은 할당을 해제해야합니다.

물론, 실제로 말한 것을 수행하기 위해 이것을 구현해야합니다 (특정 시간 동안 사용되지 않은 메모리를 제거하고 사용하지 않은 일부 컬렉션을 지우십시오 등) 가능성은 무한합니다 (다른 가능한 자세한 내용은 공식 문서를 참조하십시오) 임계 수준).

그러나 흥미로운 점은 OS가 말하고 있다는 것입니다. HEY, 앱이 백그라운드로 갔다!

처음에 정확히 알고 싶었던 것입니다.

언제 돌아 왔는지 어떻게 알 수 있습니까?

쉽게 글쎄, 난 당신 때문에 당신이 "BaseActivity"가 확신 할 수 플래그 사실 당신있는 거 다시 당신의 onResume ()를 사용합니다. 당신이 돌아 오지 않는다고 말하는 유일한 시간은 실제로 위의 onTrimMemory메소드에 대한 호출을받는 시점이기 때문 입니다.

효과가있다. 당신은 오 탐지를 얻지 못합니다. 활동이 재개되면 100 %의 시간으로 돌아옵니다. 사용자가 다시 뒤로 가면 다른 onTrimMemory()전화를받습니다.

활동 (또는 더 나은 사용자 정의 클래스)을 등록해야합니다.

항상 이것을받을 수있는 가장 쉬운 방법은 다음과 같은 간단한 클래스를 만드는 것입니다.

public class MemoryBoss 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) {
            // We're in the Background
        }
        // you might as well implement some memory cleanup here and be a nice Android dev.
    }
}

이것을 사용하려면 응용 프로그램 구현 ( RIGHT? )에서 다음과 같이하십시오.

MemoryBoss mMemoryBoss;
@Override
public void onCreate() {
   super.onCreate();
   if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
      mMemoryBoss = new MemoryBoss();
      registerComponentCallbacks(mMemoryBoss);
   } 
}

당신이를 만들 경우 Interface당신은 추가 할 수 else그것에 if및 구현하는 ComponentCallbacksAPI (14) 아래에 아무것도에 사용 (2없이) 콜백 만 가지고 onLowMemory()방법을하고 당신이 배경에 갈 때 호출되지 않습니다 ,하지만 당신은 트림 메모리에 사용한다 .

이제 앱을 시작하고 집을 누르십시오. 귀하의 onTrimMemory(final int level)(: 추가 로깅 힌트) 메소드를 호출해야합니다.

마지막 단계는 콜백에서 등록을 취소하는 것입니다. 아마도 가장 좋은 곳은 onTerminate()앱 의 방법 이지만 실제 장치에서는 해당 방법이 호출되지 않습니다.

/**
 * This method is for use in emulated process environments.  It will
 * never be called on a production Android device, where processes are
 * removed by simply killing them; no user code (including this callback)
 * is executed when doing so.
 */

따라서 등록을 원하지 않는 상황이 아니라면 프로세스가 OS 수준에서 죽어 가고 있기 때문에 무시해도 안전합니다.

특정 시점에 등록을 취소하기로 결정한 경우 (예를 들어 앱이 정리 및 종료되도록 종료 메커니즘을 제공하는 경우) 다음을 수행 할 수 있습니다.

unregisterComponentCallbacks(mMemoryBoss);

그리고 그게 다야.


서비스에서 이것을 확인할 때 홈 버튼을 눌렀을 때만 작동하는 것 같습니다. KitKat에서 뒤로 버튼을 눌러도 실행되지 않습니다.
OpenGL ES 배우기

서비스에는 UI가 없으므로 관련이있을 수 있습니다. 서비스가 아닌 기본 활동을 점검하십시오. 당신은 당신의 UI가 언제 숨겨져 있는지 알고 싶을 것입니다 (그리고 아마도 서비스를 알려 주면 Foreground로갑니다)
Martin Marconcini

1
휴대 전화를 끄면 작동하지 않습니다. 트리거되지 않습니다.
Juangcg

2
Martin에서 고맙게도 ComponentCallbacks2.onTrimMemory ()를 ActivityLifecycleCallbacks와 함께 사용하는 것이 지금까지 찾은 신뢰할 수있는 유일한 솔루션입니다! 관심있는 사람들은 제공된 답변을 참조하십시오.
rickul

3
나는 1 년 전부터이 방법을 사용 해 왔으며 항상 신뢰할 수 있습니다. 다른 사람들도 그것을 사용한다는 것을 아는 것이 좋습니다. 나는 단지 level >= ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN업데이트 2, 포인트 2의 문제를 피하는 데 사용 합니다. 포인트 1과 관련하여 앱이 실제로 백그라운드로 이동하지 않았기 때문에 문제가되지 않습니다. 그래서 그것이 작동해야합니다.
sorianiv 2016 년

175

이 문제를 해결하는 방법은 다음과 같습니다. 활동 전환 사이의 시간 참조를 사용하면 앱이 "배경"인지 여부에 대한 적절한 증거를 제공 할 가능성이 높습니다.

먼저, 타이머, TimerTask, 한 활동에서 다른 활동으로의 전환이 합리적으로 걸릴 수있는 최대 밀리 초 수를 나타내는 상수가있는 android.app.Application 인스턴스 (MyApplication이라고 함)를 사용했습니다. 값이 2s)이고 앱이 "백그라운드"인지 여부를 나타내는 부울 값입니다.

public class MyApplication extends Application {

    private Timer mActivityTransitionTimer;
    private TimerTask mActivityTransitionTimerTask;
    public boolean wasInBackground;
    private final long MAX_ACTIVITY_TRANSITION_TIME_MS = 2000;
    ...

이 응용 프로그램은 타이머 / 태스크를 시작하고 중지하는 두 가지 방법도 제공합니다.

public void startActivityTransitionTimer() {
    this.mActivityTransitionTimer = new Timer();
    this.mActivityTransitionTimerTask = new TimerTask() {
        public void run() {
            MyApplication.this.wasInBackground = true;
        }
    };

    this.mActivityTransitionTimer.schedule(mActivityTransitionTimerTask,
                                           MAX_ACTIVITY_TRANSITION_TIME_MS);
}

public void stopActivityTransitionTimer() {
    if (this.mActivityTransitionTimerTask != null) {
        this.mActivityTransitionTimerTask.cancel();
    }

    if (this.mActivityTransitionTimer != null) {
        this.mActivityTransitionTimer.cancel();
    }

    this.wasInBackground = false;
}

이 솔루션의 마지막 부분은 모든 활동의 onResume () 및 onPause () 이벤트에서 또는 바람직하게는 모든 구체적인 활동이 상속하는 기본 활동에서 이러한 각 메소드에 대한 호출을 추가하는 것입니다.

@Override
public void onResume()
{
    super.onResume();

    MyApplication myApp = (MyApplication)this.getApplication();
    if (myApp.wasInBackground)
    {
        //Do specific came-here-from-background code
    }

    myApp.stopActivityTransitionTimer();
}

@Override
public void onPause()
{
    super.onPause();
    ((MyApplication)this.getApplication()).startActivityTransitionTimer();
}

따라서 사용자가 단순히 앱의 활동을 탐색하는 경우 출발 활동의 onPause ()가 타이머를 시작하지만 거의 즉시 입력되는 새 활동이 최대 전환 시간에 도달하기 전에 타이머를 취소합니다. 그래서 wasInBackground거짓 일 것입니다 입니다.

반면에 런처에서 활동이 포 그라운드로 오면 디바이스 깨우기, 전화 끊기 등이이 이벤트 이전에 실행 된 타이머 작업보다 많았으므로 wasInBackgroundtrue 로 설정되었습니다 .


4
안녕하세요 d60402, 귀하의 답변이 정말 도움이됩니다 ..이 답장을 보내 주셔서 감사합니다 ... 작은 통지 .. MyApplication은 android : name = "MyApplication"과 같은 매니페스트 파일 응용 프로그램 태그에서 언급해야합니다. 나와 같은 사람
praveenb

2
내가 지금까지 겪었던 가장 복잡한 문제 중 하나에 대한 훌륭한 프로그래머, 간단한 해결책의 표식.
Aashish Bhatnagar

2
멋진 솔루션! 감사. 누군가 "ClassCastException"오류가 발생하면 Manifest.xml <application android : name = "your.package.MyApplication"내의 응용 프로그램 태그에 추가하지 않은 것입니다.
Wahib Ul Haq

27
이것은 훌륭하고 간단한 구현입니다. 그러나 이것이 onPause / onResume이 아닌 onStart / onStop에서 구현되어야한다고 생각합니다. 활동을 부분적으로 다루는 대화 상자를 시작하더라도 onPause가 호출됩니다. 대화 상자를 닫으면 실제로 onResume이 호출되어 마치 앱이 마치 전경에 온 것처럼 보입니다.
Shubhayu

7
이 솔루션의 변형을 사용하고 싶습니다. 위에서 식별 된 대화에 대한 요점은 나에게 문제가되므로 @Shubhayu의 제안 (onStart / onStop)을 시도했습니다. 그러나 A-> B로 갈 때 활동 A의 onStop () 전에 활동 B의 onStart ()가 호출되기 때문에 도움이되지 않습니다.
Trevor

150

편집 : 새로운 아키텍처 구성 요소는 유망한 것을 가져 왔습니다 : ProcessLifecycleOwner , @vokilam의 답변 참조


Google I / O 토크 에 따른 실제 솔루션 :

class YourApplication : Application() {

  override fun onCreate() {
    super.onCreate()
    registerActivityLifecycleCallbacks(AppLifecycleTracker())
  }

}


class AppLifecycleTracker : Application.ActivityLifecycleCallbacks  {

  private var numStarted = 0

  override fun onActivityStarted(activity: Activity?) {
    if (numStarted == 0) {
      // app went to foreground
    }
    numStarted++
  }

  override fun onActivityStopped(activity: Activity?) {
    numStarted--
    if (numStarted == 0) {
      // app went to background
    }
  }

}

예. 우리는 여기에 너무 많은 이상한 해결책이 있기 때문에이 간단한 해결책이 효과가 있다고 믿기가 어렵다는 것을 알고 있습니다.

그러나 희망이 있습니다.


3
이것은 완벽하게 작동합니다! 나는 이미 너무 많은 결함이있는 너무 많은 이상한 해결책을 시도했습니다 ... 매우 감사합니다! 나는 이것을 잠시 동안 찾고 있었다.
Eggakin Baconwalker

7
그것은 여러 활동을 위해 작동하지만, 한 번의 onrotate는 모든 활동이 사라 졌거나 배경에 있음을 나타냅니다
deadfish

2
@Shyri는 정확하지만이 솔루션의 일부이므로 걱정할 필요가 있습니다. firebase가 이것에 의존한다면, 평범한 앱도 가능하다고 생각합니다. : BTW.
ElliotM

3
@deadfish 답변 상단에 제공된 I / O 링크를 확인하십시오. 활동 중지와 시작 사이의 시간 간격을 확인하여 실제로 백그라운드에 있는지 여부를 판별 할 수 있습니다. 이것은 실제로 훌륭한 솔루션입니다.
Alex Berdnikov

2
Java 솔루션이 있습니까? 코 틀린입니다.
Giacomo Bartoli

116

ProcessLifecycleOwner 유망한 솔루션 인 것 같습니다.

ProcessLifecycleOwner는 첫 번째 활동이 이러한 이벤트를 진행함에 따라 ON_START, ON_RESUME이벤트 를 발송 합니다. ON_PAUSE,, 마지막 활동이 ON_STOP이벤트를 통과 한 후 지연 되어 이벤트가 전달됩니다. 이 지연 시간은ProcessLifecycleOwner 구성 변경으로 인해 활동이 소멸 및 재 작성되는 경우 이벤트를 보내지 않을 .

구현은 다음과 같이 간단 할 수 있습니다.

class AppLifecycleListener : LifecycleObserver {

    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    fun onMoveToForeground() { // app moved to foreground
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    fun onMoveToBackground() { // app moved to background
    }
}

// register observer
ProcessLifecycleOwner.get().lifecycle.addObserver(AppLifecycleListener())

소스 코드에 따르면 현재 지연 값은 700ms 입니다.

또한이 기능을 사용하려면 다음이 필요합니다 dependencies.

implementation "androidx.lifecycle:lifecycle-extensions:$lifecycleVersion"

10
당신은 라이프 사이클 종속성을 추가 할 필요가 있음을 참고 implementation "android.arch.lifecycle:extensions:1.0.0"하고 annotationProcessor "android.arch.lifecycle:compiler:1.0.0"(즉, 구글의 저장소에서 google())
경 Codesalot

1
이것은 나에게 큰 도움이되었습니다. 감사합니다. 안드로이드 의존성이 컴파일 및 런타임 클래스 경로의 버전이 다르다는 오류로 인해 구현 대신 api 'android.arch.lifecycle : extensions : 1.1.0'을 사용해야했습니다.
FSUWX2011

이것은 활동 참조가 필요없는 모듈에서 작동하기 때문에 훌륭한 솔루션입니다!
최대

앱이 다운되면 작동하지 않습니다. 이 솔루션을 통해 또한 이벤트를 추락 응용 프로그램을 얻을 수있는 솔루션이 있습니까
tejraj

훌륭한 솔루션. 내 하루를 구했다.
Sunny

69

Martín Marconcinis의 답변 (감사합니다!)을 바탕으로 마침내 신뢰할 수 있고 매우 간단한 솔루션을 찾았습니다.

public class ApplicationLifecycleHandler implements Application.ActivityLifecycleCallbacks, ComponentCallbacks2 {

    private static final String TAG = ApplicationLifecycleHandler.class.getSimpleName();
    private static boolean isInBackground = false;

    @Override
    public void onActivityCreated(Activity activity, Bundle bundle) {
    }

    @Override
    public void onActivityStarted(Activity activity) {
    }

    @Override
    public void onActivityResumed(Activity activity) {

        if(isInBackground){
            Log.d(TAG, "app went to foreground");
            isInBackground = false;
        }
    }

    @Override
    public void onActivityPaused(Activity activity) {
    }

    @Override
    public void onActivityStopped(Activity activity) {
    }

    @Override
    public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {
    }

    @Override
    public void onActivityDestroyed(Activity activity) {
    }

    @Override
    public void onConfigurationChanged(Configuration configuration) {
    }

    @Override
    public void onLowMemory() {
    }

    @Override
    public void onTrimMemory(int i) {
        if(i == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN){
            Log.d(TAG, "app went to background");
            isInBackground = true;
        }
    }
}

그런 다음 이것을 Application 클래스의 onCreate ()에 추가하십시오.

public class MyApp extends android.app.Application {

    @Override
    public void onCreate() {
        super.onCreate();

        ApplicationLifeCycleHandler handler = new ApplicationLifeCycleHandler();
        registerActivityLifecycleCallbacks(handler);
        registerComponentCallbacks(handler);

    }

}

앱에서 이것을 어떻게 사용하는지 보여줄 수 있습니까, App 클래스 또는 다른 곳에서 호출합니까?
JPM

이건 정말 고마워요 !! 지금까지 테스트에서 훌륭하게 작동합니다
aherrick

이 예제는 불완전한 경우입니다. registerActivityLifecycleCallbacks 란 무엇입니까?
노먼

클래스 android.app.Application의 메소드
rickul

1
잘 했어요 +1 위로 가기, 완벽하기 때문에, 다른 답변을 보지 마십시오, 이것은 @reno 답변을 기반으로하지만 실제 예를 들어
Stoycho Andreev

63

이 방법을 사용합니다. 작동하기에는 너무 단순 해 보이지만 앱에서 잘 테스트되었으며 실제로 "홈"버튼, "리턴"버튼 또는 화면 잠금 후 홈 화면으로 이동하는 것을 포함하여 모든 경우에 놀랍도록 잘 작동합니다. 시도 해봐.

아이디어는 포 그라운드에서 Android가 항상 이전 활동을 중지하기 전에 항상 새로운 활동을 시작한다는 것입니다. 보장되지는 않지만 작동 방식입니다. BTW, Flurry는 동일한 논리를 사용하는 것 같습니다 (그런데, 나는 그것을 확인하지 않았지만 동일한 이벤트에 연결됩니다).

public abstract class BaseActivity extends Activity {

    private static int sessionDepth = 0;

    @Override
    protected void onStart() {
        super.onStart();       
        sessionDepth++;
        if(sessionDepth == 1){
        //app came to foreground;
        }
    }

    @Override
    protected void onStop() {
        super.onStop();
        if (sessionDepth > 0)
            sessionDepth--;
        if (sessionDepth == 0) {
            // app went to background
        }
    }

}

편집 : 주석에 따라 이후 버전의 코드에서 onStart ()로 이동했습니다. 또한 초기 코드에서 누락 된 슈퍼 호출을 추가하고 있습니다. 이는 작동 코드보다 개념이기 때문입니다.


2
onResume 대신 onStart를 사용하지만 가장 신뢰할 수있는 답변입니다.
Greg Ennis

재정의 된 메서드에서 super.onResume () 및 super.onStop ()에 대한 호출을 추가해야합니다. 그렇지 않으면 android.app.SuperNotCalledException이 발생합니다.
Jan Laussmann

1
나를 위해 그것은 작동하지 않습니다 ... 적어도 장치를 회전 할 때 이벤트가 발생합니다 (거짓 긍정적 인 imho).
Noya

매우 간단하고 효과적인 솔루션! 그러나 이전 활동의 일부를 볼 수 있도록 부분적으로 투명한 활동으로 작동하는지 확실하지 않습니다. 문서에서 onStop is called when the activity is no longer visible to the user.
Nicolas Buquet

3
사용자가 첫 번째 활동에서 방향을 변경하면 어떻게됩니까? 앱이 백그라운드로 진행되었다고보고하지만 사실이 아닙니다. 이 시나리오를 어떻게 처리합니까?
Nimrod Dayan

54

앱이 여러 활동 및 / 또는 탭 막대 위젯과 같은 누적 활동으로 구성된 경우 onPause () 및 onResume ()을 재정의하면 작동하지 않습니다. 즉, 새로운 활동을 시작할 때 새로운 활동이 생성되기 전에 현재 활동이 일시 중지됩니다. 활동을 완료 할 때 ( "뒤로"단추 사용) 동일하게 적용됩니다.

원하는대로 작동하는 두 가지 방법을 찾았습니다.

첫 번째는 GET_TASKS 권한이 필요하며 패키지 이름을 비교하여 장치에서 최상위 실행 활동이 애플리케이션에 속하는지 확인하는 간단한 메소드로 구성됩니다.

private boolean isApplicationBroughtToBackground() {
    ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
    List<RunningTaskInfo> tasks = am.getRunningTasks(1);
    if (!tasks.isEmpty()) {
        ComponentName topActivity = tasks.get(0).topActivity;
        if (!topActivity.getPackageName().equals(context.getPackageName())) {
            return true;
        }
    }

    return false;
}

이 방법은 Droid-Fu (현재 점화라고 함) 프레임 워크에서 발견되었습니다.

내가 직접 구현 한 두 번째 방법에는 GET_TASKS 권한이 필요하지 않습니다. 대신 구현하기가 조금 더 복잡합니다.

MainApplication 클래스에는 응용 프로그램에서 실행중인 활동 수를 추적하는 변수가 있습니다. 각 활동에 대한 onResume ()에서 변수를 늘리고 onPause ()에서 변수를 줄입니다.

실행중인 활동 수가 0에 도달하면 다음 조건에 해당하는 경우 애플리케이션이 백그라운드에 놓입니다.

  • 일시 정지 된 활동이 완료되지 않았습니다 ( "뒤로"단추가 사용됨). 이것은 activity.isFinishing () 메소드를 사용하여 수행 할 수 있습니다
  • 새로운 활동 (동일한 패키지 이름)이 시작되지 않습니다. startActivity () 메소드를 대체하여이를 나타내는 변수를 설정 한 후 onPostResume ()에서 재설정 할 수 있습니다. onPostResume ()은 활동이 작성 / 재개 될 때 마지막으로 실행되는 메소드입니다.

애플리케이션이 백그라운드로 사임되었음을 감지 할 수 있으면 애플리케이션이 다시 포 그라운드로 돌아올 때이를 쉽게 감지 할 수 있습니다.


18
Google은 아마도 ActivityManager.getRunningTasks ()를 사용하는 앱을 거부 할 것입니다. 이 문서에는 적의 목적만을위한 것이라고 명시되어 있습니다. developer.android.com/reference/android/app/…
Sky Kelsey


1
이러한 접근 방식을 조합하여 사용해야한다는 것을 알았습니다. onUserLeaveHint ()는 14에서 활동을 시작할 때 호출되었습니다.`@Override public void onUserLeaveHint () {inBackground = isApplicationBroughtToBackground (); }`
리스팅 보트

7
사용자는 강력한 권한 android.permission.GET_TASKS를 사용하는 것에 너무 만족하지 않습니다.
MSquare

6
getRunningTasks는 API 레벨 21에서 사용되지 않습니다.
Noya

33

확장 하는 클래스 를 만듭니다 Application. 그런 다음 재정의 방법을 사용할 수 있습니다 onTrimMemory().

애플리케이션이 백그라운드로 이동했는지 감지하기 위해 다음을 사용합니다.

 @Override
    public void onTrimMemory(final int level) {
        if (level == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) { // Works for Activity
            // Get called every-time when application went to background.
        } 
        else if (level == ComponentCallbacks2.TRIM_MEMORY_COMPLETE) { // Works for FragmentActivty
        }
    }

1
들어 FragmentActivity당신은 또한 추가 할 수 있습니다 level == ComponentCallbacks2.TRIM_MEMORY_COMPLETE너무.
Srujan Simha

2
이 방법을 지적 해 주셔서 감사합니다. 사용자가 백그라운드 활동을 다시 시작할 때마다 핀 대화 상자를 표시 하고이 방법을 사용하여 pref 값을 작성하고 baseActivity 에서이 값을 확인해야합니다.
Sam

18

onUserLeaveHint 사용을 고려하십시오. 앱이 백그라운드로 들어갈 때만 호출됩니다. onPause는 다른 이유로 호출 될 수 있으므로 처리해야 할 코너 케이스가 있습니다. 예를 들어, 사용자가 설정 페이지와 같은 앱에서 다른 활동을 열면 주 활동의 onPause 메소드가 여전히 앱에 있어도 호출됩니다. 무슨 일이 일어나고 있는지 추적하면 대신 요청하는 것을 수행하는 onUserLeaveHint 콜백을 간단히 사용할 수있을 때 버그가 발생합니다.

UserLeaveHint가 호출되면 부울 inBackground 플래그를 true로 설정할 수 있습니다. onResume이 호출되면 inBackground 플래그가 설정된 경우에만 전경으로 돌아 왔다고 가정하십시오. 사용자가 설정 메뉴에 있고 앱을 떠나지 않은 경우 기본 활동에서 onResume이 호출되기 때문입니다.

설정 화면에서 사용자가 홈 버튼을 누르면 설정 활동에서 onUserLeaveHint가 호출되고 설정 활동에서 onResume이 호출 될 때 기억됩니다. 기본 활동에이 탐지 코드 만있는 경우이 사용 사례를 놓치게됩니다. 코드를 복제하지 않고 모든 활동에이 코드를 사용하려면 활동을 확장하는 추상 활동 클래스를 작성하고 공통 코드를 입력하십시오. 그런 다음 각 활동이이 추상 활동을 확장 할 수 있습니다.

예를 들면 다음과 같습니다.

public abstract AbstractActivity extends Activity {
    private static boolean inBackground = false;

    @Override
    public void onResume() {
        if (inBackground) {
            // You just came from the background
            inBackground = false;
        }
        else {
            // You just returned from another activity within your own app
        }
    }

    @Override
    public void onUserLeaveHint() {
        inBackground = true;
    }
}

public abstract MainActivity extends AbstractActivity {
    ...
}

public abstract SettingsActivity extends AbstractActivity {
    ...
}

19
onUserLeaveHint는 다른 활동을 탐색 할 때도 호출됩니다
Jonas Stawski

3
onUserLeaveHint는 예를 들어 전화가 걸려오고 전화 활동이 활성화 될 때 호출되지 않으므로이 경우도 마찬가지입니다. onUserLeaveHint 호출을 억제하려는 의도에 플래그를 추가 할 수 있기 때문에 다른 경우도있을 수 있습니다. developer.android.com/reference/android/content/…
Groxx

1
또한 onResume이 제대로 작동하지 않습니다. 나는 그것을 시도하고 때로는 전화가 잠겨있는 동안 onResume이 호출됩니다. 설명서에 onResume 정의가 표시되면 다음을 확인할 수 있습니다. onResume은 사용자의 활동이 사용자에게 표시되는 가장 좋은 지표가 아님을 명심하십시오. 키 가드와 같은 시스템 창이 앞에있을 수 있습니다. onWindowFocusChanged (boolean)를 사용하여 활동이 사용자에게 표시되는지 확인하십시오 (예 : 게임 재개). developer.android.com/reference/android/app/…
J-Rou

이 솔루션은 활동이 여러 개인 경우 포 그라운드 / 백그라운드를 결정하는 데 도움이되지 않습니다.
PLZ

14

ActivityLifecycleCallbacks 는 관심이있을 수 있지만 잘 문서화되어 있지 않습니다.

registerActivityLifecycleCallbacks () 를 호출 하면 활동이 작성, 소멸되는 등의 콜백을 얻을 수 있어야합니다 . 활동에 대해 getComponentName ()을 호출 할 수 있습니다 .


11
api level 14 이후 = =
imort

이 것이 깨끗하고 나를 위해 일하는 것처럼 보입니다. 감사합니다
duanbo1983

이것은 동일한 활동 수명주기에 의존하는 허용되는 답변과 어떻게 다릅니 까?
사이타마

13

android.arch.lifecycle의 패키지는 라이프 사이클 인식 구성 요소를 만들 수있는 클래스와 인터페이스를 제공합니다

응용 프로그램은 LifecycleObserver 인터페이스를 구현해야합니다.

public class MyApplication extends Application implements LifecycleObserver {

    @Override
    public void onCreate() {
        super.onCreate();
        ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    private void onAppBackgrounded() {
        Log.d("MyApp", "App in background");
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    private void onAppForegrounded() {
        Log.d("MyApp", "App in foreground");
    }
}

이렇게하려면이 종속성을 build.gradle 파일에 추가해야합니다.

dependencies {
    implementation "android.arch.lifecycle:extensions:1.1.1"
}

Google에서 권장하는대로 수명주기 활동 방식에서 실행되는 코드를 최소화해야합니다.

일반적인 패턴은 활동 및 단편의 라이프 사이클 방법에서 종속 컴포넌트의 조치를 구현하는 것입니다. 그러나이 패턴으로 인해 코드가 제대로 구성되지 않고 오류가 확산됩니다. 수명주기 인식 구성 요소를 사용하면 종속 구성 요소의 코드를 수명주기 방법에서 구성 요소 자체로 옮길 수 있습니다.

자세한 내용은 https://developer.android.com/topic/libraries/architecture/lifecycle을 참조하십시오.


<application android : name = ". AnotherApp">
Dan Alboteanu

9

응용 프로그램에서 콜백을 추가하고 다음과 같은 방법으로 루트 활동을 확인하십시오.

@Override
public void onCreate() {
    super.onCreate();
    registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
        @Override
        public void onActivityStopped(Activity activity) {
        }

        @Override
        public void onActivityStarted(Activity activity) {
        }

        @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) {
            if (activity.isTaskRoot() && !(activity instanceof YourSplashScreenActivity)) {
                Log.e(YourApp.TAG, "Reload defaults on restoring from background.");
                loadDefaults();
            }
        }
    });
}

이 구현 방식을 사용하는 것이 좋습니다. 한 활동에서 다른 활동으로 전환하는 데 몇 밀리 초가 걸립니다. 마지막 활동이 사라진 시간을 기준으로 특정 전략으로 사용자를 다시 로그인하는 것으로 간주 할 수 있습니다.
drindt

6

Github app-foreground-background-listen 에서 프로젝트를 만들었습니다.

애플리케이션의 모든 활동에 대한 BaseActivity를 작성하십시오.

public class BaseActivity extends Activity {

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
    }

    public static boolean isAppInFg = false;
    public static boolean isScrInFg = false;
    public static boolean isChangeScrFg = false;

    @Override
    protected void onStart() {
        if (!isAppInFg) {
            isAppInFg = true;
            isChangeScrFg = false;
            onAppStart();
        }
        else {
            isChangeScrFg = true;
        }
        isScrInFg = true;

        super.onStart();
    }

    @Override
    protected void onStop() {
        super.onStop();

        if (!isScrInFg || !isChangeScrFg) {
            isAppInFg = false;
            onAppPause();
        }
        isScrInFg = false;
    }

    public void onAppStart() {

        // Remove this toast
        Toast.makeText(getApplicationContext(), "App in foreground",    Toast.LENGTH_LONG).show();

        // Your code
    }

    public void onAppPause() {

        // Remove this toast
        Toast.makeText(getApplicationContext(), "App in background",  Toast.LENGTH_LONG).show();

        // Your code
    }
}

이제이 BaseActivity를 MainActivity와 같이 모든 Activity의 수퍼 클래스로 사용하십시오 .BaseActivity가 확장되고 응용 프로그램을 시작할 때 onAppStart가 호출되고 응용 프로그램이 어떤 화면에서 백그라운드로 갈 때 onAppPause ()가 호출됩니다.


@ kiran boghra : 솔루션에 잘못된 긍정이 있습니까?
Harish Vishwakarma

이 경우 onStart () 및 onStop () 함수를 사용할 수 있습니다. 앱에 대한 정보
Pir Fahim Shah

6

ProcessLifecycleOwner를 사용하면 매우 쉽습니다.

의존성을 추가하십시오

implementation "android.arch.lifecycle:extensions:$project.archLifecycleVersion"
kapt "android.arch.lifecycle:compiler:$project.archLifecycleVersion"

에서 코 틀린 :

class ForegroundBackgroundListener : LifecycleObserver {


    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    fun startSomething() {
        Log.v("ProcessLog", "APP IS ON FOREGROUND")
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    fun stopSomething() {
        Log.v("ProcessLog", "APP IS IN BACKGROUND")
    }
}

그런 다음 기본 활동에서 :

override fun onCreate() {
        super.onCreate()

        ProcessLifecycleOwner.get()
                .lifecycle
                .addObserver(
                        ForegroundBackgroundListener()
                                .also { appObserver = it })
    }

이 주제에 대한 내 기사를 참조하십시오 : https://medium.com/@egek92/how-to-actually-detect-foreground-background-changes-in-your-android-application-without-wanting-9719cc822c48


5

라이프 사이클 관찰자를 첨부하는 ProcessLifecycleOwner 를 사용할 수 있습니다 .

  public class ForegroundLifecycleObserver implements LifecycleObserver {

    @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
    public void onAppCreated() {
        Timber.d("onAppCreated() called");
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    public void onAppStarted() {
        Timber.d("onAppStarted() called");
    }

    @OnLifecycleEvent(Event.ON_RESUME)
    public void onAppResumed() {
        Timber.d("onAppResumed() called");
    }

    @OnLifecycleEvent(Event.ON_PAUSE)
    public void onAppPaused() {
        Timber.d("onAppPaused() called");
    }

    @OnLifecycleEvent(Event.ON_STOP)
    public void onAppStopped() {
        Timber.d("onAppStopped() called");
    }
}

그런 다음 onCreate()Application 클래스에서 다음을 호출합니다.

ProcessLifecycleOwner.get().getLifecycle().addObserver(new ForegroundLifecycleObserver());

이를 통해 백그라운드에서 발생할 때 발생하는 응용 프로그램 ON_PAUSE및 이벤트의 이벤트를 캡처 할 수 있습니다 ON_STOP.


4

전체 애플리케이션이 백그라운드 / 전경이 될 때 알려주는 간단한 수명주기 방법은 없습니다.

나는 간단한 방법으로 이것을했다. 응용 프로그램 배경 / 전경 단계를 감지하려면 아래 지침을 따르십시오.

약간의 해결 방법으로 가능합니다. 여기서 ActivityLifecycleCallbacks 가 구출됩니다. 단계별로 살펴 보겠습니다.

  1. 먼저 android.app.Application 을 확장 하고 ActivityLifecycleCallbacks 인터페이스를 구현 하는 클래스를 만듭니다 . Application.onCreate ()에서 콜백을 등록하십시오.

    public class App extends Application implements 
        Application.ActivityLifecycleCallbacks {
    
        @Override
        public void onCreate() {
            super.onCreate();
            registerActivityLifecycleCallbacks(this);
        }
    }
  2. 아래와 같이 매니페스트에“앱”클래스를 등록하십시오 <application android:name=".App".

  3. 앱이 포 그라운드에있을 때 시작 상태에있는 활동이 하나 이상 있고 앱이 백그라운드에있을 때 시작 상태에있는 활동이 없습니다.

    “App”클래스에서 아래와 같이 2 개의 변수를 선언하십시오.

    private int activityReferences = 0;
    private boolean isActivityChangingConfigurations = false;

    activityReferences시작된 상태 의 활동 수를 유지합니다 . isActivityChangingConfigurations현재 활동이 방향 전환과 같은 구성 변경을 진행 중인지 여부를 나타내는 플래그입니다.

  4. 다음 코드를 사용하면 앱이 포 그라운드인지 감지 할 수 있습니다.

    @Override
    public void onActivityStarted(Activity activity) {
        if (++activityReferences == 1 && !isActivityChangingConfigurations) {
            // App enters foreground
        }
    }
  5. 앱이 백그라운드인지 확인하는 방법입니다.

    @Override
    public void onActivityStopped(Activity activity) {
        isActivityChangingConfigurations = activity.isChangingConfigurations();
        if (--activityReferences == 0 && !isActivityChangingConfigurations) {
            // App enters background
        }
    }

작동 방식 :

Lifecycle 메소드가 순차적으로 호출되는 방식으로 수행되는 약간의 트릭입니다. 시나리오를 살펴 보겠습니다.

사용자가 앱을 시작하고 실행기 활동 A가 시작되었다고 가정합니다. 수명주기 통화는

A.onCreate ()

A.onStart () (++ activityReferences == 1) (앱이 Foreground에 들어감)

A.onResume ()

이제 활동 A가 활동 B를 시작합니다.

A.onPause ()

B.onCreate ()

B.onStart () (++ activityReferences == 2)

B.onResume ()

A.onStop () (--activityReferences == 1)

그런 다음 사용자는 활동 B에서 다시 탐색합니다.

B.onPause ()

A.onStart () (++ activityReferences == 2)

A.onResume ()

B.onStop () (--activityReferences == 1)

B.onDestroy ()

그런 다음 사용자는 홈 버튼을 누릅니다.

A.onPause ()

A.onStop () (--activityReferences == 0) (앱이 백그라운드에 들어감)

사용자가 뒤로 버튼 대신 액티비티 B에서 홈 버튼을 누르면 여전히 동일하며 액티비티 참조는 0 . 따라서 백그라운드로 진입하는 앱으로 감지 할 수 있습니다.

그래서, 역할은 isActivityChangingConfigurations무엇입니까? 위 시나리오에서 활동 B가 방향을 변경한다고 가정하십시오. 콜백 시퀀스는

B.onPause ()

B.onStop () (--activityReferences == 0) (앱이 백그라운드로 들어 갑니까 ??)

B.onDestroy ()

B.onCreate ()

B.onStart () (++ activityReferences == 1) (앱이 Foreground로 들어 갑니까 ??)

B.onResume ()

그렇기 때문에 isActivityChangingConfigurations활동이 구성 변경을 겪을 때 시나리오를 피하기 위해 추가 검사가 필요한 이유 입니다.


3

전경 또는 배경을 입력하는지 여부에 관계없이 응용 프로그램을 감지하는 좋은 방법을 찾았습니다. 여기 내 코드가 있습니다. 이것이 당신을 돕기를 바랍니다.

/**
 * Custom Application which can detect application state of whether it enter
 * background or enter foreground.
 *
 * @reference http://www.vardhan-justlikethat.blogspot.sg/2014/02/android-solution-to-detect-when-android.html
 */
 public abstract class StatusApplication extends Application implements ActivityLifecycleCallbacks {

public static final int STATE_UNKNOWN = 0x00;
public static final int STATE_CREATED = 0x01;
public static final int STATE_STARTED = 0x02;
public static final int STATE_RESUMED = 0x03;
public static final int STATE_PAUSED = 0x04;
public static final int STATE_STOPPED = 0x05;
public static final int STATE_DESTROYED = 0x06;

private static final int FLAG_STATE_FOREGROUND = -1;
private static final int FLAG_STATE_BACKGROUND = -2;

private int mCurrentState = STATE_UNKNOWN;
private int mStateFlag = FLAG_STATE_BACKGROUND;

@Override
public void onCreate() {
    super.onCreate();
    mCurrentState = STATE_UNKNOWN;
    registerActivityLifecycleCallbacks(this);
}

@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
    // mCurrentState = STATE_CREATED;
}

@Override
public void onActivityStarted(Activity activity) {
    if (mCurrentState == STATE_UNKNOWN || mCurrentState == STATE_STOPPED) {
        if (mStateFlag == FLAG_STATE_BACKGROUND) {
            applicationWillEnterForeground();
            mStateFlag = FLAG_STATE_FOREGROUND;
        }
    }
    mCurrentState = STATE_STARTED;

}

@Override
public void onActivityResumed(Activity activity) {
    mCurrentState = STATE_RESUMED;

}

@Override
public void onActivityPaused(Activity activity) {
    mCurrentState = STATE_PAUSED;

}

@Override
public void onActivityStopped(Activity activity) {
    mCurrentState = STATE_STOPPED;

}

@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {

}

@Override
public void onActivityDestroyed(Activity activity) {
    mCurrentState = STATE_DESTROYED;
}

@Override
public void onTrimMemory(int level) {
    super.onTrimMemory(level);
    if (mCurrentState == STATE_STOPPED && level >= TRIM_MEMORY_UI_HIDDEN) {
        if (mStateFlag == FLAG_STATE_FOREGROUND) {
            applicationDidEnterBackground();
            mStateFlag = FLAG_STATE_BACKGROUND;
        }
    }else if (mCurrentState == STATE_DESTROYED && level >= TRIM_MEMORY_UI_HIDDEN) {
        if (mStateFlag == FLAG_STATE_FOREGROUND) {
            applicationDidDestroyed();
            mStateFlag = FLAG_STATE_BACKGROUND;
        }
    }
}

/**
 * The method be called when the application been destroyed. But when the
 * device screen off,this method will not invoked.
 */
protected abstract void applicationDidDestroyed();

/**
 * The method be called when the application enter background. But when the
 * device screen off,this method will not invoked.
 */
protected abstract void applicationDidEnterBackground();

/**
 * The method be called when the application enter foreground.
 */
protected abstract void applicationWillEnterForeground();

}


3

당신이 사용할 수있는:

protected void onRestart ()

새로운 시작과 다시 시작이 다릅니다.

여기에 이미지 설명을 입력하십시오


3

편집 2 : 내가 쓴 내용은 실제로 작동하지 않습니다. Google은 ActivityManager.getRunningTasks ()에 대한 호출을 포함하는 앱을 거부했습니다. 에서 문서 ,이 API 디버깅 및 개발 용으로 만 사용됩니다 것을 알 수있다. 타이머를 사용하고 거의 좋은 새로운 체계로 아래 GitHub 프로젝트를 업데이트 할 시간이 생기면이 게시물을 업데이트 할 것입니다.

편집 1 : 블로그 게시물 을 작성 하고 이를 쉽게하기 위해 간단한 GitHub 저장소 를 만들었습니다 .

인정 된 답변과 최고 등급의 답변이 모두 최선의 방법은 아닙니다. 최상위 응용 프로그램의 isApplicationBroughtToBackground () 구현은 응용 프로그램의 기본 활동이 동일한 응용 프로그램에 정의 된 활동으로 양보되는 상황을 처리하지 않지만 Java 패키지는 다릅니다. 나는이 경우에 효과가있는 방법을 생각해 냈습니다.

onPause ()에서 이것을 호출하면 다른 응용 프로그램이 시작되었거나 사용자가 홈 버튼을 눌렀 기 때문에 응용 프로그램이 백그라운드로 가고 있는지 알려줍니다.

public static boolean isApplicationBroughtToBackground(final Activity activity) {
  ActivityManager activityManager = (ActivityManager) activity.getSystemService(Context.ACTIVITY_SERVICE);
  List<ActivityManager.RunningTaskInfo> tasks = activityManager.getRunningTasks(1);

  // Check the top Activity against the list of Activities contained in the Application's package.
  if (!tasks.isEmpty()) {
    ComponentName topActivity = tasks.get(0).topActivity;
    try {
      PackageInfo pi = activity.getPackageManager().getPackageInfo(activity.getPackageName(), PackageManager.GET_ACTIVITIES);
      for (ActivityInfo activityInfo : pi.activities) {
        if(topActivity.getClassName().equals(activityInfo.name)) {
          return false;
        }
      }
    } catch( PackageManager.NameNotFoundException e) {
      return false; // Never happens.
    }
  }
  return true;
}

참고로, onStart ()에서 이것을 호출하면 간단한 대화 상자가 발생했을 때 호출되는 것을 피할 수 있습니다.
스카이 켈지

2

정답은 여기

아래와 같이 이름이 MyApp 인 클래스를 만듭니다.

public class MyApp implements Application.ActivityLifecycleCallbacks, ComponentCallbacks2 {

    private Context context;
    public void setContext(Context context)
    {
        this.context = context;
    }

    private boolean isInBackground = false;

    @Override
    public void onTrimMemory(final int level) {
        if (level == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {


            isInBackground = true;
            Log.d("status = ","we are out");
        }
    }


    @Override
    public void onActivityCreated(Activity activity, Bundle bundle) {

    }

    @Override
    public void onActivityStarted(Activity activity) {

    }

    @Override
    public void onActivityResumed(Activity activity) {

        if(isInBackground){

            isInBackground = false;
            Log.d("status = ","we are in");
        }

    }

    @Override
    public void onActivityPaused(Activity activity) {

    }

    @Override
    public void onActivityStopped(Activity activity) {

    }

    @Override
    public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {

    }

    @Override
    public void onActivityDestroyed(Activity activity) {

    }

    @Override
    public void onConfigurationChanged(Configuration configuration) {

    }

    @Override
    public void onLowMemory() {

    }
}

그런 다음 원하는 모든 곳에서 (앱에서 처음 시작된 활동보다 나은) 아래 코드를 추가하십시오.

MyApp myApp = new MyApp();
registerComponentCallbacks(myApp);
getApplication().registerActivityLifecycleCallbacks(myApp);

끝난! 이제 앱이 백그라운드에 있으면 로그 status : we are out 가 표시되고 앱에 들어가면 로그가 표시됩니다.status : we are out


1

내 솔루션은 @ d60402의 답변에서 영감을 얻었으며 시간 창에 의존하지만 다음을 사용하지 않습니다 Timer.

public abstract class BaseActivity extends ActionBarActivity {

  protected boolean wasInBackground = false;

  @Override
  protected void onStart() {
    super.onStart();
    wasInBackground = getApp().isInBackground;
    getApp().isInBackground = false;
    getApp().lastForegroundTransition = System.currentTimeMillis();
  }

  @Override
  protected void onStop() {
    super.onStop();
    if( 1500 < System.currentTimeMillis() - getApp().lastForegroundTransition )
      getApp().isInBackground = true;
  }

  protected SingletonApplication getApp(){
    return (SingletonApplication)getApplication();
  }
}

여기서 클래스 SingletonApplication의 확장입니다 Application.

public class SingletonApplication extends Application {
  public boolean isInBackground = false;
  public long lastForegroundTransition = 0;
}

1

Google Analytics EasyTracker와 함께 이것을 사용하고 있었고 효과가있었습니다. 간단한 정수를 사용하여 원하는 것을 수행하도록 확장 할 수 있습니다.

public class MainApplication extends Application {

    int isAppBackgrounded = 0;

    @Override
    public void onCreate() {
        super.onCreate();
        appBackgroundedDetector();
    }

    private void appBackgroundedDetector() {
        registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
            @Override
            public void onActivityCreated(Activity activity, Bundle bundle) {

            }

            @Override
            public void onActivityStarted(Activity activity) {
                EasyTracker.getInstance(MainApplication.this).activityStart(activity);
            }

            @Override
            public void onActivityResumed(Activity activity) {
                isAppBackgrounded++;
                if (isAppBackgrounded > 0) {
                    // Do something here
                }
            }

            @Override
            public void onActivityPaused(Activity activity) {
                isAppBackgrounded--;
            }

            @Override
            public void onActivityStopped(Activity activity) {
                EasyTracker.getInstance(MainApplication.this).activityStop(activity);
            }

            @Override
            public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {

            }

            @Override
            public void onActivityDestroyed(Activity activity) {

            }
        });
    }
}

1

나는 그것의 조금 늦게 알고 있지만 아래 에서처럼하고 모든 것이 완벽하게 작동하는 동안이 모든 대답에 몇 가지 문제가 있다고 생각합니다.

다음과 같이 액티비티 라이프 사이클 콜백을 만듭니다.

 class ActivityLifeCycle implements ActivityLifecycleCallbacks{

    @Override
    public void onActivityCreated(Activity activity, Bundle savedInstanceState) {

    }

    @Override
    public void onActivityStarted(Activity activity) {

    }

    Activity lastActivity;
    @Override
    public void onActivityResumed(Activity activity) {
        //if (null == lastActivity || (activity != null && activity == lastActivity)) //use this condition instead if you want to be informed also when  app has been killed or started for the first time
        if (activity != null && activity == lastActivity) 
        {
            Toast.makeText(MyApp.this, "NOW!", Toast.LENGTH_LONG).show();
        }

        lastActivity = activity;
    }

    @Override
    public void onActivityPaused(Activity activity) {

    }

    @Override
    public void onActivityStopped(Activity activity) {

    }

    @Override
    public void onActivitySaveInstanceState(Activity activity, Bundle outState) {

    }

    @Override
    public void onActivityDestroyed(Activity activity) {

    }
}

다음과 같이 응용 프로그램 클래스에 등록하십시오.

public class MyApp extends Application {

@Override
public void onCreate() {
    super.onCreate();
    registerActivityLifecycleCallbacks(new ActivityLifeCycle());
}

각 활동에서 항상 호출됩니다. 예를 들어 사용자 온라인 상태를 감지하려는 경우이 기능을 사용하는 방법
Maksim Kniazev

그것이 질문이 원하는 것입니다. 홈 화면으로 이동하여 활동으로 돌아갈 때만 호출됩니다.
아미르 지아 라티

인터넷 연결을 의미한다면 필요할 때 확인하는 것이 좋습니다. API를 호출 해야하는 경우 전화 직전에 인터넷 연결을 확인하십시오.
아미르 지아 라티

1

이 나타납니다 안드로이드는 아이폰 OS의 등가물이없는 (이 글을 쓰는 현재)부터 안드로이드에서 가장 복잡한 문제 중 하나가 될 수 있습니다 applicationDidEnterBackground()또는 applicationWillEnterForeground()콜백을. @jenzz 가 만든 AppState Library 를 사용했습니다 .

[AppState]는 앱 상태 변경을 모니터링하는 RxJava를 기반으로하는 단순하고 반응적인 Android 라이브러리입니다. 앱이 백그라운드로 전환되고 다시 포 그라운드로 돌아올 때마다 구독자에게 알립니다.

특히 앱에 여러 활동이 있으므로 단순히 확인 onStart()하거나 onStop()활동을 차단하지 않았기 때문에 이것이 필요한 것입니다.

먼저 이러한 종속성을 gradle에 추가했습니다.

dependencies {
    compile 'com.jenzz.appstate:appstate:3.0.1'
    compile 'com.jenzz.appstate:adapter-rxjava2:3.0.1'
}

그런 다음이 줄을 코드의 적절한 위치에 추가하는 것이 간단했습니다.

//Note that this uses RxJava 2.x adapter. Check the referenced github site for other ways of using observable
Observable<AppState> appState = RxAppStateMonitor.monitor(myApplication);
//where myApplication is a subclass of android.app.Application
appState.subscribe(new Consumer<AppState>() {
    @Override
    public void accept(@io.reactivex.annotations.NonNull AppState appState) throws Exception {
        switch (appState) {
            case FOREGROUND:
                Log.i("info","App entered foreground");
                break;
            case BACKGROUND:
                Log.i("info","App entered background");
                break;
        }
    }
});

옵저버 블을 구독하는 방법에 따라 메모리 누수를 피하기 위해 옵저버 블을 구독 해제해야 할 수도 있습니다. github 페이지 에 대한 추가 정보 .


1

이것은 @ d60402의 답변의 수정 된 버전입니다 : https://stackoverflow.com/a/15573121/4747587

거기에 언급 된 모든 것을하십시오. 그러나 대신에있는의 Base Activity모든 활동에 대한 부모로서 그 제작하고,이를 무시 onResume()하고 onPause의를 아래 수행

애플리케이션 클래스에서 다음 행을 추가하십시오.

registerActivityLifecycleCallbacks (Application.ActivityLifecycleCallbacks 콜백);

callback모든 활동의 라이프 사이클 방식을 가지고 있으며, 당신은 지금 대체 할 수 있습니다 onActivityResumed()onActivityPaused().

이 요지를 살펴보십시오 : https://gist.github.com/thsaravana/1fa576b6af9fc8fff20acfb2ac79fa1b


1

당신의 도움으로 쉽게 달성 할 수 ActivityLifecycleCallbacksComponentCallbacks2 다음과 같은.

AppLifeCycleHandler상기 인터페이스 위에 구현 하는 클래스를 작성하십시오 .

package com.sample.app;

import android.app.Activity;
import android.app.Application;
import android.content.ComponentCallbacks2;
import android.content.res.Configuration;
import android.os.Bundle;

/**
 * Created by Naveen on 17/04/18
 */
public class AppLifeCycleHandler
    implements Application.ActivityLifecycleCallbacks, ComponentCallbacks2 {

  AppLifeCycleCallback appLifeCycleCallback;

  boolean appInForeground;

  public AppLifeCycleHandler(AppLifeCycleCallback appLifeCycleCallback) {
    this.appLifeCycleCallback = appLifeCycleCallback;
  }

  @Override
  public void onActivityResumed(Activity activity) {
    if (!appInForeground) {
      appInForeground = true;
      appLifeCycleCallback.onAppForeground();
    }
  }

  @Override
  public void onTrimMemory(int i) {
    if (i == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
      appInForeground = false;
      appLifeCycleCallback.onAppBackground();
    }
  }

  @Override
  public void onActivityCreated(Activity activity, Bundle bundle) {

  }

  @Override
  public void onActivityStarted(Activity activity) {

  }

  @Override
  public void onActivityPaused(Activity activity) {

  }

  @Override
  public void onActivityStopped(Activity activity) {

  }

  @Override
  public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {

  }

  @Override
  public void onActivityDestroyed(Activity activity) {

  }

  @Override
  public void onConfigurationChanged(Configuration configuration) {

  }

  @Override
  public void onLowMemory() {

  }

  interface AppLifeCycleCallback {

    void onAppBackground();

    void onAppForeground();
  }
}

클래스에서 앱이 전경과 배경 사이를 전환 할 때 콜백을 얻도록 Application구현 AppLifeCycleCallback을 확장 합니다. 아래와 같은 것.

public class BaseApplication extends Application implements AppLifeCycleHandler.AppLifeCycleCallback{

    @Override
    public void onCreate() {
        super.onCreate();
        AppLifeCycleHandler appLifeCycleHandler = new AppLifeCycleHandler(this);
        registerActivityLifecycleCallbacks(appLifeCycleHandler);
        registerComponentCallbacks(appLifeCycleHandler);
    }

    @Override
    public void onAppBackground() {
        Log.d("LifecycleEvent", "onAppBackground");
    }

    @Override
    public void onAppForeground() {
        Log.d("LifecycleEvent", "onAppForeground");
    }
}

도움이 되었기를 바랍니다.

편집 대안으로 이제 수명주기 인식 아키텍처 구성 요소를 사용할 수 있습니다.


1

타임 스탬프를 확인하지 않고 회전을 처리하는 접근법을 찾지 못했기 때문에 이제 앱에서 어떻게 수행하는지 공유한다고 생각했습니다. 이 답변 https://stackoverflow.com/a/42679191/5119746 에 대한 유일한 추가 사항은 방향도 고려한다는 것입니다.

class MyApplication : Application(), Application.ActivityLifecycleCallbacks {

   // Members

   private var mAppIsInBackground = false
   private var mCurrentOrientation: Int? = null
   private var mOrientationWasChanged = false
   private var mResumed = 0
   private var mPaused = 0

그런 다음 콜백의 경우 이력서가 먼저 있습니다.

   // ActivityLifecycleCallbacks

   override fun onActivityResumed(activity: Activity?) {

      mResumed++

      if (mAppIsInBackground) {

         // !!! App came from background !!! Insert code

         mAppIsInBackground = false
      }
      mOrientationWasChanged = false
    }

그리고 onActivityStopped :

   override fun onActivityStopped(activity: Activity?) {

       if (mResumed == mPaused && !mOrientationWasChanged) {

       // !!! App moved to background !!! Insert code

        mAppIsInBackground = true
    }

그리고 여기에 추가됩니다 : 방향 변경 확인 :

   override fun onConfigurationChanged(newConfig: Configuration) {

       if (newConfig.orientation != mCurrentOrientation) {
           mCurrentOrientation = newConfig.orientation
           mOrientationWasChanged = true
       }
       super.onConfigurationChanged(newConfig)
   }

그게 다야. 희망이 누군가에게 도움이되기를 바랍니다 :)


1

다음을 사용 하여이 솔루션 을 확장 할 수 있습니다 LiveData.

class AppForegroundStateLiveData : LiveData<AppForegroundStateLiveData.State>() {

    private var lifecycleListener: LifecycleObserver? = null

    override fun onActive() {
        super.onActive()
        lifecycleListener = AppLifecycleListener().also {
            ProcessLifecycleOwner.get().lifecycle.addObserver(it)
        }
    }

    override fun onInactive() {
        super.onInactive()
        lifecycleListener?.let {
            this.lifecycleListener = null
            ProcessLifecycleOwner.get().lifecycle.removeObserver(it)
        }
    }

    internal inner class AppLifecycleListener : LifecycleObserver {

        @OnLifecycleEvent(Lifecycle.Event.ON_START)
        fun onMoveToForeground() {
            value = State.FOREGROUND
        }

        @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
        fun onMoveToBackground() {
            value = State.BACKGROUND
        }
    }

    enum class State {
        FOREGROUND, BACKGROUND
    }
}

이제이 LiveData를 구독하고 필요한 이벤트를 잡을 수 있습니다. 예를 들면 다음과 같습니다.

appForegroundStateLiveData.observeForever { state ->
    when(state) {
        AppForegroundStateLiveData.State.FOREGROUND -> { /* app move to foreground */ }
        AppForegroundStateLiveData.State.BACKGROUND -> { /* app move to background */ }
    }
}

0

이 답변은 정확하지 않은 것 같습니다. 이 메소드는 다른 활동이 시작되고 종료 될 때도 호출됩니다. 당신이 할 수있는 일은 글로벌 플래그를 유지하고 (예, 글로벌은 나쁘다 :) 새로운 활동을 시작할 때마다 이것을 true로 설정하십시오. 각 활동의 onCreate에서이를 false로 설정하십시오. 그런 다음 onPause에서이 플래그를 확인합니다. 거짓 인 경우 앱이 백그라운드로 이동하거나 종료됩니다.


나는 데이터베이스에 대해 이야기하지 않았다 ... 무슨 뜻입니까?
Joris Weimar

나는 당신의 대답을지지하고 있습니다. 일시 정지 호출 중에 데이터베이스에 플래그 값을 저장할 수는 있지만 좋은 해결책은 아닙니다.
Sandeep P
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.