アクティビティがフォアグラウンドか可視バックグラウンドかを確認するにはどうすればよいですか?


104

タイマーにスプラッシュスクリーンがあります。私の問題はfinish()、システムダイアログボックスがポップアップし、次のアクティビティのみを実行したいため、アクティビティの前に次のアクティビティが開始したことを確認する必要があることfinish()です。ユーザーがダイアログボックスからオプションを選択したら?

私はあなたの活動がフォアグラウンドにあるかどうかを確認する方法について多くの質問があることを知っていますが、これが活動の上にダイアログボックスを許可するかどうかもわかりません。

ここに問題があります、赤は会話が前景にある間にバックグラウンドにある私の活動です:

赤は会話が前景にある間にバックグラウンドにある私の活動です

編集:私は使用せずに試しましたfinish()が、私の活動は回避しようとしているアプリケーションのスタックに戻る可能性があります。


関連するかもしれない:stackoverflow.com/questions/4414171/...
jarmod

明確にするために、インテントチューザーを起動し、ユーザーが選択肢の1つをタップするまで、アプリがfinish()を待機する必要がありますか?結果を受け取ったら、Intent.createChooser()とstartActivityForResult()に続けてfinish()が必要なようです。
alanv 2013


ProcessLifecycleOwnerは最新のソリューションです
SR

回答:


189

これは正しい解決策として推奨されるものです:

適切なソリューション(クレジットはDan、CommonsWare、およびNeTeInStEiNに送られます)Activity.onPause、Activity.onResumeメソッドを使用して、自分でアプリケーションの可視性を追跡します。「可視性」ステータスを他のクラスに保存します。適切な選択肢は、アプリケーションまたはサービスの独自の実装です(サービスからアクティビティの可視性を確認する場合は、このソリューションのいくつかのバリエーションもあります)。

カスタムアプリケーションクラスを実装します(isActivityVisible()静的メソッドに注意してください):

public class MyApplication extends Application {

  public static boolean isActivityVisible() {
    return activityVisible;
  }  

  public static void activityResumed() {
    activityVisible = true;
  }

  public static void activityPaused() {
    activityVisible = false;
  }

  private static boolean activityVisible;
}

アプリケーションクラスをAndroidManifest.xmlに登録します。

<application
    android:name="your.app.package.MyApplication"
    android:icon="@drawable/icon"
    android:label="@string/app_name" >

プロジェクトのすべてのアクティビティにonPauseとonResumeを追加します(必要に応じてアクティビティの共通の祖先を作成できますが、アクティビティがすでにMapActivity / ListActivityなどから拡張されている場合は、手動で次のように記述する必要があります)。 :

@Override
protected void onResume() {
  super.onResume();
  MyApplication.activityResumed();
}

@Override
protected void onPause() {
  super.onPause();
  MyApplication.activityPaused();
}

あなたにはfinish()この方法は、使用したいisActivityVisible()アクティビティが表示されたかどうかをチェックします。そこで、ユーザーがオプションを選択したかどうかを確認することもできます。両方の条件が満たされたときに続行します。

出典では2つの間違った解決策についても言及しています...そのため、そうすることは避けてください。

ソース:stackoverflow


終了と開始のアクティビティの間に少し時間があり、遅延とカウンターを追加する必要があります
sagus_helgy 14

28
これは確実に機能しません。次のような状況が考えられます。再開A再開B一時停止A.アプリケーションが表示されているのに、activityVisibleがfalseになっています。おそらく、可視性カウンターを使用します:onResumeのvisibleCounter ++およびonPauseのvisibleCounter。
Joris Weimar

4
Joris Weimar氏は、これは絶対的な解決策ではないことに同意しました。1つのシナリオは、ユーザーが、通知パネルを引き下げている場合ではありませんどちらもonPauseonStop、またonResumeイベントが呼び出されます。では、これらのイベントがどれも発生しない場合はどうしますか?

1
実際、他の回答も100%機能しません。

2
アプリに複数のアクティビティがある場合、このスキームは機能しません。少なくともカウンターに置き換える
ruX

