しばらくしてフォアグラウンドに戻されたときに特定のことを行うアプリを作成しようとしています。アプリがバックグラウンドに送信されたとき、またはフォアグラウンドに移動されたときを検出する方法はありますか?
45 に答える
2018:Androidは、ライフサイクルコンポーネントを通じてこれをネイティブにサポートします。
2018年3月の更新:より良い解決策があります。ProcessLifecycleOwnerを参照してください。新しいアーキテクチャコンポーネント1.1.0(現時点では最新)を使用する必要がありますが、これを行うために特別に設計されています。
この回答には簡単なサンプルがありますが、サンプルアプリとそれに関するブログ投稿を作成しました。
2014年にこれを書き戻したときから、さまざまな解決策が生まれました。動作するものもあれば、動作していると思われるものもありましたが、欠陥があり(私のものも含みます!)、コミュニティ(Android)として、結果に対応することを学び、特別な場合の回避策を作成しました。
単一のコードスニペットがあなたが探している解決策であると思い込まないでください。そうなる可能性は低いです。さらに良いことに、それが何をするのか、そしてなぜそれをするのかを理解するようにしてください。
ここに書かれているように、このMemoryBoss
クラスが実際に使用されることはありませんでした。たまたま機能したのは疑似コードの一部でした。
新しいアーキテクチャコンポーネントを使用しない正当な理由がない限り(特に、非常に古いAPIをターゲットにしている場合はいくつかあります)、先に進んでそれらを使用してください。それらは完璧にはほど遠いですが、どちらも完璧ではありませんでしたComponentCallbacks2
。
更新/メモ(2015年11月):人々は2つのコメントをしています。最初は、正確な値をチェックするべきではないとドキュメントに記載されているため、>=
代わりに使用する必要があります。これはほとんどの場合問題ありませんが、アプリがバックグラウンドに移行したときに何かを行うことだけを気にする場合は、==を使用し、それを別のソリューション(Activity Lifecycleコールバックなど)と組み合わせる必要があることに注意してください。希望する効果が得られない場合があります。例(そしてこれは私に起こりました)は、あなたがロックしたい場合です==
アプリがバックグラウンドに移動したときにパスワード画面が表示される場合(よく知っている場合は1Passwordなど)、Androidが呼び出し>= TRIM_MEMORY
をトリガーするため、メモリが不足して突然テストしている場合、誤ってアプリをロックする可能性があります。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
するため、が呼び出されてフラグが再びtrueに設定されるまでfalse
、コードは実行されません。。onMemoryTrimmed
お役に立てば幸いです。
更新/メモ(2015年4月) :このコードをすべてコピーアンドペーストする前に、100%信頼性がなく、最良の結果を得るには他の方法と組み合わせる必要がある場合がいくつかあることに注意してください。特に、コールバックの実行が保証されていない2つの既知のインスタンスがあります。onTrimMemory
アプリが表示されているときにスマートフォンが画面をロックした場合(たとえば、デバイスがnn分後にロックされた場合)、ロック画面が真上にあるため、このコールバックは呼び出されません(または常にではありません)が、アプリはカバーされていても「実行中」です。
デバイスのメモリが比較的少ない場合(およびメモリの負荷が低い場合)、オペレーティングシステムはこの呼び出しを無視し、より重要なレベルに直接移行するようです。
ここで、アプリがいつバックグラウンドに移行したかを知ることがどれほど重要かによって、アクティビティのライフサイクルなどを追跡しながら、このソリューションを拡張する必要がある場合とない場合があります。
上記を念頭に置いて、優れたQAチームを編成してください;)
更新の終わり
遅いかもしれませんが、アイスクリームサンドイッチ(API 14)以上には信頼できる方法があります。
アプリに表示されるUIがなくなると、コールバックがトリガーされることがわかりました。カスタムクラスに実装できるコールバックは、ComponentCallbacks2と呼ばれます(はい、2つあります)。このコールバックは、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.
}
}
これを使用するには、アプリケーションの実装で(1つありますよね?)、次のようにします。
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
て、 API 14より下の何かで使用される(2なしで)if
実装できます。そのコールバックにはメソッドのみがあり、バックグラウンドに移動したときに呼び出されませんが、メモリをトリミングするために使用する必要があります。ComponentCallbacks
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);
以上です。
これが私がこれを解決する方法です。アクティビティ遷移間の時間参照を使用すると、アプリが「バックグラウンド」になっているかどうかの十分な証拠が得られる可能性が高いという前提で機能します。
まず、android.app.Applicationインスタンス(MyApplicationと呼びます)を使用しました。このインスタンスには、Timer、TimerTask、あるアクティビティから別のアクティビティへの移行に合理的にかかる可能性のある最大ミリ秒数を表す定数があります(私は行きました)値は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の回答を参照してください
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
}
}
}
はい。ここには奇妙な解決策がたくさんあるので、この単純な解決策が機能するとは信じがたいです。
しかし、希望があります。
2021年11月の更新
実際のセットアップは次のとおりです
class App : Application() {
override fun onCreate() {
super.onCreate()
ProcessLifecycleOwner.get().lifecycle.addObserver(AppLifecycleListener())
}
}
class AppLifecycleListener : DefaultLifecycleObserver {
override fun onStart(owner: LifecycleOwner) { // app moved to foreground
}
override fun onStop(owner: LifecycleOwner) { // app moved to background
}
}
依存関係
implementation "androidx.lifecycle:lifecycle-process:$lifecycle_version"
implementation "androidx.lifecycle:lifecycle-common:$lifecycle_version"
元の回答
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"
およびメソッドはonPause()
、onResume()
アプリケーションがバックグラウンドおよびフォアグラウンドに戻されたときに呼び出されます。ただし、アプリケーションが初めて起動されたとき、およびアプリケーションが強制終了される前にも呼び出されます。詳細については、アクティビティをご覧ください。
バックグラウンドまたはフォアグラウンドでアプリケーションのステータスを取得するための直接的なアプローチはありませんが、私でさえこの問題に直面し、とで解決策を見つけましonWindowFocusChanged
たonStop
。
詳細については、こちらを確認してください。Android:Androidアプリがバックグラウンドに移行し、getRunningTasksまたはgetRunningAppProcessesを使用せずにフォアグラウンドに戻るタイミングを検出するソリューション。
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;
}
}
}
次に、これをApplicationクラスの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()に移動しました。また、これは実際のコードというよりも概念であったため、最初の投稿にはなかったスーパーコールを追加しています。
アプリが複数のアクティビティやタブバーウィジェットのようなスタックされたアクティビティで構成されている場合、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に達すると、次の条件が当てはまる場合、アプリケーションはバックグラウンドに置かれます。
- 一時停止中のアクティビティは終了していません(「戻る」ボタンが使用されました)。これは、メソッドactivity.isFinishing()を使用して実行できます。
- 新しいアクティビティ(同じパッケージ名)は開始されていません。startActivity()メソッドをオーバーライドして、これを示す変数を設定し、アクティビティが作成/再開されたときに実行される最後のメソッドであるonPostResume()でリセットできます。
アプリケーションがバックグラウンドに戻ったことを検出できれば、アプリケーションがフォアグラウンドに戻ったときにも簡単に検出できます。
を拡張するクラスを作成します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
}
}
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 {
...
}
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をご覧ください。
ActivityLifecycleCallbacksは興味深いかもしれませんが、十分に文書化されていません。
ただし、 registerActivityLifecycleCallbacks ()を呼び出すと、アクティビティが作成または破棄されたときなどのコールバックを取得できるはずです。アクティビティに対してgetComponentName()を呼び出すことができます。
アプリケーションでコールバックを追加し、次のようにルートアクティビティを確認します。
@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();
}
}
});
}
ライフサイクルオブザーバーをアタッチする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()
、アプリケーションクラスでこれを呼び出します。
ProcessLifecycleOwner.get().getLifecycle().addObserver(new ForegroundLifecycleObserver());
これにより、アプリケーションがバックグラウンドで実行されたときに発生するアプリケーションのイベントON_PAUSE
をキャプチャできるようになります。ON_STOP
Githubapp -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は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
アプリケーション全体がバックグラウンド/フォアグラウンドになったときに通知する簡単なライフサイクルメソッドはありません。
私はこれを簡単な方法で行いました。以下の手順に従って、アプリケーションのバックグラウンド/フォアグラウンドフェーズを検出します。
少しの回避策で、それは可能です。ここで、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 } }
使い方:
これは、ライフサイクルメソッドが順番に呼び出される方法で行われる小さなトリックです。シナリオをウォークスルーさせてください。
ユーザーがアプリを起動し、LauncherActivityAが起動されたと想定します。ライフサイクルの呼び出しは、
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
そのため、アクティビティで構成の変更が行われるときにシナリオを回避するための追加のチェックがあります。
編集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;
}
フォアグラウンドに入るのかバックグラウンドに入るのかにかかわらず、アプリケーションを検出するための良い方法を見つけました。これが私のコードです。これがお役に立てば幸いです。
/**
* 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();
}
アプリケーションクラスでこのメソッドを呼び出すだけです
ProcessLifecycleOwner.get().getLifecycle().addObserver(new LifecycleEventObserver() {
@Override
public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) {
Log.e(TAG, "onStateChanged: " + event.toString());
}
});
Lifecycle.Event
アプリケーションの状態を返すだけです
ON_CREATE
ON_START
ON_RESUME
ON_PAUSE
ON_STOP
ON_DESTROY
ON_ANY
アプリがバックグラウンドになるとON_PAUSEとON_STOPを返し、アプリがフォアグラウンドになるとON_STARTとON_RESUMEを返します。
私はこれをGoogleAnalyticsEasyTrackerで使用していましたが、機能しました。単純な整数を使用して、目的の処理を実行するように拡張できます。
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) {
}
});
}
}
ここで正解
以下のように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
タイムスタンプをチェックせずにローテーションも処理するアプローチが見つからなかったので、アプリで現在の方法も共有したいと思いました。この回答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)
}
それでおしまい。これが誰かに役立つことを願っています:)
私のソリューションは@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;
}
私はそれが少し遅いことを知っていますが、私が以下のようにそれをした間、これらすべての答えにはいくつかの問題があると思います、そしてそれは完璧に機能します。
次のようなアクティビティライフサイクルコールバックを作成します。
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 ://stackoverflow.com/a/15573121/4747587
そこに記載されているすべてを行います。ただし、を持ってBase Activity
それをすべてのアクティビティの親として作成し、とをオーバーライドするonResume()
代わりにonPause
、以下を実行します。
アプリケーションクラスに、次の行を追加します。
registerActivityLifecycleCallbacks(Application.ActivityLifecycleCallbacksコールバック);
これ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
implementを拡張するクラスで。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");
}
}
お役に立てれば。
編集 別の方法として、ライフサイクル対応のアーキテクチャコンポーネントを使用できるようになりました。
このソリューションは、次を使用して拡張できます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 */ }
}
}
これを実現する方法は3つあります。
- シングルアクティビティアーキテクチャ
- ActivityLifecycleCallback
- LifecycleObserverおよびProcessLifecycleOwner
これについての詳細な記事をここに書いています。それが役に立てば幸い。
Activity
を使用して、(または任意のクラスの)バックグラウンドからフォアグラウンドまでアプリを検出する例ProcessLifecycleOwner
。
アプリケーションの開始時に開始時刻をキャッシュし、各アクティビティでアプリケーション時刻を確認して、アクティビティが最初に開始するのか、バックグラウンドから開始するのかを確認します。
class MyApplication : Application(), LifecycleObserver {
var appStartBeginTime: Long? = null
override fun onCreate() {
super.onCreate()
ProcessLifecycleOwner.get().lifecycle.addObserver(this);
}
@OnLifecycleEvent(Lifecycle.Event.ON_START)
fun onMoveToForeground() {
Log.i("TAG", "onMoveToForeground")
appStartBeginTime = System.currentTimeMillis()
}
}
LoginActivity
class LoginActivity : AppCompatActivity() {
var localAppStartBeginTime: Long? = null
...
// Detect in onResume() instead of onStart because
// onMoveToForeground() in MyApplication will fired before onStart
override fun onResume() {
super.onResume()
if (isOpenedFirstTimeOrFromBackground()) {
Log.i("TAG", "open first time or from background")
// do something: eg, call API
} else {
Log.i("TAG", "on in another time")
}
}
private fun isOpenedFirstTimeOrFromBackground(): Boolean {
val globalStartBeginTime = (application as MyApplication).appStartBeginTime
if (localAppStartBeginTime != globalStartBeginTime) {
localAppStartBeginTime = globalStartBeginTime
return true
}
return false
}
}
AndroidManifest
<manifest ...>
<application
android:name=".MyApplication"
...>
</application>
</manifest>
デモ https://github.com/PhanVanLinh/AndroidDetectAppFromBackgroundToForeground
これらの答えは正しくないようです。これらのメソッドは、別のアクティビティが開始および終了するときにも呼び出されます。あなたができることは、グローバルフラグを保持し(はい、グローバルは悪いです:)、新しいアクティビティを開始するたびにこれをtrueに設定することです。各アクティビティのonCreateでfalseに設定します。次に、onPauseでこのフラグをチェックします。falseの場合、アプリはバックグラウンドに移行しているか、強制終了されています。
私がしたことは、すべてのアプリ内アクティビティが起動されていることを確認してstartActivityForResult
から、onResumeの前にonActivityResultが呼び出されたかどうかを確認することです。そうでない場合は、アプリの外部から戻ってきたことを意味します。
boolean onActivityResultCalledBeforeOnResume;
@Override
public void startActivity(Intent intent) {
startActivityForResult(intent, 0);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
super.onActivityResult(requestCode, resultCode, intent);
onActivityResultCalledBeforeOnResume = true;
}
@Override
protected void onResume() {
super.onResume();
if (!onActivityResultCalledBeforeOnResume) {
// here, app was brought to foreground
}
onActivityResultCalledBeforeOnResume = false;
}
これは私のソリューションですhttps://github.com/doridori/AndroidUtils/blob/master/App/src/main/java/com/doridori/lib/app/ActivityCounter.java
基本的に、タイマーを使用してすべてのアクティビティのライフサイクルメソッドをカウントし、現在フォアグラウンドにアクティビティがないがアプリが(ローテーション中)ある場合をキャッチします。
これが私の解決策です。このActivityLifecycleCallbacksをメインのApplicationクラスに登録するだけです。コメントでは、ユーザープロファイルのアクティビティエッジケースについて説明します。そのアクティビティは、単に透明なエッジを持つアクティビティです。
/**
* This class used Activity lifecycle callbacks to determine when the application goes to the
* background as well as when it is brought to the foreground.
*/
public class Foreground implements Application.ActivityLifecycleCallbacks
{
/**
* How long to wait before checking onStart()/onStop() count to determine if the app has been
* backgrounded.
*/
public static final long BACKGROUND_CHECK_DELAY_MS = 500;
private static Foreground sInstance;
private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper());
private boolean mIsForeground = false;
private int mCount;
public static void init(final Application application)
{
if (sInstance == null)
{
sInstance = new Foreground();
application.registerActivityLifecycleCallbacks(sInstance);
}
}
public static Foreground getInstance()
{
return sInstance;
}
public boolean isForeground()
{
return mIsForeground;
}
public boolean isBackground()
{
return !mIsForeground;
}
@Override
public void onActivityStarted(final Activity activity)
{
mCount++;
// Remove posted Runnables so any Meteor disconnect is cancelled if the user comes back to
// the app before it runs.
mMainThreadHandler.removeCallbacksAndMessages(null);
if (!mIsForeground)
{
mIsForeground = true;
}
}
@Override
public void onActivityStopped(final Activity activity)
{
mCount--;
// A transparent Activity like community user profile won't stop the Activity that launched
// it. If you launch another Activity from the user profile or hit the Android home button,
// there are two onStops(). One for the user profile and one for its parent. Remove any
// posted Runnables so we don't get two session ended events.
mMainThreadHandler.removeCallbacksAndMessages(null);
mMainThreadHandler.postDelayed(new Runnable()
{
@Override
public void run()
{
if (mCount == 0)
{
mIsForeground = false;
}
}
}, BACKGROUND_CHECK_DELAY_MS);
}
@Override
public void onActivityCreated(final Activity activity, final Bundle savedInstanceState)
{
}
@Override
public void onActivityResumed(final Activity activity)
{
}
@Override
public void onActivityPaused(final Activity activity)
{
}
@Override
public void onActivitySaveInstanceState(final Activity activity, final Bundle outState)
{
}
@Override
public void onActivityDestroyed(final Activity activity)
{
}
}
私のアプリは、バックグラウンドから戻った後に「再起動」する必要があります。クライアントの要請に応じて、一連のアクティビティを表示します。バックグラウンド/フォアグラウンドトランジションを管理する方法(iOSとAndroidの間で非常に異なる扱い)を徹底的に検索した後、私はこの質問に答えました。ここで非常に役立つヘルプが見つかりました。特に、最も投票された回答と正しいとフラグが付けられた回答からです。ただし、UXについて考えると、アプリがフォアグラウンドに入るたびにルートアクティビティを復元するだけで、煩わしすぎるように見えます。私にとってうまくいった解決策、そして私が最も適切だと思う解決策は、YoutubeとTwitterアプリの機能に基づいて、@GirishNairと@d60402からの回答を組み合わせることでした:アプリのトリミングメモリのときにタイマーを呼び出す、次のように:
@Override
public void onTrimMemory(int level) {
if (stateOfLifeCycle.equals("Stop")) {
startActivityTransitionTimer();
}
super.onTrimMemory(level);
}
私のタイマー制限は30秒に設定されています-これを少し増やすことを考えています。
private final long MAX_ACTIVITY_TRANSITION_TIME = 30000;
また、アプリがフォアグラウンドになったとき、再起動されたとき、またはアプリが破棄されたときは、メソッドを呼び出してタイマーをキャンセルします。
アプリ拡張について:
@Override
public void onActivityCreated(Activity activity, Bundle arg1) {
stopActivityTransitionTimer();
stateOfLifeCycle = "Create";
}
@Override
public void onActivityDestroyed(Activity activity) {
stopActivityTransitionTimer();
stateOfLifeCycle = "Destroy";
}
アクティビティについて(できれば、他の人に継承された基本アクティビティについて):
@Override
protected void onStart() {
super.onStart();
if (App.wasInBackground) {
stopActivityTransitionTimer();
}
}
私の場合、アプリが最大時間の後にフォアグラウンドになると、新しいタスクが作成されるため、アプリ拡張クラスのonActivityCreated()またはonActivityDestroyed()でstopActivityTransitionTimer()が呼び出されます-アクティビティでメソッドを呼び出す必要はありません。それが役に立てば幸い。
このソリューションはどうですか
public class BaseActivity extends Activity
{
static String currentAct = "";
@Override
protected void onStart()
{
super.onStart();
if (currentAct.equals(""))
Toast.makeText(this, "Start", Toast.LENGTH_LONG).show();
currentAct = getLocalClassName();
}
@Override
protected void onStop()
{
super.onStop();
if (currentAct.equals(getLocalClassName()))
{
currentAct = "";
Toast.makeText(this, "Stop", Toast.LENGTH_LONG).show();
}
}
}
すべてのアクティビティはBaseActivityを拡張する必要があります。
アクティビティが別のアクティビティ(A-> B)を呼び出す場合、2番目のアクティビティ(B)のonStart()が最初の(A)のonStop()の前に呼び出されるため、currentActはgetLocalClassName()と等しくありません(https://developer .android.com / guide / components / activitys.html#CoordinatingActivities)。
ユーザーがホームボタンを押すか、アプリケーション間で変更すると、onStop()が呼び出され、currentActはgetLocalClassName()と等しくなります。
以下のコードを使用することで、アプリのフォアグラウンドまたはバックグラウンドの状態を取得できます。
動作する強力なテキストの詳細については、ここをクリックしてください
import android.content.ComponentCallbacks2;
import android.content.Context;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
private Context context;
private Toast toast;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
context = this;
}
private void showToast(String message) {
//If toast is already showing cancel it
if (toast != null) {
toast.cancel();
}
toast = Toast.makeText(context, message, Toast.LENGTH_SHORT);
toast.show();
}
@Override
protected void onStart() {
super.onStart();
showToast("App In Foreground");
}
@Override
public void onTrimMemory(int level) {
super.onTrimMemory(level);
if (level == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
showToast("App In Background");
}
}
}
onResume、onPause、onStopアクティビティコールバックの使用を利用するBaseActivityを実装することで、アプリのナビゲーションがバックグラウンドに移動してフォアグラウンドに戻るのを監視することができました。これが私の実装です。
override fun onResume() {
super.onResume()
if (AppActivityState.state == AppState.ON_LAUNCHED) {
// We are in the first launch.
onLaunched()
} else {
if (AppActivityState.state == AppState.ON_BACKGROUND) {
// We came from background to foreground.
AppActivityState.state = AppState.ON_FOREGROUND
onForeground()
} else {
// We are just navigating through pages.
AppActivityState.state = AppState.RESUMED
}
}
}
override fun onPause() {
super.onPause()
// If state is followed by onStop then it means we will going to background.
AppActivityState.state = AppState.PAUSED
}
override fun onStop() {
super.onStop()
// App will go to background base on the 'pause' cue.
if (AppActivityState.state == AppState.PAUSED) {
AppActivityState.state = AppState.ON_BACKGROUND
onBackground()
}
}
BaseActivityを作成したら、このアクティビティをアプリの任意のアクティビティに拡張する必要があります。
これらのタイプの実装では、以下を正確に検出できます。-onBackground>アプリがバックグラウンドに移動します-onForeground>アプリがフォアグラウンドに戻ります-onLaunch>アプリを開いたばかり
これがお役に立てば幸いです:)
私はこのProcessLifecycleOwner
アプローチが好きですが、実際にはそのすべてをスキップできます。これは、Activity
のonCreate()
メソッドでは、それが最初の実行か後続の実行かを簡単に判断できるためです。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState == null) {
/* savedInstanceState is always null on first run */
} else {
/* it's a subsequent run */
}
}
これは、デバウンスロジックを使用して、バックグラウンド/フォアグラウンドイベントが連続して発生しないようにするソリューションです。したがって、それは常にバックグラウンド/フォアグラウンドの安定した状態を反映します。
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.LifecycleOwner
import java.util.Timer
import java.util.TimerTask
/**
* An observer class to listen on the app's lifecycle.
*/
class AppLifecycleObserver(
private val onAppGoesToBackground: () -> Unit = {},
private val onAppEntersForeground: () -> Unit = {}
) : LifecycleEventObserver {
private val debounce = DebouncingTimer(timeout = 10)
override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
debounce.refresh {
when (event.targetState) {
Lifecycle.State.CREATED -> onAppGoesToBackground()
Lifecycle.State.RESUMED -> onAppEntersForeground()
else -> Unit
}
}
}
fun attach() {
ProcessLifecycleOwner.get().lifecycle.addObserver(this)
}
fun detach() {
ProcessLifecycleOwner.get().lifecycle.removeObserver(this)
}
private class DebouncingTimer(private val timeout: Long) {
private var timer: Timer? = null
fun refresh(job: () -> Unit) {
timer?.cancel()
timer = Timer()
timer?.schedule(object : TimerTask() {
override fun run() = job.invoke()
}, timeout)
}
}
}
のインスタンスを作成する必要がありますAppLifecycleObserver
:
private val appLifecycleObserver = AppLifecycleObserver(
onAppGoesToBackground = { // do whatever... },
onAppEntersForeground = { // do whatever... }
)
// Attach the observer when it is needed:
appLifecycleObserver.attach()
// Remove when there is no need to it:
appLifecycleObserver.detach()
依存関係の適切なバージョンを追加することを忘れないでください。
implementation("androidx.lifecycle:lifecycle-process:$lifecycle_version")
私はこのソリューションを使用しています:http: //nathanael.hevenet.com/android-dev-detecting-when-your-app-is-in-the-background-across-activities/
つまり、すべてのアクティビティが各ライフサイクルイベントについて報告する専用サービスを構築します。このサービスは、アプリのステータスに関する情報を取得します。
@ oldschool4664ソリューションに非常によく似ていますが、私の意見ではよりクリーンです
最も簡単な方法(追加のライブラリは必要ありません)
Kotlin:
var handler = Handler()
var isAppInBackground = true
override fun onStop() {
super.onStop()
handler.postDelayed({ isAppInBackground = true },2000)
}
override fun onDestroy() {
super.onDestroy()
handler.removeCallbacksAndMessages(null)
isAppInBackground = false
}
Java:
Handler handler = new Handler();
boolean isAppInBackground = true;
@Override
public void onStop() {
super.onStop();
handler.postDelayed(() -> { isAppInBackground = true; },2000);
}
@Override
public void onDestroy() {
super.onDestroy();
handler.removeCallbacksAndMessages(null);
isAppInBackground = false;
}
主な問題は、バックグラウンドからアクティビティを開始するときに特定の動作を取得する必要があることです。onPause()メソッドとonResume()メソッドをオーバーライドすると、厳密な答えは得られますが、解決策は得られません。問題は、アプリケーションを最小化しない場合でもonPause()メソッドとonResume()メソッドが呼び出されることです。これらは、アクティビティを開始し、後で戻るボタンを押してアクティビティに戻るときに呼び出すことができます。その問題を排除し、アプリケーションがバックグラウンドから来ていることを実際に知るには、実行中のプロセスを取得して、プロセスと比較する必要があります。
private boolean isApplicationBroughtToBackground() {
ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
List<RunningTaskInfo> tasks = am.getRunningTasks(1);
if (!tasks.isEmpty()) {
ComponentName topActivity = tasks.get(0).topActivity;
if (!topActivity.getPackageName().equals(getPackageName())) {
return true;
}
}
return false;
}
次に、ブール変数を宣言する必要があります。
public boolean wasPaused = false;
そして、あなたの活動がいつ背景になるか尋ねてください:
@Override
public void onPause(){
super.onPause();
if(isApplicationBroughtToBackground())
wasPaused = true;
}
ここで、アクティビティが再び画面に表示されたら、onResume()メソッドで質問します。
@Override
public void onResume(){
super.onResume();
if(wasPaused){
lockScreen(true);
}
wasPaused = false;
}
そして、これはそれです。これで、アクティビティがバックグラウンドになり、後でユーザーがそれをフォアグラウンドにすると、ロック画面が表示されます。
アプリのアクティビティに対してこの動作を繰り返したい場合は、アクティビティ(BaseActivityの場合もあります)を作成し、このメソッドを配置する必要があります。すべてのアクティビティはBaseActivityから継承する必要があります。
これがお役に立てば幸いです。
ご挨拶!