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

Есть ли собственный способ андроида, чтобы получить ссылку на текущую активность из службы?

У меня есть служба, работающая в фоновом режиме, и я хотел бы обновить текущую активность, когда происходит событие (в службе). Есть ли простой способ сделать это (например, тот, который я предложил выше)?

Ответ 1

Есть ли собственный способ андроида, чтобы получить ссылку на текущую активность из службы?

Возможно, вам не принадлежит "текущая работа".

У меня есть служба, работающая в фоновом режиме, и я хотел бы обновить текущую активность, когда происходит событие (в службе). Есть ли простой способ сделать это (например, тот, который я предложил выше)?

  • Отправить трансляцию Intent в действие - вот пример проекта, демонстрирующий этот шаблон
  • Имейте операцию, поставьте PendingIntent (например, через createPendingResult()), что служба вызывает
  • Регистрируйте объект обратного вызова или слушателя с помощью службы через bindService() и получите вызов службы метод события для этого объекта обратного вызова/прослушивания
  • Отправьте упорядоченную широковещательную рассылку Intent в действие с низкоприоритетным BroadcastReceiver как резервное копирование (для повышения Notification, если активность не находится на экране) - вот сообщение в блоге с более подробным описанием этого шаблона

Ответ 2

Вот хороший способ сделать это с помощью диспетчера активности. Вы в основном получаете runTasks из диспетчера активности. Он всегда будет возвращать текущую активную задачу. Оттуда вы можете получить topActivity.

Пример здесь

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

После этого вы можете получить объект ComponentName, запросив topActivity из вашего списка.

Вот пример.

    ActivityManager am = (ActivityManager) this.getSystemService(ACTIVITY_SERVICE);
    List<ActivityManager.RunningTaskInfo> taskInfo = am.getRunningTasks(1);
    Log.d("topActivity", "CURRENT Activity ::" + taskInfo.get(0).topActivity.getClassName());
    ComponentName componentInfo = taskInfo.get(0).topActivity;
    componentInfo.getPackageName();

Вам понадобится следующее разрешение на ваш манифест:

<uses-permission android:name="android.permission.GET_TASKS"/>

Ответ 3

Предупреждение: нарушение Google Play

Использование этого метода может привести к удалению вашего приложения из Google Play. Недавно Google сказал мне обновить мое приложение, потому что оно неправильно использовало службу доступности. Я проверил документацию и подтвердил, что это так:

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


Используйте AccessibilityService

Преимущества

  • Протестировано и работает в Android 2.2 (API 8) через Android 7.1 (API 25).
  • Не требует опроса.
  • Не требует разрешения GET_TASKS.

Недостатки

  • Каждый пользователь должен включить службу в настройках доступности Android.
  • Служба всегда работает.
  • Когда пользователь пытается включить AccessibilityService, они не могут нажать кнопку OK, если приложение разместило наложение на экран. Некоторые приложения, которые делают это, это Velis Auto Brightness и Lux. Это может сбивать с толку, потому что пользователь может не знать, почему они не могут нажать кнопку или как ее обойти.
  • AccessibilityService не будет знать текущую активность до первого изменения активности. li >

Пример

Сервис

public class WindowChangeDetectingService extends AccessibilityService {

    @Override
    protected void onServiceConnected() {
        super.onServiceConnected();

        //Configure these here for compatibility with API 13 and below.
        AccessibilityServiceInfo config = new AccessibilityServiceInfo();
        config.eventTypes = AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED;
        config.feedbackType = AccessibilityServiceInfo.FEEDBACK_GENERIC;

        if (Build.VERSION.SDK_INT >= 16)
            //Just in case this helps
            config.flags = AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS;

        setServiceInfo(config);
    }

    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) {
        if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
            if (event.getPackageName() != null && event.getClassName() != null) {
                ComponentName componentName = new ComponentName(
                    event.getPackageName().toString(),
                    event.getClassName().toString()
                );

                ActivityInfo activityInfo = tryGetActivity(componentName);
                boolean isActivity = activityInfo != null;
                if (isActivity)
                    Log.i("CurrentActivity", componentName.flattenToShortString());
            }
        }
    }

    private ActivityInfo tryGetActivity(ComponentName componentName) {
        try {
            return getPackageManager().getActivityInfo(componentName, 0);
        } catch (PackageManager.NameNotFoundException e) {
            return null;
        }
    }

    @Override
    public void onInterrupt() {}
}

AndroidManifest.xml

Объедините это в свой манифест:

<application>
    <service
        android:label="@string/accessibility_service_name"
        android:name=".WindowChangeDetectingService"
        android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
        <intent-filter>
            <action android:name="android.accessibilityservice.AccessibilityService"/>
        </intent-filter>
        <meta-data
            android:name="android.accessibilityservice"
            android:resource="@xml/accessibilityservice"/>
    </service>
</application>

Сервисная информация

Поместите это в res/xml/accessibilityservice.xml:

<?xml version="1.0" encoding="utf-8"?>
<!-- These options MUST be specified here in order for the events to be received on first
 start in Android 4.1.1 -->
<accessibility-service
    xmlns:tools="http://schemas.android.com/tools"
    android:accessibilityEventTypes="typeWindowStateChanged"
    android:accessibilityFeedbackType="feedbackGeneric"
    android:accessibilityFlags="flagIncludeNotImportantViews"
    android:description="@string/accessibility_service_description"
    xmlns:android="http://schemas.android.com/apk/res/android"
    tools:ignore="UnusedAttribute"/>

Включение службы

Каждый пользователь приложения должен будет явно включить AccessibilityService, чтобы он мог использоваться. См. fooobar.com/questions/30140/..., чтобы узнать, как это сделать.

Обратите внимание, что пользователь не сможет нажать кнопку "ОК" при попытке включить службу доступности, если приложение разместило наложение на экране, например, Velis Auto Brightness или Lux.

Ответ 4

Используйте ActivityManager

Если вы хотите узнать приложение, содержащее текущую активность, вы можете сделать это, используя ActivityManager. Техника, которую вы можете использовать, зависит от версии Android:

Преимущества

  • Должна работать во всех версиях Android.

Недостатки

  • Не работает в Android M (на основе тестирования с использованием эмулятора предварительного выпуска)
  • Документация для этих API говорит, что они предназначены только для пользовательских интерфейсов отладки и управления.
  • Если вам нужны обновления в реальном времени, вам нужно использовать опрос.
  • Опирается на скрытый API: ActivityManager.RunningAppProcessInfo.processState
  • Эта реализация не отображает активность переключателя приложений.

Пример (на основе Код KNaito)

public class CurrentApplicationPackageRetriever {

    private final Context context;

    public CurrentApplicationPackageRetriever(Context context) {
        this.context = context;
    }

    public String[] get() {
        if (Build.VERSION.SDK_INT < 21)
            return getPreLollipop();
        else
            return getLollipop();
    }

    private String[] getPreLollipop() {
        @SuppressWarnings("deprecation")
        List<ActivityManager.RunningTaskInfo> tasks =
            activityManager().getRunningTasks(1);
        ActivityManager.RunningTaskInfo currentTask = tasks.get(0);
        ComponentName currentActivity = currentTask.topActivity;
        return new String[] { currentActivity.getPackageName() };
    }

    private String[] getLollipop() {
        final int PROCESS_STATE_TOP = 2;

        try {
            Field processStateField = ActivityManager.RunningAppProcessInfo.class.getDeclaredField("processState");

            List<ActivityManager.RunningAppProcessInfo> processes =
                activityManager().getRunningAppProcesses();
            for (ActivityManager.RunningAppProcessInfo process : processes) {
                if (
                    // Filters out most non-activity processes
                    process.importance <= ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND
                    &&
                    // Filters out processes that are just being
                    // _used_ by the process with the activity
                    process.importanceReasonCode == 0
                ) {
                    int state = processStateField.getInt(process);

                    if (state == PROCESS_STATE_TOP)
                        /*
                         If multiple candidate processes can get here,
                         it most likely that apps are being switched.
                         The first one provided by the OS seems to be
                         the one being switched to, so we stop here.
                         */
                        return process.pkgList; 
                }
            }
        } catch (NoSuchFieldException | IllegalAccessException e) {
            throw new RuntimeException(e);
        }

        return new String[] { };
    }

    private ActivityManager activityManager() {
        return (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
    }

}

манифеста

Добавьте GET_TASKS разрешение AndroidManifest.xml:

<!--suppress DeprecatedClassUsageInspection -->
<uses-permission android:name="android.permission.GET_TASKS" />

Ответ 5

Я использую это для своих тестов. Это API > 19, и только для действий вашего приложения.

@TargetApi(Build.VERSION_CODES.KITKAT)
public static Activity getRunningActivity() {
    try {
        Class activityThreadClass = Class.forName("android.app.ActivityThread");
        Object activityThread = activityThreadClass.getMethod("currentActivityThread")
                .invoke(null);
        Field activitiesField = activityThreadClass.getDeclaredField("mActivities");
        activitiesField.setAccessible(true);
        ArrayMap activities = (ArrayMap) activitiesField.get(activityThread);
        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);
                return (Activity) activityField.get(activityRecord);
            }
        }
    } catch (Exception e) {
        throw new RuntimeException(e);
    }

    throw new RuntimeException("Didn't find the running activity");
}