70

APIレベル14以上を対象とする場合は、android.app.Application.ActivityLifecycleCallbacksを使用できます

public class MyApplication extends Application implements ActivityLifecycleCallbacks {
    private static boolean isInterestingActivityVisible;

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

        // Register to be notified of activity state changes
        registerActivityLifecycleCallbacks(this);
        ....
    }

    public boolean isInterestingActivityVisible() {
        return isInterestingActivityVisible;
    }

    @Override
    public void onActivityResumed(Activity activity) {
        if (activity instanceof MyInterestingActivity) {
             isInterestingActivityVisible = true;
        }
    }

    @Override
    public void onActivityStopped(Activity activity) {
        if (activity instanceof MyInterestingActivity) {
             isInterestingActivityVisible = false;
        }
    }

    // Other state change callback stubs
    ....
}

18
通常のアクティビティライフサイクルコールバック(onResume()、onStop())でもこれを実行できます。
Daniel Wilson

5
@DanielWilsonすでに存在する場所で何かをするためのシステムを構築しないことがポイントだと思います。私見これは受け入れられた答えであるべきです。
Jeffrey Blattman

26

UPD:状態に更新されましたLifecycle.State.RESUMED@htafoyaに感謝します。

2019では、新しいサポートライブラリ28+またはAndroidXを使用して、次のように簡単に使用できます。

val isActivityInForeground = activity.lifecycle.currentState.isAtLeast(Lifecycle.State.RESUMED)

文書で詳細を読んで、内部で何が起こったかを理解することができます。


2
実際にはそうではありません。おそらく、配置activity.lifecycle.currentState.isAtLeast(Lifecycle.State.RESUMED) または開始する方が良いでしょう。 INITIALIZEDフォアグラウンドにあることを保証するものではありません。
htafoya

11

Activity :: hasWindowFocus()は、必要なブール値を返します。

public class ActivityForegroundChecker extends TimerTask
{
    private static final long FOREGROUND_CHECK_PERIOD = 5000;
    private static final long FIRST_DELAY             = 3000;

    private Activity m_activity;
    private Timer    m_timer;

    public ActivityForegroundChecker (Activity p_activity)
    {
        m_activity = p_activity;
    }

    @Override
    public void run()
    {
        if (m_activity.hasWindowFocus() == true) {
            // Activity is on foreground
            return;
        }
        // Activity is on background.
    }

    public void start ()
    {
        if (m_timer != null) {
            return;
        }
        m_timer = new Timer();
        m_timer.schedule(this, FIRST_DELAY, FOREGROUND_CHECK_PERIOD);
    }

    public void stop ()
    {
        if (m_timer == null) {
            return;
        }
        m_timer.cancel();
        m_timer.purge();
        m_timer = null;
    }
}

以下は、どこからでもアクティビティの可視性を確認するためのクラスの例です。

あなたが表示されている場合があることに注意してください]ダイアログボックスをダイアログが主な焦点を持っているので、結果はfalseになります。それ以外は、提案されたソリューションよりも本当に便利で信頼性があります。


1
@Burak Dayの回答を修正していただきありがとうございます。これは実際には回答です
Nick

これは機能しません。クラスでブールプロパティを使用し、OnResumeでtrueに設定し、OnPause()でfalseに設定します。
チャンドラー

@Chandlerこのコードであなたが抱えている正確な問題は何ですか?また、どのバージョン?
Burakの日

@Chandlerも、アクティビティのライフサイクルメソッドにアクセスできない場合はどうでしょうか。ライブラリからアクティビティの可視性を確認しているだけだとします。
Burak Day、

この回答の本当の問題は、それが機能しないことです。activity.hasWindowFocusがtrueの場合、アクティビティがonResumeとonPause状態の間であることを保証できません。そのアクティビティにbool isResumedプロパティを追加し、手動で値を設定してgetメソッドを追加することをお勧めします。
チャンドラー

10

これは、Activityクラスのドキュメントで説明されているとおり、アクティビティのイベントonPauseonStopイベントの違いです。

