web-dev-qa-db-ja.com

アクティビティがフォアグラウンドにあるのか、目に見えるバックグラウンドにあるのかを確認する方法は?

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

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

ここに問題があります。赤は、ダイアログがフォアグラウンドにあるときにバックグラウンドにある私のアクティビティです。

the red is my activity which is in the background while the dialogue is in the foreground

編集:finish()を使用しないでみましたが、回避しようとしているアプリケーションのスタックでアクティビティを元に戻すことができます。

94
Nick

これは、rightソリューションとして推奨されるものです。

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

ExampleカスタムApplicationクラスを実装します(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

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
    ....
}
61

Activity class documentation で説明されているように、それはまさに、アクティビティのonPauseイベントとonStopイベントの違いです。

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

Activity lifecycle demo

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

10
Muzikant

2019年、新しいサポートライブラリ28+またはAndroidXの助けを借りて、単純に使用できます:

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

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

8
Alex Misulya

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;
    }
}

どこにいてもアクティビティの可視性を確認するためのクラスの例を次に示します。

dialogを表示する場合、ダイアログにはメインがあるため、結果はfalseになることに注意してくださいフォーカス。それ以外は、提案されたソリューションよりも本当に便利で信頼性が高いです。

8
Burak Day

2つの可能な解決策:

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

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

アクティビティ間で状態を共有/保存する必要がある場合は、 Application を使用します。

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

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

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

7
avianey

Githubでプロジェクトを作成しました app-foreground-background-listen

非常に単純なロジックを使用し、すべてのAndroid APIレベルで正常に動作します。

4
kiran boghra

より良い解決策があると思います。単に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();
    }
}

最後に、新しいアクティビティを作成するときは、アクティビティの代わりにActivityBaseによって単純に拡張できます。

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

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

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

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

MyApplication.isActivityVisible()
3
EliaszKubala

バックグラウンドからの一時停止と再開の間の時間ギャップを使用して、バックグラウンドから目覚めているかどうかを判断します

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

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
Kit

これは、 Application.ActivityLifecycleCallbacks を使用することにより、効率的な方法でこれを達成できます。

たとえば、Activityクラス名をProfileActivityとして、その前景または背景にあるかどうかを確認できます。

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

実装する

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")
    }
2
Ramz

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

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

1
Owen Zhao

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

1
shaish

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

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) {}}

このクラスのシングルトンを作成し、次のようにアプリケーションインスタンスに設定するだけです。

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

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

1

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

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
yoah

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

// just add this line before you start an activity
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
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
InsanityOnABun

これにブロードキャストを使用しないのはなぜですか? 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);

次に、スプ​​ラッシュアクティビティ内に簡単なレシーバーを記述します。

//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"));

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

0
user1545072

これらのメソッドはActivity内で使用してください。

isDestroyed()

Api 17に追加
最後のonDestroy()呼び出しがActivityに対して行われた場合にtrueを返すため、このインスタンスは現在停止しています。

isFinishing()

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


From Memory Leaks Documentation

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
Sanket Berde

考えられる解決策の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
Diya

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
TMtech

アクティビティAのsharedPreferencesについて誰も話さなかった理由がわかりません(そのようなSharedPreferenceを設定します(たとえばonPause()))。

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

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

0
HZDI