Как получить текущий контекст активности переднего плана в Android?

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

19 ответов

Решение

Зная, что ActivityManager управляет Activity, мы можем получать информацию из ActivityManager. Мы получаем текущий передний план, запускающий Activity by

ActivityManager am = (ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE);
ComponentName cn = am.getRunningTasks(1).get(0).topActivity;

ОБНОВЛЕНИЕ 2018/10/03
getRunningTasks() УСТАРЕЛО. увидеть решения ниже.

Этот метод устарел на уровне API 21. Начиная с версии Build.VERSION_CODES.LOLLIPOP, этот метод больше не доступен для сторонних приложений: введение центрированных на документе рецентов означает, что он может передавать информацию о человеке вызывающей стороне. Для обратной совместимости он по-прежнему будет возвращать небольшое подмножество своих данных: по крайней мере, собственные задачи вызывающего абонента и, возможно, некоторые другие задачи, например домашние, которые, как известно, не являются чувствительными.

(Примечание: официальный API был добавлен в API 14: см. Этот ответ /questions/14590763/kak-poluchit-tekuschij-kontekst-aktivnosti-perednego-plana-v-android/14590778#14590778)

НЕ ИСПОЛЬЗУЙТЕ ПРЕДЫДУЩИЙ (waqas716) ответ.

У вас будет проблема утечки памяти из-за статической ссылки на действие. Для более подробной информации смотрите следующую ссылку http://android-developers.blogspot.fr/2009/01/avoiding-memory-leaks.html

Чтобы избежать этого, вы должны управлять ссылками на действия. Добавьте название приложения в файл манифеста:

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

Ваш класс приложения:

  public class MyApp extends Application {
        public void onCreate() {
              super.onCreate();
        }

        private Activity mCurrentActivity = null;
        public Activity getCurrentActivity(){
              return mCurrentActivity;
        }
        public void setCurrentActivity(Activity mCurrentActivity){
              this.mCurrentActivity = mCurrentActivity;
        }
  }

Создать новую активность:

public class MyBaseActivity extends Activity {
    protected MyApp mMyApp;

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mMyApp = (MyApp)this.getApplicationContext();
    }
    protected void onResume() {
        super.onResume();
        mMyApp.setCurrentActivity(this);
    }
    protected void onPause() {
        clearReferences();
        super.onPause();
    }
    protected void onDestroy() {        
        clearReferences();
        super.onDestroy();
    }

    private void clearReferences(){
        Activity currActivity = mMyApp.getCurrentActivity();
        if (this.equals(currActivity))
            mMyApp.setCurrentActivity(null);
    }
}

Итак, теперь вместо расширения класса Activity для ваших действий просто расширьте MyBaseActivity. Теперь вы можете получить текущую активность из приложения или из контекста активности следующим образом:

Activity currentActivity = ((MyApp)context.getApplicationContext()).getCurrentActivity();

Я раскрываю верхнюю часть ответа @gezdy.

В каждой деятельности вместо того, чтобы "регистрироваться" Application с помощью ручного кодирования мы можем использовать следующий API начиная с уровня 14, чтобы помочь нам достичь аналогичной цели с меньшим количеством ручного кодирования.

public void registerActivityLifecycleCallbacks (Application.ActivityLifecycleCallbacks callback)

http://developer.android.com/reference/android/app/Application.html

В Application.ActivityLifecycleCallbacksВы можете получить какой Activity "привязан" или "отделен" от этого Application,

Однако этот метод доступен только с уровня API 14.

Обновление 2: Для этого добавлен официальный API, используйте взамен ActivityLifecycleCallbacks.

ОБНОВИТЬ:

Как указано @gezdy, и я благодарен за это. установите ссылку на null тоже для текущей активности, вместо обновления только для каждого onResume, установите для нее значение null в каждой активности onDestroy, чтобы избежать проблемы утечки памяти.

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

@Override
protected void onResume() {
    super.onResume();
    appConstantsObj.setCurrentActivity(this);

}

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

@Override
protected void onDestroy() {        
   clearReferences();
   super.onDestroy();
}

private void clearReferences(){
          Activity currActivity = appConstantsObj.getCurrentActivity();
          if (this.equals(currActivity))
                appConstantsObj.setCurrentActivity(null);
}

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

@lockwobr Спасибо за обновление

Это не работает в 100% случаев в API версии 16, если вы читаете код на github, функция "currentActivityThread" была изменена в Kitkat, поэтому я хочу сказать, что версия 19ish, довольно сложно сопоставить версию API с выпусками в github,