私があなたを正しく理解しているなら、あなたがしたいことはfinish()、あなたの活動onStopからそれを終了させるための呼び出しです。Activity Lifecycle Demo Appの添付画像を参照してください。これは、アクティビティBがアクティビティAから起動されたときの様子です。イベントの順序は下から上になっているためonStop、アクティビティB onResumeがすでに呼び出された後にアクティビティA が呼び出されていることがわかります。

アクティビティライフサイクルのデモ

ダイアログが表示された場合、アクティビティはバックグラウンドで淡色表示され、onPause呼び出されるだけです。


7

2つの可能な解決策:

1)アクティビティのライフサイクルコールバック

ActivityLifecycleCallbacksを実装するアプリケーションを使用して、アプリケーションのアクティビティライフサイクルイベントを追跡します。ActivityLifecycleCallbacksはAndroid api> = 14用であることに注意してください。以前のAndroid apiの場合、すべてのアクティビティ内で自分で実装する必要があります;-)

アクティビティ全体で状態を共有/保存する必要がある場合は、アプリケーションを使用します

2)実行中のプロセス情報を確認する

このクラスRunningAppProcessInfoを使用して、実行中のプロセスのステータスを確認できます

ActivityManager.getRunningAppProcesses() を使用して実行中のプロセスリストを取得し、結果リストをフィルタリングして、目的のRunningAppProcessInfoをチェックし、その「重要性」をチェックします



3

一時停止とバックグラウンドからの再開の間の時間差を使用して、バックグラウンドから起きているかどうかを判断します

カスタムアプリケーションで

private static boolean isInBackground;
private static boolean isAwakeFromBackground;
private static final int backgroundAllowance = 10000;

public static void activityPaused() {
    isInBackground = true;
    final Handler handler = new Handler();
    handler.postDelayed(new Runnable() {
        @Override
        public void run() {
            if (isInBackground) {
                isAwakeFromBackground = true;
            }
        }
    }, backgroundAllowance);
    Log.v("activity status", "activityPaused");
}

public static void activityResumed() {
    isInBackground = false;
    if(isAwakeFromBackground){
        // do something when awake from background
        Log.v("activity status", "isAwakeFromBackground");
    }
    isAwakeFromBackground = false;
    Log.v("activity status", "activityResumed");
}

BaseActivityクラス内

@Override
protected void onResume() {
  super.onResume();
  MyApplication.activityResumed();
}

@Override
protected void onPause() {
  super.onPause();
  MyApplication.activityPaused();
}

3

私にはもっと良い解決策があると思います。MyApplication.activityResumed();で簡単にビルドできるからです。すべてのアクティビティに1つの延長で。

まず、作成する必要があります(Cyber​​neticTwerkGuruOrcなど)

public class MyApplication extends Application {

  public static boolean isActivityVisible() {
    return activityVisible;
  }  

  public static void activityResumed() {
    activityVisible = true;
  }

  public static void activityPaused() {
    activityVisible = false;
  }

  private static boolean activityVisible;
}

次に、AndroidManifest.xmlにApplicationクラスを追加する必要があります

<application
    android:name="your.app.package.MyApplication"
    android:icon="@drawable/icon"
    android:label="@string/app_name" >

次に、クラスActivityBaseを作成します

public class ActivityBase extends Activity {

    @Override
    protected void onPause() {
        super.onPause();
        MyApplication.activityPaused();
    }

    @Override
    protected void onResume() {
        super.onResume();
        MyApplication.activityResumed();
    }
}

最後に、新しいActivityを作成するときは、ActivityではなくActivityBaseで拡張できます。

public class Main extends ActivityBase {
    @Override
    protected void onResume() {
        super.onResume();
    }

    @Override
    protected void onPause() {
        super.onPause();
    }
}

私にとっては、ActivityBaseによる拡張について覚えておかなければならないより良い方法です。さらに、将来的に基本機能を拡張することができます。私の場合、サービスのレシーバーとネットワークに関するアラートを1つのクラスに追加しました。

アプリの可視性を確認したい場合は、単に呼び出すことができます

