Join FlipAndroid.COM Telegram Group: https://t.me/joinchat/F_aqThGkhwcLzmI49vKAiw


Android: ¿Cómo puedo obtener la actividad de primer plano actual (de un servicio)?

¿Existe un modo androide nativo para obtener una referencia a la actividad actualmente en ejecución de 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). ¿Hay una manera fácil de hacer eso (como el que sugerí arriba)?

  • Omitir correctamente la actividad de inicio de sesión si ya ha iniciado sesión
  • Tratando de UNINSTALL_SHORTCUT pero el acceso directo no desaparecerá
  • GetActivity () devuelve null en la función Fragmento
  • Android - No se puede resolver el símbolo "R"
  • Cómo detectar si la actividad principal de la vista se está destruyendo
  • System.exit (0) no cierra todas mis actividades?
  • Cómo iniciar la actividad desde el widget de pantalla de inicio de Android
  • ¿Debo usar "_activity = this;"?
  • 10 Solutions collect form web for “Android: ¿Cómo puedo obtener la actividad de primer plano actual (de un servicio)?”

    ¿Existe un modo androide nativo para obtener una referencia a la actividad actualmente en ejecución de un servicio?

    Es posible que no pertenezcas a la "Actividad en ejecución".

    Tengo un servicio ejecutándose en segundo plano, y me gustaría actualizar mi Actividad actual cuando ocurre un evento (en el servicio). ¿Hay una manera fácil de hacer eso (como el que sugerí arriba)?

    1. Enviar una difusión Intent de la actividad – aquí hay un ejemplo de proyecto que demuestra este patrón
    2. Haga que la actividad suministre un PendingIntent (por ejemplo, a través de createPendingResult() ) que el servicio invoca
    3. Haga que la actividad registre un objeto de devolución de llamada o de escucha con el servicio a través de bindService() , y bindService() que el servicio llame a un método de evento en ese objeto callback / listener
    4. Envíe una Intent difusión ordenada a la actividad, con un BroadcastReceiver baja prioridad como copia de seguridad (para levantar una Notification si la actividad no está en pantalla) – aquí hay una entrada de blog con más información sobre este patrón

    Esta es una buena manera de hacerlo utilizando el gestor de actividades. Básicamente, obtiene las pruebas de ejecución del gestor de actividades. Siempre devolverá primero la tarea activa. Desde allí se puede obtener la topActivity.

    Ejemplo aquí

    Hay una manera fácil de obtener una lista de tareas en ejecución desde el servicio de ActivityManager. Puede solicitar un número máximo de tareas que se ejecutan en el teléfono y, de forma predeterminada, se devuelve primero la tarea activa.

    Una vez que tenga que puede obtener un objeto ComponentName solicitando la 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"/> 

    Utilice un AccessibilityService

    • Puede detectar la ventana activa con un AccessibilityService .
    • En la onAccessibilityEvent llamada onAccessibilityEvent , compruebe si el tipo de evento TYPE_WINDOW_STATE_CHANGED determina cuándo cambia la ventana actual.
    • Compruebe si la ventana es una actividad llamando a PackageManager.getActivityInfo() .

    Beneficios

    • Probado y funcionando en Android 2.2 (API 8) a través de Android 7.1 (API 25).
    • No requiere sondeo.
    • No requiere el permiso GET_TASKS .

    Desventajas

    • Cada usuario debe habilitar el servicio en la configuración de accesibilidad de Android.
    • El servicio siempre se está ejecutando.
    • 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 ser confuso porque el usuario no puede saber por qué no puede presionar el botón o cómo trabajar alrededor de él.
    • El AccessibilityService no conocerá 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

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

    Habilitación del servicio

    Cada usuario de la aplicación tendrá que habilitar explícitamente AccessibilityService para que se pueda utilizar. Vea esta respuesta de StackOverflow para saber cómo hacer esto.

    Tenga en cuenta que el usuario no podrá presionar el botón OK cuando intente activar el servicio de accesibilidad si una aplicación ha colocado una superposición en la pantalla, como Velis Auto Brightness o Lux.

    Utilizar ActivityManager

    Si sólo desea conocer la aplicación que contiene la actividad actual, puede hacerlo utilizando ActivityManager . La técnica que puedes usar depende de la versión de Android:

    • Pre-Lollipop: ActivityManager.getRunningTasks ( ejemplo )
    • Lollipop: ActivityManager.getRunningAppProcesses ( ejemplo )

    Beneficios

    • Debe funcionar en todas las versiones de Android hasta la fecha.

    Desventajas

    • No funciona en Android M (basado en pruebas con el emulador de pre-lanzamiento)
    • La documentación de estas APIs dice que sólo se destinan a la depuración y las interfaces de usuario de administración.
    • Si desea actualizaciones en tiempo real, tiene que utilizar el sondeo.
    • Se basa en una API oculta: ActivityManager.RunningAppProcessInfo.processState
    • Esta implementación no capta la actividad del conmutador de aplicaciones.

    Ejemplo (basado en el código de 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's 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); } } 

    Manifiesto

    Agrega el permiso GET_TASKS a AndroidManifest.xml :

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

    Estoy usando esto para mis pruebas. Su API> 19, y sólo para las actividades de su aplicación, sin embargo.

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

    No pude encontrar una solución que nuestro equipo estaría contento con lo que rodó nuestro propio. Utilizamos ActivityLifecycleCallbacks para realizar un seguimiento de la actividad actual y luego exponerla a través de un servicio:

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

    A continuación, configure su contenedor DI para devolver la instancia de MyApplication para ContextProvider , por ejemplo

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

    (Tenga en cuenta que la implementación de getCurrent() se omite del código anterior.Sólo es una variable estática que se establece desde el constructor de la aplicación)

    Se puede hacer por:

    1. Implemente su propia clase de aplicación, regístrese en ActivityLifecycleCallbacks, de esta manera podrá ver lo que está pasando con nuestra aplicación. En cada reanudación la devolución de llamada asigna la actividad visible actual en la pantalla y en pausa se elimina la asignación. Utiliza el método registerActivityLifecycleCallbacks() que se agregó en la 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; } } 
    2. En su llamada de servicio getApplication() y getApplication() al nombre de su clase de aplicación (App en este caso). Que puede llamar app.getActiveActivity() – que le dará una actividad visible actual (o nulo cuando no hay actividad visible). Puede obtener el nombre de la actividad llamando a activeActivity.getClass().getSimpleName()

    Aquí está mi respuesta que funciona bien …

    Usted debe ser capaz de obtener la actividad actual de esta manera … Si la estructura de su aplicación con unas pocas actividades con muchos fragmentos y desea realizar un seguimiento de lo que es su actividad actual, que tendría un montón de trabajo sin embargo. Mi senario fue que tengo una Actividad con múltiples Fragmentos. Así que puedo realizar un seguimiento de la actividad actual a través de Application Object, que puede almacenar todo el estado actual de las variables globales.

    Aquí hay un camino. Cuando inicia su Actividad, almacena esa Actividad mediante Application.setCurrentActivity (getIntent ()); Esta aplicación lo almacenará. En su clase de servicio, simplemente puede hacer como Intent currentIntent = Application.getCurrentActivity (); GetApplication (). StartActivity (currentIntent);

    Utilice este código para la API 21 o superior. Esto funciona y da mejores resultados en comparación con las otras respuestas, detecta perfectamente el proceso de primer plano.

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

    No sé si es una respuesta estúpida, pero resolvió este problema almacenando un indicador en preferencias compartidas cada vez que entré onCreate () de cualquier actividad, después utilicé el valor de las preferencias shered para descubrir cuál es la actividad del primero plano.

    FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.