Имея доступ к текущему Activity очень удобно Не было бы неплохо иметь статичный getActivity способ вернуть текущую активность без лишних вопросов?

Activity класс очень полезен. Он предоставляет доступ к потоку пользовательского интерфейса приложения, представлениям, ресурсам и многому другому. Многочисленные методы требуют Contextа как получить указатель? Вот несколько способов:

  • Отслеживание состояния приложения с использованием переопределенных методов жизненного цикла. Вы должны сохранить текущую активность в статической переменной, и вам нужен доступ к коду всех операций.
  • Отслеживание состояния приложения с использованием Инструментов. Объявите Инструментарий в манифесте, внедрите его и используйте его методы для отслеживания изменений в Деятельности. Передача указателя Activity на методы и классы, используемые в ваших Activity. Внедрение указателя с использованием одной из библиотек внедрения кода. Все эти подходы довольно неудобны; К счастью, есть намного более простой способ получить текущую активность.
  • Похоже, что системе нужен доступ ко всем действиям без проблем, упомянутых выше. Так что, скорее всего, есть способ получить Activity, используя только статические вызовы. Я потратил много времени, копаясь в источниках Android на grepcode.com, и нашел то, что искал. Есть класс под названием ActivityThread, Этот класс имеет доступ ко всем действиям и, что еще лучше, имеет статический метод для получения текущего ActivityThread, Есть только одна маленькая проблема - список действий имеет доступ к пакету.

Легко решить с помощью отражения:

public static Activity getActivity() {
    Class activityThreadClass = Class.forName("android.app.ActivityThread");
    Object activityThread = activityThreadClass.getMethod("currentActivityThread").invoke(null);
    Field activitiesField = activityThreadClass.getDeclaredField("mActivities");
    activitiesField.setAccessible(true);

    Map<Object, Object> activities = (Map<Object, Object>) activitiesField.get(activityThread);
    if (activities == null)
        return null;

    for (Object activityRecord : activities.values()) {
        Class activityRecordClass = activityRecord.getClass();
        Field pausedField = activityRecordClass.getDeclaredField("paused");
        pausedField.setAccessible(true);
        if (!pausedField.getBoolean(activityRecord)) {
            Field activityField = activityRecordClass.getDeclaredField("activity");
            activityField.setAccessible(true);
            Activity activity = (Activity) activityField.get(activityRecord);
            return activity;
        }
    }

    return null;
}

Такой метод можно использовать в любом месте приложения, и он намного удобнее, чем все упомянутые подходы. Более того, кажется, что это не так опасно, как кажется. Он не вводит никаких новых потенциальных утечек или нулевых указателей.

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

Сообщение блога

Я сделал следующее в Котлине

  1. Создать класс приложения
  2. Изменить класс приложения следующим образом

    class FTApplication: MultiDexApplication() {
    override fun attachBaseContext(base: Context?) {
        super.attachBaseContext(base)
        MultiDex.install(this)
    }
    
    init {
        instance = this
    }
    
    val mFTActivityLifecycleCallbacks = FTActivityLifecycleCallbacks()
    
    override fun onCreate() {
        super.onCreate()
    
        registerActivityLifecycleCallbacks(mFTActivityLifecycleCallbacks)
    }
    
    companion object {
        private var instance: FTApplication? = null
    
        fun currentActivity(): Activity? {
    
            return instance!!.mFTActivityLifecycleCallbacks.currentActivity
        }
    }
    
     }
    
  3. Создайте класс ActivityLifecycleCallbacks

    class FTActivityLifecycleCallbacks: Application.ActivityLifecycleCallbacks {
    
    var currentActivity: Activity? = null
    
    override fun onActivityPaused(activity: Activity?) {
        currentActivity = activity
    }
    
    override fun onActivityResumed(activity: Activity?) {
        currentActivity = activity
    }
    
    override fun onActivityStarted(activity: Activity?) {
        currentActivity = activity
    }
    
    override fun onActivityDestroyed(activity: Activity?) {
    }
    
    override fun onActivitySaveInstanceState(activity: Activity?, outState: Bundle?) {
    }
    
    override fun onActivityStopped(activity: Activity?) {
    }
    
    override fun onActivityCreated(activity: Activity?, savedInstanceState: Bundle?) {
        currentActivity = activity
    }
    
    }
    
  4. теперь вы можете использовать его в любом классе, вызвав следующее: FTApplication.currentActivity()

getCurrentActivity() также находится в ReactContextBaseJavaModule.
(Поскольку этот вопрос был задан изначально, многие приложения для Android также имеют компонент ReactNative - гибридное приложение.)