MyApplication.isActivityVisible()

AppCombatActivityを拡張するためにアクティビティが必要な場合はどうなりますか?
winklerrr

2

これは、Application.ActivityLifecycleCallbacksを使用して効率的な方法でこれを実現できます。

たとえば、ProfileActivityがフォアグラウンドまたはバックグラウンドのどちらにあるかを見つけることができるように、Activityクラス名を取得できます

最初に、アプリケーションクラスを拡張してアプリケーションクラスを作成する必要があります。

実装する

Application.ActivityLifecycleCallbacks

次のように私のアプリケーションクラスにしましょう

アプリケーションクラス

public class AppController extends Application implements Application.ActivityLifecycleCallbacks {


private boolean activityInForeground;

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

//register ActivityLifecycleCallbacks  

    registerActivityLifecycleCallbacks(this);

}



public static boolean isActivityVisible() {
    return activityVisible;
}

public static void activityResumed() {
    activityVisible = true;
}

public static void activityPaused() {
    activityVisible = false;
}

private static boolean activityVisible;

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

}

@Override
public void onActivityStarted(Activity activity) {

}

@Override
public void onActivityResumed(Activity activity) {
    //Here you can add all Activity class you need to check whether its on screen or not

    activityInForeground = activity instanceof ProfileActivity;
}

@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 boolean isActivityInForeground() {
    return activityInForeground;
}
}

上記のクラスには、ActivityLifecycleCallbacksのonActivityResumedのオーバーライドメソッドがあります。

 @Override
public void onActivityResumed(Activity activity) {
    //Here you can add all Activity class you need to check whether its on screen or not

    activityInForeground = activity instanceof ProfileActivity;
}

現在画面に表示されているすべてのアクティビティインスタンスが見つかる場合は、上記の方法で、アクティビティが画面上にあるかどうかを確認してください。

アプリケーションクラスをmanifest.xmlに登録します。

<application
    android:name=".AppController" />

上記のソリューションに従って、アクティビティがフォアグラウンドまたはバックグラウンドであるかを確認するには、確認する必要がある場所で次のメソッドを呼び出します

AppController applicationControl = (AppController) getApplicationContext();
    if(applicationControl.isActivityInForeground()){
     Log.d("TAG","Activity is in foreground")
    }
    else
    {
      Log.d("TAG","Activity is in background")
    }

1

アプリのアクティビティが画面に表示されているかどうかを知りたい場合は、次のようにできます。

