しばらくしてフォアグラウンドに戻されたときに特定のことを行うアプリを作成しようとしています。アプリがバックグラウンドに送信されたとき、またはフォアグラウンドに移動したときを検出する方法はありますか?
しばらくしてフォアグラウンドに戻されたときに特定のことを行うアプリを作成しようとしています。アプリがバックグラウンドに送信されたとき、またはフォアグラウンドに移動したときを検出する方法はありますか?
回答:
onPause()
そしてonResume()
、アプリケーションが再びバックグラウンドにし、フォアグラウンドに持ち込まれたときにメソッドが呼び出されます。ただし、アプリケーションが初めて起動されたときや、アプリケーションが強制終了される前にも呼び出されます。詳しくは、アクティビティをご覧ください。
バックグラウンドまたはフォアグラウンドでアプリケーションのステータスを取得するための直接的なアプローチはありませんが、私もこの問題に直面していてonWindowFocusChanged
、およびで解決策を見つけましたonStop
。
詳細については、こちらを確認してくださいAndroid:Androidアプリがバックグラウンドに移行し、getRunningTasksまたはgetRunningAppProcessesを使用せずにフォアグラウンドに戻ったときに検出するソリューション。
2018年3月の更新:より良いソリューションがあります。ProcessLifecycleOwnerを参照してください。新しいアーキテクチャコンポーネント1.1.0(現時点では最新)を使用する必要がありますが、これはそのために特別に設計されています。
この回答には簡単なサンプルが用意されていますが、サンプルアプリとそれに関するブログ投稿を書きました。
これを2014年に書いて以来、さまざまな解決策が生まれました。一部は機能し、一部は機能していると考えられていましたが、欠陥(私のものを含む)があり、私たちはコミュニティ(Android)として結果に耐えることを学び、特別なケースの回避策を書きました。
単一のコードスニペットがあなたが探しているソリューションであると思い込まないでください。さらに良いことに、それが何をするのか、なぜそれをするのかを理解するようにしてください。
MemoryBoss
ここに書かれたクラスは実際に私が使用していなかった、それが仕事に起こった擬似コードのちょうど作品でした。
新しいアーキテクチャコンポーネントを使用しない正当な理由がない限り(そして、特に超古いAPIを対象とする場合はいくつかあります)、先に進んでそれらを使用してください。彼らは完璧にはほど遠いが、どちらも完璧ではなかったComponentCallbacks2
。
更新/注(2015年11月):人々は2つのコメントを書いています。1つ目は、正確な値を確認するべきではないとドキュメントに記載されているため、>=
代わりに使用する必要があるということです。これは、ほとんどの場合の罰金ですが、あなたがあれば心に留めていることだけやって気に何かアプリがバックグラウンドに行って、あなたが使う==する必要がありますし、あなたも(活動ライフサイクルコールバックのような)他のソリューションとそれを組み合わせますか、ご希望の効果が得られない場合があります。例(これは私に起こりました)は、ロックしたい場合==
バックグラウンドに移行したときにパスワード画面が表示されるアプリ(慣れている場合は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ブロックのコードは1回だけ実行されます。別のアクティビティに移動した場合でも、新しい(それもextends BaseActivity
)レポートwasInBackground
はfalse
、が呼び出されてフラグが再びtrueに設定されるまでonMemoryTrimmed
コードを実行しないように報告します。
お役に立てば幸いです。
更新/メモ(2015年4月):このコードをすべてコピーアンドペーストする前に、100%信頼性が低く、他の方法と組み合わせて最良の結果を得る必要がある場合があることに気づきました。注目すべきは、ある二つの既知のインスタンスonTrimMemory
のコールバックが実行されることが保証されていませんが。
アプリが表示されているときに携帯電話が画面をロックする場合(たとえば、nn分後にデバイスがロックされるなど)、ロックスクリーンは真上にあるため、このコールバックは呼び出されません(または常に呼び出されるわけではありません)が、アプリは覆われていますが、まだ「実行中」です。
デバイスのメモリが比較的少ない場合(およびメモリ負荷が低い場合)、オペレーティングシステムはこの呼び出しを無視し、より重要なレベルに直接移動するようです。
ここで、アプリがいつバックグラウンドに移行したかを知ることがどれほど重要かによって、アクティビティのライフサイクルなどを追跡しながら、このソリューションを拡張する必要がある場合とない場合があります。
上記を念頭に置き、優れたQAチームを作成してください;)
更新の終わり
遅いかもしれませんが、Ice Cream Sandwich(API 14)以降には信頼できる方法があります。
アプリにUIが表示されなくなると、コールバックがトリガーされることがわかります。カスタムクラスで実装できるコールバックは、ComponentCallbacks2(はい、2つあります)と呼ばれます。このコールバックは、APIレベル14(アイスクリームサンドイッチ)以上でのみ使用できます。
基本的に、メソッドの呼び出しを取得します。
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);
以上です。
level >= ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN
アップデート2の問題を回避するために使用するだけです。ポイント2については、アプリは実際にはバックグラウンドに移動しなかったので、私には関係ありません。
これを解決する方法を以下に示します。アクティビティの遷移間で時間参照を使用すると、アプリが「バックグラウンド」であるかどうかの十分な証拠が得られる可能性が高いという前提で機能します。
最初に、1つのアクティビティから別のアクティビティへの遷移が合理的に取ることができる最大ミリ秒数を表す定数であるTimer、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;
...
アプリケーションは、タイマー/タスクを開始および停止するための2つの方法も提供します。
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はfalseになります。
一方、アクティビティがランチャーからフォアグラウンドに到達したとき、デバイスのウェイクアップ、通話の終了などの場合、このイベントの前にタイマータスクが実行された可能性が高いため、wasInBackgroundがtrueに設定されました。
編集:新しいアーキテクチャコンポーネントは有望なものをもたらしました:ProcessLifecycleOwner、@ vokilamの回答を参照してください
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"
implementation "android.arch.lifecycle:extensions:1.0.0"
ありannotationProcessor "android.arch.lifecycle:compiler:1.0.0"
、Googleのリポジトリ(つまりgoogle()
)から追加する必要があることに注意してください
MartínMarconcinisの回答に基づいて(ありがとう!)ようやく、信頼できる(そして非常にシンプルな)ソリューションを見つけました。
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;
}
}
}
次に、これをアプリケーションクラスのonCreate()に追加します。
public class MyApp extends android.app.Application {
@Override
public void onCreate() {
super.onCreate();
ApplicationLifeCycleHandler handler = new ApplicationLifeCycleHandler();
registerActivityLifecycleCallbacks(handler);
registerComponentCallbacks(handler);
}
}
この方法を使用します。見た目は単純すぎるように見えますが、アプリで十分にテストされており、実際には、「ホーム」ボタン、「戻る」ボタン、または画面ロック後にホーム画面に移動するなど、すべてのケースで驚くほどうまく機能します。試してみる。
アイデアは、フォアグラウンドでは、Androidは常に前のアクティビティを停止する直前に新しいアクティビティを開始するというものです。それは保証されていませんが、それはそれがどのように機能するかです。ところで、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()にも移動しました。また、これは実際のコードというよりはコンセプトの概念だったため、最初の投稿から欠落していたスーパーコールを追加しています。
onStop is called when the activity is no longer visible to the user
。
アプリが複数のアクティビティまたはタブバーウィジェットのようなスタックされたアクティビティで構成されている場合、onPause()およびonResume()のオーバーライドは機能しません。つまり、新しいアクティビティを開始すると、現在のアクティビティは新しいアクティビティが作成される前に一時停止されます。アクティビティを終了する(「戻る」ボタンを使用する)場合も同様です。
期待どおりに動作するように見える2つの方法を見つけました。
1つ目は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(現在はIgnitionと呼ばれています)フレームワークで発見されました。
私が自分で実装した2番目の方法では、GET_TASKS権限は不要です。代わりに、実装が少し複雑になります。
MainApplicationクラスには、アプリケーションで実行中のアクティビティの数を追跡する変数があります。各アクティビティのonResume()で変数を増やし、onPause()で変数を減らします。
実行中のアクティビティの数が0に達すると、次の条件に該当する場合、アプリケーションはバックグラウンドになります。
アプリケーションがバックグラウンドに辞任したことを検出できる場合は、フォアグラウンドに戻ったときにも簡単に検出できます。
拡張するクラスを作成します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
}
}
FragmentActivity
あなたはまた、追加したいかもしれませんlevel == ComponentCallbacks2.TRIM_MEMORY_COMPLETE
あまりにも。
onUserLeaveHintの使用を検討してください。これは、アプリがバックグラウンドに移行したときにのみ呼び出されます。onPauseは他の理由で呼び出される可能性があるため、処理するべきコーナーケースがあります。たとえば、ユーザーが設定ページなどのアプリで別のアクティビティを開いた場合、メインのアクティビティのonPauseメソッドがまだアプリにある場合でも呼び出されます。何が起こっているのかを追跡すると、代わりに単に求めていることを実行するonUserLeaveHintコールバックを使用できる場合にバグが発生します。
on UserLeaveHintが呼び出されると、ブールのinBackgroundフラグをtrueに設定できます。onResumeが呼び出されたとき、inBackgroundフラグが設定されている場合にのみ、フォアグラウンドに戻ったと想定します。これは、ユーザーが設定メニューにいてアプリを終了しなかった場合、メインアクティビティでもonResumeが呼び出されるためです。
ユーザーが設定画面でホームボタンを押すと、onUserLeaveHintが設定アクティビティで呼び出され、onResumeが戻ると設定アクティビティで呼び出されることに注意してください。メインのアクティビティにこの検出コードしかない場合は、この使用例を見逃してしまいます。コードを複製せずにすべてのアクティビティでこのコードを使用するには、Activityを拡張する抽象アクティビティクラスを用意し、そこに共通コードを配置します。次に、あなたが持っている各アクティビティは、この抽象的なアクティビティを拡張できます。
例えば:
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 {
...
}
ActivityLifecycleCallbacksは興味深いかもしれませんが、十分に文書化されていません。
ただし、registerActivityLifecycleCallbacks()を呼び出すと、アクティビティの作成時、破棄時などにコールバックを取得できるはずです。アクティビティのgetComponentName()を呼び出すことができます。
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をご覧ください。
アプリケーションにコールバックを追加し、次のような方法でルートアクティビティを確認します。
@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();
}
}
});
}
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
}
}
MainActivityがBaseActivityを拡張するようなすべてのアクティビティのスーパークラスとしてこのBaseActivityを使用し、アプリケーションの起動時にonAppStartが呼び出され、アプリケーションが任意の画面からバックグラウンドに移動したときにonAppPause()が呼び出されます。
これはProcessLifecycleOwnerでかなり簡単です
これらの依存関係を追加する
implementation "android.arch.lifecycle:extensions:$project.archLifecycleVersion"
kapt "android.arch.lifecycle:compiler:$project.archLifecycleVersion"
でKotlin:
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
ライフサイクルオブザーバをアタッチする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
それがバックグラウンドで行く場合に発生アプリケーションのを。
アプリケーション全体がいつバックグラウンド/フォアグラウンドになるかを伝える簡単なライフサイクルメソッドはありません。
私はこれを簡単な方法で行いました。以下の手順に従って、アプリケーションのバックグラウンド/フォアグラウンドフェーズを検出します。
少しの回避策で、それは可能です。ここで、ActivityLifecycleCallbacksが役に立ちます。順を追って説明します。
最初に、android.app.Applicationを拡張し、ActivityLifecycleCallbacksインターフェースを実装するクラスを作成します。Application.onCreate()で、コールバックを登録します。
public class App extends Application implements
Application.ActivityLifecycleCallbacks {
@Override
public void onCreate() {
super.onCreate();
registerActivityLifecycleCallbacks(this);
}
}
「App」クラスをマニフェストに以下のように登録し<application android:name=".App"
ます。
アプリがフォアグラウンドにある場合、開始状態のアクティビティは少なくとも1つあり、アプリがバックグラウンドにある場合、開始状態のアクティビティはありません。
「App」クラスで以下のように2つの変数を宣言します。
private int activityReferences = 0;
private boolean isActivityChangingConfigurations = false;
activityReferences
開始状態のアクティビティの数を保持します。isActivityChangingConfigurations
現在のアクティビティが方向スイッチのように構成変更を行っているかどうかを示すフラグです。
次のコードを使用すると、アプリがフォアグラウンドになったかどうかを検出できます。
@Override
public void onActivityStarted(Activity activity) {
if (++activityReferences == 1 && !isActivityChangingConfigurations) {
// App enters foreground
}
}
これは、アプリがバックグラウンドになるかどうかを検出する方法です。
@Override
public void onActivityStopped(Activity activity) {
isActivityChangingConfigurations = activity.isChangingConfigurations();
if (--activityReferences == 0 && !isActivityChangingConfigurations) {
// App enters background
}
}
使い方:
これは、ライフサイクルメソッドが順番に呼び出される方法で行われる小さなトリックです。シナリオについて説明します。
ユーザーがアプリを起動し、Launcher Activity Aが起動されたと想定します。ライフサイクルコールは、
A.onCreate()
A.onStart()(++ activityReferences == 1)(アプリがフォアグラウンドに入ります)
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からホームボタンを押しても、それは同じで、activityReferencesは 0
。したがって、アプリがバックグラウンドに入ったことを検出できます。
それで、の役割はisActivityChangingConfigurations
何ですか?上記のシナリオで、アクティビティBが方向を変更するとします。コールバックシーケンスは、
B.onPause()
B.onStop()(--activityReferences == 0)(アプリがバックグラウンドに入りますか?)
B.onDestroy()
B.onCreate()
B.onStart()(++ activityReferences == 1)(アプリがフォアグラウンドに入りますか?)
B.onResume()
そのisActivityChangingConfigurations
ため、アクティビティが構成の変更を通過するシナリオを回避するための追加チェックがあります。
フォアグラウンドまたはバックグラウンドのどちらに入るのかを問わず、アプリケーションを検出するための良い方法を見つけました。これが私のコードです。これがお役に立てば幸いです。
/**
* 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();
}
編集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;
}
ここで正解
以下のような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
私のソリューションは、@ 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;
}
私はこれを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) {
}
});
}
}
私はそれが少し遅れていることを知っていますが、以下のようにしてそれが完璧に機能する間、これらすべての回答にはいくつかの問題があると思います。
次のようなアクティビティライフサイクルコールバックを作成します。
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());
}
(これを書いている時点では)AndroidにはiOSに相当するものapplicationDidEnterBackground()
やapplicationWillEnterForeground()
コールバックがないため、これはAndroidで最も複雑な質問の1つであるようです。@jenzzによって作成されたAppStateライブラリを使用しました。
[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ページにます。
これは@ d60402の回答の修正版です。 https
そこで言及されているすべてを行います。しかしBase Activity
、すべてのアクティビティの親として、それをオーバーライドしてonResume()
、onPause
、下の操作を行います。
アプリケーションクラスに次の行を追加します。
registerActivityLifecycleCallbacks(Application.ActivityLifecycleCallbacks callback);
これにcallback
はすべてのアクティビティライフサイクルメソッドがあり、オーバーライドonActivityResumed()
してonActivityPaused()
。
この要旨をご覧ください:https : //gist.github.com/thsaravana/1fa576b6af9fc8fff20acfb2ac79fa1b
あなたはの助けを借りて、簡単にこれを達成することができますActivityLifecycleCallbacks
し、ComponentCallbacks2
以下のようなもの。
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");
}
}
お役に立てれば。
編集 代替手段として、ライフサイクル対応のアーキテクチャコンポーネントを使用できるようになりました。
タイムスタンプをチェックせずにローテーションも処理するアプローチが見つからなかったので、アプリでこれをどのように実行するかも共有したいと思いました。この回答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)
}
それでおしまい。これが誰かを助けることを願っています:)
私たちは、拡張することができます。このソリューションを使用しました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 */ }
}
}
これらの答えは正しくないようです。これらのメソッドは、別のアクティビティが開始および終了したときにも呼び出されます。できることは、グローバルフラグを保持し(はい、グローバルは悪いです:)、新しいアクティビティを開始するたびにこれをtrueに設定します。各アクティビティのonCreateでfalseに設定します。次に、onPauseでこのフラグをチェックします。falseの場合、アプリがバックグラウンドにあるか、強制終了されています。