Ответ 6

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

public interface ContextProvider {
    Context getActivityContext();
}

public class MyApplication extends Application implements ContextProvider {
    private Activity currentActivity;

    @Override
    public Context getActivityContext() {
         return currentActivity;
    }

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

        registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
            @Override
            public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
                MyApplication.this.currentActivity = activity;
            }

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

            @Override
            public void onActivityResumed(Activity activity) {
                MyApplication.this.currentActivity = activity;
            }

            @Override
            public void onActivityPaused(Activity activity) {
                MyApplication.this.currentActivity = null;
            }

            @Override
            public void onActivityStopped(Activity activity) {
                // don't clear current activity because activity may get stopped after
                // the new activity is resumed
            }

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

            }

            @Override
            public void onActivityDestroyed(Activity activity) {
                // don't clear current activity because activity may get destroyed after
                // the new activity is resumed
            }
        });
    }
}

Затем настройте контейнер DI для возврата экземпляра MyApplication для ContextProvider, например.

public class ApplicationModule extends AbstractModule {    
    @Provides
    ContextProvider provideMainActivity() {
        return MyApplication.getCurrent();
    }
}

(Обратите внимание, что реализация getCurrent() опускается из приведенного выше кода. Это просто статическая переменная, заданная из конструктора приложения)

Ответ 7

Это можно сделать:

  • Внедрите свой собственный класс приложения, зарегистрируйтесь для ActivityLifecycleCallbacks - таким образом вы можете видеть, что происходит с нашим приложением. При каждом возобновлении обратный вызов назначает текущую видимую активность на экране, а при паузе он удаляет задание. Он использует метод registerActivityLifecycleCallbacks(), который был добавлен в API 14.

    public class App extends Application {
    
    private Activity activeActivity;
    
    @Override
    public void onCreate() {
        super.onCreate();
        setupActivityListener();
    }
    
    private void setupActivityListener() {
    registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
            @Override
            public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
            }
            @Override
            public void onActivityStarted(Activity activity) {
            }
            @Override
            public void onActivityResumed(Activity activity) {
                activeActivity = activity;
            }
            @Override
            public void onActivityPaused(Activity activity) {
                activeActivity = null;
            }
            @Override
            public void onActivityStopped(Activity activity) {
            }
            @Override
            public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
            }
            @Override
            public void onActivityDestroyed(Activity activity) {
            }
        });
    }
    
    public Activity getActiveActivity(){
        return activeActivity;
    }
    
    }
    
  • В своем сервисе звоните getApplication() и отсылайте его к имени своего приложения (приложение в этом случае). Чем вы можете позвонить app.getActiveActivity() - это даст вам текущую видимую активность (или null, если активность не видна). Вы можете получить имя Activity, вызвав activeActivity.getClass().getSimpleName()

Ответ 8

Вот мой ответ, который отлично работает...

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

Вот путь. Когда вы начинаете свою деятельность, вы сохраняете эту активность по Application.setCurrentActivity(getIntent()); Это приложение сохранит его. В вашем классе обслуживания вы можете просто сделать Intent currentIntent = Application.getCurrentActivity();  . GetApplication() startActivity (currentIntent);

Ответ 9

Используйте этот код для API 21 или выше. Это работает и дает лучший результат по сравнению с другими ответами, он отлично определяет процесс переднего плана.

if (Build.VERSION.SDK_INT >= 21) {
    String currentApp = null;
    UsageStatsManager usm = (UsageStatsManager) this.getSystemService(Context.USAGE_STATS_SERVICE);
    long time = System.currentTimeMillis();
    List<UsageStats> applist = usm.queryUsageStats(UsageStatsManager.INTERVAL_DAILY, time - 1000 * 1000, time);
    if (applist != null && applist.size() > 0) {
        SortedMap<Long, UsageStats> mySortedMap = new TreeMap<Long, UsageStats>();
        for (UsageStats usageStats : applist) {
            mySortedMap.put(usageStats.getLastTimeUsed(), usageStats);

        }
        if (mySortedMap != null && !mySortedMap.isEmpty()) {
            currentApp = mySortedMap.get(mySortedMap.lastKey()).getPackageName();
        }
    }

Ответ 10

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