public class MyAppActivityCallbacks implements Application.ActivityLifecycleCallbacks {
private Set<Class<Activity>> visibleActivities = new HashSet<>();

@Override
public void onActivityResumed(Activity activity) {
    visibleActivities.add((Class<Activity>) activity.getClass());
}

@Override
public void onActivityStopped(Activity activity) {
     visibleActivities.remove(activity.getClass());
}

public boolean isAnyActivityVisible() {
    return !visibleActivities.isEmpty();
}

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

@Override
public void onActivityStarted(Activity activity) {}

@Override
public void onActivityPaused(Activity activity) {}

@Override
public void onActivityDestroyed(Activity activity) {}

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

このクラスのシングルトンを作成し、次のようにApplicationインスタンスに設定します。

class App extends Application{
     @Override
     public void onCreate() {
         registerActivityLifecycleCallbacks(myAppActivityCallbacks);
     }
}

次に、MyAppActivityCallbacksインスタンスのisAnyActivityVisible()メソッドをどこでも使用できます。


0

finishを呼び出さず、「android:noHistory = "true"をマニフェストに入れましたか?これにより、アクティビティがスタックに移動できなくなります。


0

私はあなたのワークフローが標準的なAndroidの方法ではないことを言わなければなりません。Androidでは、finish()Intentから別のアクティビティを開く場合は、アクティビティを行う必要はありません。ユーザーの利便性については、Androidではユーザーが「戻る」キーを使用して、開いたアクティビティからアプリに戻ることができます。

したがって、システムにアクティビティを停止させ、アクティビティがコールバックされたときに必要なものをすべて保存してください。


これがなぜダウンレートされているのか、私には本当にわかりません。理由としては、少なくともコメントをお願いします。
Owen Zhao

3
「これはアンドロイドではありません」という方法は、答えが面倒であり、そもそも提起されている質問に答えないようにする方法です。さらに、finish();には正当な理由があります。-たとえば、いったんアクションが実行された後でそれに戻ることは、何の意味もないことが考えられます。言い換えれば、楽しみのためにそこにfinish()を入れていると思いますか?スタックに留まることこそが、質問者が避けたがっていたものです
Lassi Kinnunen

0

一時停止または再開した場合は、フラグを保存してください。再開すると、フォアグラウンドにいることになります

boolean  isResumed = false;

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

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

private void finishIfForeground() {
  if (isResumed) {
    finish();
  }
}

0

考えられる解決策の1つとして、システムダイアログの表示中にフラグを設定し、アクティビティのライフサイクルのonStopメソッドでフラグを確認し、trueの場合はアクティビティを終了します。

たとえば、システムダイアログがボタンクリックによってトリガーされた場合、onclickリスナーは次のようになります。

private OnClickListener btnClickListener = new OnClickListener() {

    @Override
    public void onClick(View v) {           
        Intent intent = new Intent();
        intent.setAction(Intent.ACTION_SEND);
        intent.setType("text/plain");
        CheckActivity.this.startActivity(Intent.createChooser(intent, "Complete action using"));
        checkFlag = true;  //flag used to check

    }
};

活動の停止中:

@Override
protected void onStop() {
    if(checkFlag){
        finish();
    }
    super.onStop();
}

0

これにブロードキャストを使用しないのはなぜですか?2番目のアクティビティ(アップする必要のあるアクティビティ)は、次のようなローカルブロードキャストを送信できます。

//put this in onCreate(..) or any other lifecycle method that suits you best
//notice the string sent to the intent, it will be used to register a receiver!
Intent result = new Intent("broadcast identifier");
result.putString("some message");//this is optional
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(result);

次に、splashアクティビティ内に単純なレシーバーを記述します。

//this goes on the class level (like a class/instance variable, not in a method) of your splash activity:
private BroadcastReceiver receiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        //kill activity here!!!
        //mission accomplished!
    }
};

新しいレシーバーをLocalBroadcastManagerに登録して、2番目のアクティビティからのブロードキャストをリッスンします。

//notice the string sent to the intent filter, this is where you tell the BroadcastManager which broadcasts you want to listen to!
LocalBroadcastManager.getInstance(getApplicationContext()).registerReceiver(receiver, new IntentFilter("broadcast identifier"));

「ブロードキャスト識別子」文字列には定数または文字列リソースを使用できることに注意してください。


セキュリティ広告の効率を高めるには、LocalBroadcastManagerこちらをご利用ください
Alexander Farber

0

finish()アプリのスタック(タスク)で新しいアプリが起動するのを避けるためだけに使用する場合は、新しいアプリを起動するIntent.FLAG_ACTIVITY_NEW_TASKときにフラグを使用して、まったく呼び出さないようにすることができますfinish()ドキュメントによると、これは「ランチャー」スタイルの動作を実装するために使用されるフラグです。

// just add this line before you start an activity
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);

0

内でこれらのメソッドを使用しActivityます。

isDestroyed()


API 17で追加アクティビティで最後のonDestroy()呼び出しが行われた場合にtrueを返すため、このインスタンスは無効になります。

isFinishing()

Api 1に追加されました。Finish
()を呼び出したか、他の誰かが終了を要求したために、このアクティビティが終了処理中であるかどうかを確認してください。これはonPause()でよく使用され、アクティビティが単に一時停止しているか、完全に終了しているかを判断します。


メモリリークのドキュメントから

のよくある間違いAsyncTaskは、ホストActivity(またはFragment)への強い参照をキャプチャすることです。

class MyActivity extends Activity {
  private AsyncTask<Void, Void, Void> myTask = new AsyncTask<Void, Void, Void>() {
    // Don't do this! Inner classes implicitly keep a pointer to their
    // parent, which in this case is the Activity!
  }
}

