Android: ¿Cómo puedo obtener la actividad actual en primer plano (de un servicio)?
¿Existe una forma nativa de Android de obtener una referencia a la actividad que se está ejecutando actualmente desde un servicio?
Tengo un servicio ejecutándose en segundo plano y me gustaría actualizar mi Actividad actual cuando ocurre un evento (en el servicio). ¿Existe una manera fácil de hacerlo (como la que sugerí anteriormente)?
Actualización : esto ya no funciona con las actividades de otras aplicaciones a partir de Android 5.0
Aquí tienes una buena forma de hacerlo utilizando el administrador de actividades. Básicamente, obtienes las tareas en ejecución del administrador de actividades. Siempre devolverá primero la tarea actualmente activa. Desde allí puedes obtener la actividad superior.
Ejemplo aquí
Existe una forma sencilla de obtener una lista de tareas en ejecución desde el servicio ActivityManager. Puede solicitar una cantidad máxima de tareas que se ejecutan en el teléfono y, de forma predeterminada, la tarea actualmente activa se devuelve primero.
Una vez que lo tenga, puede obtener un objeto ComponentName solicitando topActivity de su lista.
He aquí un ejemplo.
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();
Necesitará el siguiente permiso en su manifiesto:
<uses-permission android:name="android.permission.GET_TASKS"/>
Advertencia: infracción de Google Play
Google ha amenazado con eliminar aplicaciones de Play Store si utilizan servicios de accesibilidad con fines no relacionados con la accesibilidad. Sin embargo, según se informa, esto se está reconsiderando .
Utilice unAccessibilityService
- Puede detectar la ventana actualmente activa utilizando un archivo
AccessibilityService
. - En la
onAccessibilityEvent
devolución de llamada, verifique el tipo de evento para determinar cuándo cambia la ventana actual.TYPE_WINDOW_STATE_CHANGED
- Compruebe si la ventana es una actividad llamando
PackageManager.getActivityInfo()
.
Beneficios
- Probado y funcionando en Android 2.2 (API 8) hasta Android 7.1 (API 25).
- No requiere encuestas.
- No requiere el
GET_TASKS
permiso.
Desventajas
- Cada usuario debe habilitar el servicio en la configuración de accesibilidad de Android.
- Esto no es 100% confiable. De vez en cuando los acontecimientos se suceden fuera de orden.
- El servicio siempre está funcionando.
- Cuando un usuario intenta habilitar
AccessibilityService
, no puede presionar el botón Aceptar si una aplicación ha colocado una superposición en la pantalla. Algunas aplicaciones que hacen esto son Velis Auto Brightness y Lux. Esto puede resultar confuso porque es posible que el usuario no sepa por qué no puede presionar el botón o cómo solucionarlo. - No
AccessibilityService
sabrán la actividad actual hasta el primer cambio de actividad.
Ejemplo
Servicio
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
Combina esto en tu manifiesto:
<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>
Información de servicio
Pon esto en 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"/>
Habilitando el servicio
Cada usuario de la aplicación deberá habilitar explícitamente AccessibilityService
para poder utilizarla. Consulte esta respuesta de StackOverflow para saber cómo hacer esto.
Tenga en cuenta que el usuario no podrá presionar el botón Aceptar cuando intente habilitar el servicio de accesibilidad si una aplicación ha colocado una superposición en la pantalla, como Velis Auto Brightness o Lux.