Класс ReactContext в ReactNative имеет весь набор логики для поддержки mCurrentActivity, который возвращается в getCurrentActivity().

Примечание. Мне бы хотелось, чтобы функция getCurrentActivity() была реализована в классе приложений Android.

Для обратной совместимости:

ComponentName cn;
ActivityManager am = (ActivityManager) getApplicationContext().getSystemService(Context.ACTIVITY_SERVICE);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
    cn = am.getAppTasks().get(0).getTaskInfo().topActivity;
} else {
    //noinspection deprecation
    cn = am.getRunningTasks(1).get(0).topActivity;
}

Я не мог найти решение, которым наша команда была бы довольна, поэтому мы сделали свое дело. Мы используем ActivityLifecycleCallbacks отслеживать текущую активность, а затем выставлять ее через сервис. Более подробная информация здесь: /questions/20260429/android-kak-ya-mogu-poluchit-tekuschuyu-aktivnost-perednego-plana-iz-sluzhbyi/20260444#20260444

Лучшее решение на данный момент Создайте имя класса ActivityManager в своем приложении (java)

      public class ActivityManager implements Application.ActivityLifecycleCallbacks {

    private Activity activity;


    public ActivityManager(App myApplication) {
        myApplication.registerActivityLifecycleCallbacks(this);
    }

    public Activity getActivity(){
        return activity;
    }
    @Override
    public void onActivityCreated(@NonNull Activity activity, @Nullable Bundle bundle) {
        this. activity = activity;

    }

    @Override
    public void onActivityStarted(@NonNull Activity activity) {
       this. activity = activity;
    }

    @Override
    public void onActivityResumed(@NonNull Activity activity) {
        this. activity = activity;

    }

    @Override
    public void onActivityPaused(@NonNull Activity activity) {

    }

    @Override
    public void onActivityStopped(@NonNull Activity activity) {

    }

    @Override
    public void onActivitySaveInstanceState(@NonNull Activity activity, @NonNull Bundle bundle) {

    }

    @Override
    public void onActivityDestroyed(@NonNull Activity activity) {

    }
}

Затем инициализируйте его в своем приложении (kotlin)

      class App : Application() {

    override fun onCreate() {
     
        appOpenManager =  AppOpenManager(this);
    }
  companion object {
        lateinit var appOpenManager: AppOpenManager
    }
}

Затем используйте как

Приложение.ActivityManager.getActivity()

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

Мой ответ основан на этой статье: Android: как определить, когда приложение переходит в фоновый/передний план

Сначала создайте класс, расширяющий android.app.Applicationи реализует ActivityLifecycleCallbacksинтерфейс. в Application.onCreate(), зарегистрируйте обратный вызов.

      public class App extends Application implements ActivityLifecycleCallbacks
@Override
public void onCreate() {
    super.onCreate();
    registerActivityLifecycleCallbacks(this);
}

Зарегистрируйте класс «App» в манифесте, как показано ниже:

      <application
    android:name=".App"

Вот так выглядит интерфейс ActivityLifecycleCallbacks,

      public interface ActivityLifecycleCallbacks {
    void onActivityCreated(Activity activity, Bundle savedInstanceState);
    void onActivityStarted(Activity activity);
    void onActivityResumed(Activity activity);
    void onActivityPaused(Activity activity);
    void onActivityStopped(Activity activity);
    void onActivitySaveInstanceState(Activity activity, Bundle outState);
    void onActivityDestroyed(Activity activity);
}

Таким образом, когда любое из ваших действий (действия, которые вы создали или включили в свои библиотеки) проходит через любой из вышеупомянутых методов жизненного цикла, эти обратные вызовы будут вызываться. Будет хотя бы одно действие в запущенном состоянии, когда приложение находится на переднем плане, и не будет никакого действия в запущенном состоянии, когда приложение находится в фоновом режиме. Объявите 2 переменные, как показано ниже, в классе «App».

      private int activityReferences = 0;
private boolean isActivityChangingConfigurations = false;

ActivityReferences будет вести подсчет количества действий в запущенном состоянии. isActivityChangingConfigurations — это флаг, указывающий, проходит ли текущее действие изменение конфигурации, например переключение ориентации. Используя следующий код, вы можете определить, выходит ли приложение на передний план.

      @Override
public void onActivityStarted(Activity activity) {
    if (++activityReferences == 1 && !isActivityChangingConfigurations) {
        // App enters foreground
    }
}

Вы можете получить доступ к контексту в этом методе следующим образом:

      activity.getBaseContext()

Вот как определить, работает ли приложение в фоновом режиме.

      Override