これは、たとえばタスクの実行中に構成変更が発生した場合などAsyncTask、親よりも長く存続する可能性があるため問題Activityです。

これを行う正しい方法はstatic、親をキャプチャしないクラスをタスクにし、ホストへの弱い参照を保持することですActivity

class MyActivity extends Activity {
  static class MyTask extends AsyncTask<Void, Void, Void> {
    // Weak references will still allow the Activity to be garbage-collected
    private final WeakReference<MyActivity> weakActivity;

    MyTask(MyActivity myActivity) {
      this.weakActivity = new WeakReference<>(myActivity);
    }

    @Override
    public Void doInBackground(Void... params) {
      // do async stuff here
    }

    @Override
    public void onPostExecute(Void result) {
      // Re-acquire a strong reference to the activity, and verify
      // that it still exists and is active.
      MyActivity activity = weakActivity.get();
      if (activity == null
          || activity.isFinishing()
          || activity.isDestroyed()) {
        // activity is no longer valid, don't do anything!
        return;
      }

      // The activity is still valid, do main-thread stuff here
    }
  }
}

0

これは、Applicationクラスを使用したソリューションです。

public class AppSingleton extends Application implements Application.ActivityLifecycleCallbacks {

private WeakReference<Context> foregroundActivity;


@Override
public void onActivityResumed(Activity activity) {
    foregroundActivity=new WeakReference<Context>(activity);
}

@Override
public void onActivityPaused(Activity activity) {
    String class_name_activity=activity.getClass().getCanonicalName();
    if (foregroundActivity != null && 
            foregroundActivity.get().getClass().getCanonicalName().equals(class_name_activity)) {
        foregroundActivity = null;
    }
}

//............................

public boolean isOnForeground(@NonNull Context activity_cntxt) {
    return isOnForeground(activity_cntxt.getClass().getCanonicalName());
}

public boolean isOnForeground(@NonNull String activity_canonical_name) {
    if (foregroundActivity != null && foregroundActivity.get() != null) {
        return foregroundActivity.get().getClass().getCanonicalName().equals(activity_canonical_name);
    }
    return false;
}
}

次のように簡単に使用できます。

((AppSingleton)context.getApplicationContext()).isOnForeground(context_activity);

必要なアクティビティへの参照がある場合、またはアクティビティの正規名を使用している場合は、それがフォアグラウンドにあるかどうかを確認できます。このソリューションは、完全なものではありません。したがって、コメントは大歓迎です。


0

アクティビティAでsharedPreferencesについて誰も話さなかった理由がわからない(たとえば、onPause()で)SharedPreferenceをそのように設定する:

SharedPreferences pref = context.getSharedPreferences(SHARED_PREF, 0);
SharedPreferences.Editor editor = pref.edit();
editor.putBoolean("is_activity_paused_a", true);
editor.commit();

これは、アクティビティの可視性を追跡するための信頼できる方法だと思います。


0

Activity.onWindowFocusChanged(boolean hasFocus)、ここで役に立ちますか?それに加え、クラスレベルのフラグのようなものisFocusedというonWindowFocusChangedセットは、それがフォーカスされているかどうか、あなたの活動のどの時点で伝えるための簡単な方法だろう。ドキュメントを読むと、ダイアログが表示されていたり、通知トレイがプルダウンされているなど、アクティビティが物理的な「フォアグラウンド」に直接ない状況では、適切に「false」に設定されるようです。

例:

boolean isFocused;
@Override
void onWindowFocusChanged (boolean hasFocus) {
    super.onWindowFocusChanged(hasFocus);
    isFocused = hasFocus;
}

void someMethod() {
    if (isFocused) {
        // The activity is the foremost object on the screen
    } else {
        // The activity is obscured or otherwise not visible
    }
}

0

あなたが使用している場合はEventBusと呼ばれる方法として、それをhasSubscriberForEventかどうかを確認するために使用することができますActivity焦点を当てています。


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.