Как проверить, находится ли активность на переднем плане или в видимом фоне?

у меня есть заставка на таймере. Моя проблема в том, что прежде чем я finish() моя деятельность мне нужно проверить, что следующая деятельность началась, потому что появляется диалоговое окно системы, и я хочу только finish(); после того, как пользователь выбрал опцию из диалогового окна?

Я знаю, что есть много вопросов о том, как увидеть, если ваша деятельность находится на переднем плане, но я не знаю, позволяет ли это диалоговые окна поверх деятельности тоже.

здесь проблема, красный-это моя деятельность, которая находится на заднем плане, в то время как диалог находится на переднем плане:

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

EDIT: я пробовал просто не использовать finish() но тогда моя деятельность может быть возвращена в стек приложений, которых я пытаюсь избежать.

21 ответов


это то, что рекомендуется как право устранение:

правильное решение (кредиты идут на Dan, CommonsWare и NeTeInStEiN) Отслеживание видимости приложения самостоятельно с помощью Деятельность.методов onPause, активность.методы 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 etc. еще нужно написать от руки следующее):

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

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

в своем finish() метод, который вы хотите использовать isActivityVisible() чтобы проверить, является ли активность видимой или нет. Там вы также можете проверить, выбрал ли пользователь опцию или нет. Продолжайте при выполнении обоих условий.

источник также упоминает два неправильных решения...поэтому избегайте этого.

источник: stackoverflow


если таргетинг API уровня 14 или выше, можно использовать android.приложение.Приложение.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
    ....
}

именно в этом разница между onPause и onStop события действия, описанные в класс Activity документации.

если я правильно понимаю, что вы хотите сделать, это позвонить finish() от вашей активности onStop расторгнуть его. См. прикрепленное изображение Демо-Приложение Жизненного Цикла Активности. Вот как это выглядит, когда Activity B запускается из Activity A. Порядок событий снизу вверх, так что вы можете видеть, что Действие A onStop вызывается после действия B onResume уже звонили.

Activity lifecycle demo

в случае, если диалог показан ваша деятельность затемняется в фоновом режиме и только onPause называется.


два возможных решения :

1) Обратный Вызов Жизненного Цикла Активности

использовать приложение что реализует ActivityLifecycleCallbacks и использовать его для отслеживания событий жизненного цикла деятельности в приложении. Обратите внимание, что ActivityLifecycleCallbacks предназначены для Android api >= 14. Для предыдущего Android api вам нужно будет реализовать его самостоятельно во всех ваших действиях; -)

использовать приложение когда вам нужно поделиться / хранить состояния перекрестных действий.

2) проверьте, работает ли информация о процессе

вы можете проверить состояние запущенного процесса с помощью этого класса RunningAppProcessInfo

получить список запущенных процессов с ActivityManager.getRunningAppProcesses () и отфильтруйте список результатов, чтобы проверить желаемый RunningAppProcessInfo и проверить его "важность"


активность:: 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, так как диалог будет иметь основной фокус. Кроме того, это действительно удобно и надежнее, чем предлагаемые решения.


У меня есть создать проект на github app-передний план-фон-слушайте

который использует очень простую логику и отлично работает со всем уровнем API android.


используйте промежуток времени между паузой и возобновлением из фона, чтобы определить, проснулся ли он из фона

В Пользовательском Приложении

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

Я думаю, что у меня есть лучшее решение. Потому что вы можете создать просто MyApplication.activityResumed (); к каждой деятельности по одному распространяются.

во-первых, вы должны создать (как CyberneticTwerkGuruOrc)

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" >

затем создайте класс 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. Кроме того, вы можете расширить свою базовую функцию в будущем. В моем случае я добавил приемники для моей службы и оповещения о сети в одном классе.

если вы хотите проверить видимость вашего приложения, вы можете просто позвонить

MyApplication.isActivityVisible()

вы пытались не вызывать finish и поставить "android:noHistory="true" в манифесте? это предотвратит переход активности в стек.


Я должен сказать, что рабочий процесс не является стандартным способом для Android. В Android, вам не нужно finish() ваша деятельность, если вы хотите открыть другую деятельность из намерения. Что касается удобства пользователя, Android позволяет пользователю использовать клавишу "назад", чтобы вернуться от действия, которое вы открыли в приложение.

поэтому просто позвольте системе остановить вашу деятельность и сохранить все, что нужно, когда ваша деятельность будет отозвана.


Я не знаю, почему никто не говорил о sharedPreferences, для действия A, устанавливая SharedPreference так (например, в onPause ()):

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

Я думаю, что это надежный способ отслеживать действия visibilty.


Если вы хотите знать, если любой активности вашего приложения отображается на экране, вы можете сделать что-то вроде этого:

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

тогда вы можете использовать метод isAnyActivityVisible() вашего экземпляра MyAppActivityCallbacks везде!


сохраните флаг, если вы приостановлены или возобновлены. Если вы возобновлены, это означает, что вы находитесь на переднем плане

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

одним из возможных решений может быть установка флага при отображении системного диалога, а затем в методе onStop жизненного цикла действия проверьте флаг, если true, завершите действие.

например, если системный диалог запускается каким-то buttonclick, то прослушиватель 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

    }
};

и в onstop деятельности:

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

почему бы не использовать эфир для этого? второе действие (то, которое должно быть включено) может отправлять локальную трансляцию следующим образом:

//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 activity:

//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, чтобы прослушать трансляцию из вашего второго действия:

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

обратите внимание, что вы можете использовать константу или строковый ресурс для строки "широковещательный идентификатор".


если вы используете finish() чтобы избежать запуска нового приложения в стеке (задаче) вашего приложения, вы можете использовать Intent.FLAG_ACTIVITY_NEW_TASK флаг, при запуске нового приложения и не называть finish() на всех. Согласно документация, это флаг, который будет использоваться для реализации поведения стиля "launcher".

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

это может достичь этого эффективным способом, используя приложение.ActivityLifecycleCallbacks

например, давайте возьмем имя класса активности как ProfileActivity позволяет найти, является ли его на переднем плане или фоне

сначала нам нужно создать наш класс приложений, расширив Класса

, который реализует

приложение.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;
}
}

в вышеуказанном классе есть переопределение methord onActivityResumed of ActivityLifecycleCallbacks

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

где можно найти все экземпляры активности, которые в настоящее время отображаются на экране, просто проверьте, находится ли ваша активность на экране или нет выше метод.

зарегистрируйте класс приложения в манифесте.в 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")
    }

используйте эти методы внутри Activity.

isDestroyed()

добавлено в Api 17
Возвращает true, если последний вызов onDestroy() был сделан на Активности, так что этот экземпляр теперь мертв.

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

вот решение с помощью 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);

Если у вас есть ссылка на требуемое действие или использование канонического имени действия, вы можете узнать, находится ли оно на переднем плане или нет. Это решение не может быть верным. Поэтому ваши комментарии очень приветствуются.


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

Я люблю,

Если активность не находится на переднем плане

getIntent ()

возвращают значение null. : =P