public void onActivityStopped(Activity activity) {

    isActivityChangingConfigurations = activity.isChangingConfigurations();
    if (--activityReferences == 0 && !isActivityChangingConfigurations) {
        // App enters background
    }
}

Теперь вы можете получить доступ к имени и контексту текущей активности переднего плана.

Лично я сделал, как сказал "Чок Ян Ченг", но использовал "Список", чтобы получить "Backstack" во всех моих действиях.

Если вы хотите проверить, что является текущей активностью, вам просто нужно получить последний класс активности в списке.

Создайте приложение, которое расширяет "Приложение", и сделайте это:

public class MyApplication extends Application implements Application.ActivityLifecycleCallbacks,
EndSyncReceiver.IEndSyncCallback {

private List<Class> mActivitiesBackStack;
private EndSyncReceiver mReceiver;
    private Merlin mMerlin;
    private boolean isMerlinBound;
    private boolean isReceiverRegistered;

@Override
    public void onCreate() {
        super.onCreate();
        [....]
RealmHelper.initInstance();
        initMyMerlin();
        bindMerlin();
        initEndSyncReceiver();
        mActivitiesBackStack = new ArrayList<>();
    }

/* START Override ActivityLifecycleCallbacks Methods */
    @Override
    public void onActivityCreated(Activity activity, Bundle bundle) {
        mActivitiesBackStack.add(activity.getClass());
    }

    @Override
    public void onActivityStarted(Activity activity) {
        if(!isMerlinBound){
            bindMerlin();
        }
        if(!isReceiverRegistered){
            registerEndSyncReceiver();
        }
    }

    @Override
    public void onActivityResumed(Activity activity) {

    }

    @Override
    public void onActivityPaused(Activity activity) {

    }

    @Override
    public void onActivityStopped(Activity activity) {
        if(!AppUtils.isAppOnForeground(this)){
            if(isMerlinBound) {
                unbindMerlin();
            }
            if(isReceiverRegistered){
                unregisterReceiver(mReceiver);
            }
            if(RealmHelper.getInstance() != null){
                RealmHelper.getInstance().close();
                RealmHelper.getInstance().logRealmInstanceCount("AppInBackground");
                RealmHelper.setMyInstance(null);
            }
        }
    }

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

    }

    @Override
    public void onActivityDestroyed(Activity activity) {
        if(mActivitiesBackStack.contains(activity.getClass())){
            mActivitiesBackStack.remove(activity.getClass());
        }
    }
    /* END Override ActivityLifecycleCallbacks Methods */

/* START Override IEndSyncCallback Methods */
    @Override
    public void onEndSync(Intent intent) {
        Constants.SyncType syncType = null;
        if(intent.hasExtra(Constants.INTENT_DATA_SYNC_TYPE)){
            syncType = (Constants.SyncType) intent.getSerializableExtra(Constants.INTENT_DATA_SYNC_TYPE);
        }
        if(syncType != null){
            checkSyncType(syncType);
        }
    }
    /* END IEndSyncCallback Methods */

private void checkSyncType(Constants.SyncType){
    [...]
    if( mActivitiesBackStack.contains(ActivityClass.class) ){
         doOperation()     }
}

}

В моем случае я использовал "Application.ActivityLifecycleCallbacks", чтобы:

  • Bind / Unbind Merlin Instance (используется для получения события, когда приложение теряет или получает соединение, например, когда вы закрываете мобильные данные или когда вы открываете его). Это полезно после того, как намеренное действие "OnConnectivityChanged" было отключено. Для получения дополнительной информации о MERLIN см.: MERLIN INFO LINK

  • Закрыть мой последний экземпляр области, когда приложение закрыто; Я начну его внутри BaseActivity, которое расширено от всех других действий и имеет частный экземпляр RealmHelper. Для получения дополнительной информации о REALM см.: REALM INFO LINK. Например, у меня есть статический экземпляр "RealmHelper" внутри моего класса "RealmHelper", который создается внутри моего приложения "onCreate". У меня есть служба синхронизации, в которой я создаю новый "RealmHelper", потому что Realm "связан с потоками", а экземпляр области не может работать в другом потоке. Поэтому для того, чтобы следовать документации Realm "Вам нужно закрыть все открытые экземпляры областей, чтобы избежать утечек системных ресурсов", для достижения этой цели я использовал "Application.ActivityLifecycleCallbacks", как вы можете видеть вверх.

  • Наконец, у меня есть получатель, который запускается, когда я заканчиваю синхронизацию моего приложения, затем, когда заканчивается синхронизация, он вызывает метод "IEndSyncCallback" "onEndSync", в котором я проверяю, есть ли у меня определенный класс Activity в моем Списке ActivityBackStack, потому что мне нужно выполнить некоторую операцию, если я уже прошел этот пункт в моем приложении.

