일정 시간이 지나면 전경으로 돌아올 때 특정 작업을 수행하는 앱을 작성하려고합니다. 앱이 백그라운드로 전송되거나 포 그라운드로 가져 오는 시점을 감지하는 방법이 있습니까?
답변
onPause()
및 onResume()
응용 프로그램을 다시 배경과 전경하게 될 때 방법이라고합니다. 그러나 응용 프로그램이 처음 시작될 때와 종료되기 전에 호출됩니다. 활동 에서 더 많은 것을 읽을 수 있습니다 .
백그라운드 또는 포 그라운드에있는 동안 응용 프로그램 상태를 얻는 직접적인 방법은 없지만이 문제에 직면하여 onWindowFocusChanged
및onStop
.
자세한 내용은 여기를 확인하십시오. Android : Android 앱이 백그라운드로 이동하고 getRunningTasks 또는 getRunningAppProcesses없이 전경으로 돌아 오는시기를 감지하는 솔루션 입니다.
답변
2018 : Android는 라이프 사이클 구성 요소를 통해이를 기본적으로 지원합니다.
2018 년 3 월 업데이트 : 이제 더 나은 솔루션이 있습니다. ProcessLifecycleOwner를 참조하십시오 . 새로운 아키텍처 구성 요소 1.1.0 (현재는 최신 버전)을 사용해야하지만 특별히 위해 설계되었습니다.
이 답변 에는 간단한 샘플 이 있지만 샘플 앱 과 블로그 게시물을 작성했습니다. 을 작성했습니다.
2014 년에 이것을 쓴 이후로 다른 솔루션이 생겼습니다. 일부는 효과가 있었고 일부는 효과가 있다고 생각되었습니다 되었지만 (내를 포함하여!) 결함이 있었고 커뮤니티 (Android)로서 우리는 그 결과에 따라 생활하는 법을 배우고 특별한 경우에 대한 해결책을 썼습니다.
하나의 코드 스 니펫이 원하는 솔루션이라고 가정하지 마십시오. 더 나은 방법은 무엇을하고 왜 그렇게하는지 이해하는 것입니다.
이 MemoryBoss
클래스는 실제로 여기에서 작성된대로 사용되지 않았습니다. 작동하는 의사 코드 일뿐입니다.
새로운 아키텍처 구성 요소를 사용하지 않아야 할 정당한 이유가없는 한 (특히 오래된 오래된 API를 대상으로하는 경우 일부가있는 경우) 계속 사용하십시오. 그들은 완벽하지는 않지만 둘 다 아닙니다 ComponentCallbacks2
.
업데이트 / 참고 사항 (2015 년 11 월) : 사람들은 두 가지 의견을 제시했습니다. 먼저 문서 에서 정확한 값을 확인해서는 안되기 때문에 >=
대신 사용해야합니다 . 이것은 당신이 경우 것이 대부분의 경우 미세하지만 곰 염두에두고 있습니다 만 하고 신경 뭔가를 앱이 배경에 갔을 때, 사용 ==해야합니다 및 도 (활동 라이프 사이클 콜백과 같은) 다른 솔루션과 결합하거나, 원하는 효과를 얻지 못할 수 있습니다. 예 (그리고 이것은 나에게 일어났다)는 당신이 잠그고 싶다면==
백그라운드로 갈 때 암호 화면이있는 앱 (예 : 친숙한 경우 1Password와 같이), 메모리가 부족하고 갑자기 테스트하는 경우 실수로 앱을 잠글 수 있습니다 >= TRIM_MEMORY
.Android에서 LOW MEMORY
전화를 걸기 때문에 당신보다 더 높습니다. 테스트 방법 / 내용에주의하십시오.
또한 일부 사람들은 돌아올 때 감지하는 방법에 대해 물었습니다.
내가 생각할 수있는 가장 간단한 방법은 아래에 설명되어 있지만 일부 사람들은 익숙하지 않기 때문에 여기에 의사 코드를 추가하고 있습니다. 당신 YourApplication
과 MemoryBoss
클래스 가 있다고 가정하면 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
콜 다시 실행되도록 보장되지는 :
-
앱이 표시되어있는 동안 휴대 전화가 화면을 잠그면 (예 : 기기가 nn 분 후에 잠김) 잠금 화면이 맨 위에 있기 때문에이 콜백은 호출되지 않습니다 (또는 항상 그런 것은 아닙니다).
-
장치의 메모리가 부족하고 메모리가 부족한 경우 운영 체제는이 호출을 무시하고보다 중요한 수준으로 바로 넘어갑니다.
이제 앱이 백그라운드로 이동 한 시점을 아는 것이 얼마나 중요한지에 따라 활동 수명주기 등을 추적하면서이 솔루션을 함께 확장해야 할 수도 있고 그렇지 않을 수도 있습니다.
위의 사항을 명심하고 좋은 품질 보증 팀을 확보하십시오.)
업데이트 종료
늦었을 수도 있지만 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
및 구현하는 ComponentCallbacks
API (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);
그리고 그게 다야.
답변
이 문제를 해결하는 방법은 다음과 같습니다. 활동 전환 사이의 시간 참조를 사용하면 앱이 “배경”인지 여부에 대한 적절한 증거를 제공 할 가능성이 높습니다.
먼저, 타이머, 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 는 거짓 일 것입니다 입니다.
반면에 런처에서 활동이 포 그라운드로 오면 디바이스 깨우기, 전화 끊기 등이이 이벤트 이전에 실행 된 타이머 작업보다 많았으므로 wasInBackground 가 true 로 설정되었습니다 .
답변
편집 : 새로운 아키텍처 구성 요소는 유망한 것을 가져 왔습니다 : 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
}
}
}
예. 우리는 여기에 너무 많은 이상한 해결책이 있기 때문에이 간단한 해결책이 효과가 있다고 믿기가 어렵다는 것을 알고 있습니다.
그러나 희망이 있습니다.
답변
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"
답변
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);
}
}
답변
이 방법을 사용합니다. 작동하기에는 너무 단순 해 보이지만 앱에서 잘 테스트되었으며 실제로 “홈”버튼, “리턴”버튼 또는 화면 잠금 후 홈 화면으로 이동하는 것을 포함하여 모든 경우에 놀랍도록 잘 작동합니다. 시도 해봐.
아이디어는 포 그라운드에서 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 ()로 이동했습니다. 또한 초기 코드에서 누락 된 슈퍼 호출을 추가하고 있습니다. 이는 작동 코드보다 개념이기 때문입니다.