Вот и все, надеюсь, это полезно. Увидимся:)

Ответ от Waqas хорош. Я создал обходной путь для конкретного случая, требующего меньше кода и обслуживания.

Я нашел конкретную работу, применив статический метод, извлекающий представление из действия, которое, как я подозреваю, находится на переднем плане. Вы можете перебирать все действия и проверять, хотите ли вы или получаете название действия из ответа Мартина

ActivityManager am = (ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE);
ComponentName cn = am.getRunningTasks(1).get(0).topActivity; 

Затем я проверяю, не является ли представление нулевым, и получаю контекст через getContext().

View v = SuspectedActivity.get_view();

if(v != null)
{
    // an example for using this context for something not 
    // permissible in global application context. 
    v.getContext().startActivity(new Intent("rubberduck.com.activities.SomeOtherActivity"));
}

Если вы используете kotlin, это может помочь вам получить текущее имя активности. Однако метод getRecentTasks() устарел в Java.

       val am: ActivityManager = applicationContext.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
 val activityName: String = am.getRecentTasks(1, 0).get(0).topActivity.toString()

Использовать isоператор или его отрицательная форма !isдля выполнения проверки во время выполнения, которая определяет, соответствует ли объект заданному типу:

      if (this !is OneActivity) {
// do something
} else if (this !is TwoActivity) {
// do something 2
}

Вы можете использовать этот класс для гибкой обработки жизненного цикла

Использование:

          //Initialization
    val lifeCycleHandler = ActivityLifeCycleHandler<Activity>()

    //Detect only a specific type of activities
    val lifeCycleHandler = ActivityLifeCycleHandler<MainActivity>()

    //Get current activity
    val instance = lifeCycleHandler.currentReference

    //Get current activity state
    val state = lifeCycleHandler.currentState

    //Use listeners
    lifeCycleHandler.addStateChangeListener { newState ->
        //TODO: handle new state
    }

    lifeCycleHandler.addSpecificStateChangeListener(ActivityLifeCycleHandler.ActivityState.STARTED) {
        //TODO: handle new state
    }

    //Removable listeners
    val listener = { newState: Int ->

    }

    lifeCycleHandler.addStateChangeListener(listener)
    lifeCycleHandler.removeStateChageListener(listener)


    //Start listening
    App.app.registerActivityLifecycleCallbacks(lifeCycleHandler)

    //Stop listening
    lifeCycleHandler.releaseListeners()
    App.app.unregisterActivityLifecycleCallbacks(lifeCycleHandler)

Мне не нравятся другие ответы. ActivityManager не предназначен для получения текущей активности. Супер классификация и в зависимости от onDestroy также хрупкая и не лучший дизайн.

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

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

Довольно простое решение - создать класс синглтон-менеджера, в котором вы можете хранить ссылку на одно или несколько действий или что-то еще, к чему вы хотите получить доступ во всем приложении.

Вызов UberManager.getInstance().setMainActivity( activity ); в основной деятельности по созданию.

Вызов UberManager.getInstance().getMainActivity(); в любом месте вашего приложения, чтобы получить его. (Я использую это, чтобы иметь возможность использовать Toast из потока, не являющегося пользовательским интерфейсом.)

Убедитесь, что вы добавили звонок в UberManager.getInstance().cleanup(); когда ваше приложение уничтожается.

import android.app.Activity;

public class UberManager
{
    private static UberManager instance = new UberManager();

    private Activity mainActivity = null;

    private UberManager()
    {

    }

    public static UberManager getInstance()
    {
        return instance;
    }

    public void setMainActivity( Activity mainActivity )
    {
        this.mainActivity = mainActivity;
    }

    public Activity getMainActivity()
    {
        return mainActivity;
    }

    public void cleanup()
    {
        mainActivity = null;
    }
}

Я опоздал на 3 года, но я все равно отвечу, если кто-то найдет это, как я.

Я решил это, просто используя это:

    if (getIntent().toString().contains("MainActivity")) {
        // Do stuff if the current activity is MainActivity
    }

Обратите внимание, что "getIntent(). ToString()" включает в себя множество других текстов, таких как имя вашего пакета и любые фильтры намерений для вашей деятельности. Технически мы проверяем текущее намерение, а не деятельность, но результат тот же. Просто используйте, например, Log.d("test", getIntent(). ToString()); если вы хотите увидеть весь текст. Это решение немного хакерское, но оно намного чище в вашем коде и функциональность та же.

Другие вопросы по тегам