¿Cómo startForeground () sin mostrar notificación?

87

Quiero crear un servicio y hacerlo funcionar en primer plano.

La mayoría de los códigos de ejemplo tienen notificaciones. Pero no quiero mostrar ninguna notificación. ¿Es eso posible?

¿Me puede dar algunos ejemplos? ¿Existen alternativas?

Mi servicio de aplicaciones está haciendo mediaplayer. Cómo hacer que el sistema no elimine mi servicio, excepto que la aplicación lo elimine por sí misma (como pausar o detener la música con un botón).

Muhammad Resna Rizki Pratama
fuente
11
No puede tener un servicio "Primer plano" sin una notificación. Período.
Jug6ernaut
1
¿Qué tal dar una notificación "falsa"? eso es un truco?
Muhammad Resna Rizki Pratama
Una vez que se configura la notificación de primer plano, puede eliminar "las notificaciones en ejecución en segundo plano" con la aplicación Rubber .
Laurent
1
@MuhammadResnaRizkiPratama, ¿consideraría cambiar la respuesta aceptada? Esta pregunta es extremadamente popular, pero la respuesta aceptada está completamente desactualizada, es demasiado torpe y hace suposiciones por qué el desarrollador necesita esto. Sugiero esta respuesta porque es confiable, no aprovecha los errores del sistema operativo y funciona en la mayoría de los dispositivos Android.
Sam

Respuestas:

95

Como característica de seguridad de la plataforma Android, no puede , bajo ninguna circunstancia, tener un servicio en primer plano sin tener también una notificación. Esto se debe a que un servicio en primer plano consume una mayor cantidad de recursos y está sujeto a diferentes restricciones de programación (es decir, no se elimina tan rápido) que los servicios en segundo plano, y el usuario debe saber qué es lo que posiblemente esté consumiendo su batería. Entonces, no hagas esto.

De todos modos, eso es posible tener una notificación "falsa", es decir, puede crear un icono de notificación transparente (iirc). Esto es extremadamente falso para sus usuarios y no tiene ninguna razón para hacerlo, aparte de matar su batería y, por lo tanto, crear malware.

Kristopher Micinski
fuente
3
Gracias. Eso me aclara por qué setForeground necesita la notificación.
Muhammad Resna Rizki Pratama
21
Bueno, parece que tienes una razón para hacerlo, los usuarios lo han pedido. Aunque puede ser falso como usted dice, los grupos de prueba lo solicitaron para un producto en el que estoy trabajando. Quizás Android debería tener una opción para ocultar notificaciones de algunos servicios que sabe que siempre se están ejecutando. De lo contrario, su barra de notificaciones se verá como un árbol de Navidad.
Radu
3
@Radu en realidad no debería tener tantas aplicaciones en primer plano: eso acabará con la duración de la batería, porque están programadas de manera diferente (esencialmente, no mueren). Esto podría ser aceptable para un mod, pero no veo que esa opción se convierta en un Android básico en el corto plazo. Los "usuarios" normalmente no saben qué es lo mejor para ellos ..
Kristopher Micinski
2
@Radu eso no respalda tu argumento. Las "aplicaciones galardonadas" suelen ser aquellas que realmente "arreglan" los agujeros de Android a través de inconsistencias del sistema (como asesinos de tareas). No hay ninguna razón por la que deba utilizar un servicio de primer plano solo porque las "aplicaciones premiadas" lo hacen: si lo hace, está admitiendo que está agotando la batería del usuario.
Kristopher Micinski
2
Aparentemente, este método ya no funcionará a partir de 4.3. plus.google.com/u/0/105051985738280261832/posts/MTinJWdNL8t
erbi
79

Actualización: esto se "solucionó" en Android 7.1. https://code.google.com/p/android/issues/detail?id=213309

Desde la actualización 4.3, es básicamente imposible iniciar un servicio startForeground()sin mostrar una notificación.

Sin embargo, puede ocultar el icono utilizando las API oficiales ... no es necesario un icono transparente: (Úselo NotificationCompatpara admitir versiones anteriores)

NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
builder.setPriority(Notification.PRIORITY_MIN);

He hecho las paces con el hecho de que la notificación en sí todavía debe estar allí, pero para quien quiera ocultarla, es posible que también haya encontrado una solución para eso:

  1. Inicie un servicio falso startForeground()con la notificación y todo.
  2. Inicie el servicio real que desea ejecutar, también con startForeground()(mismo ID de notificación)
  3. Detenga el primer servicio (falso) (puede llamar stopSelf()y en onDestroy llamarstopForeground(true) ).

¡Voilà! Sin notificación en absoluto y su segundo servicio sigue funcionando.

Lior Iluz
fuente
23
Gracias por esto. Creo que algunas personas no se dan cuenta de que existe un desarrollo de Android que se realiza para uso comercial interno (es decir, no está destinado a aparecer en el mercado ni a venderse a Joe Blow). A veces, la gente pide cosas como "dejar de mostrar la notificación de servicio" y es bueno ver soluciones alternativas incluso si no es kosher para el consumo general.
JamieB
5
@JamieB No podría estar más de acuerdo ... Le sugerí a Dianne Hackborn que necesitaran repensar todo el concepto de servicios en segundo plano y tal vez agregar un permiso para ejecutar un servicio en segundo plano sin las notificaciones. No es que nos importe a los desarrolladores si hay una notificación o no, ¡es la solicitud del USUARIO para eliminarla! :) Por cierto, ¿puedes verificar que también funcione para ti?
Lior Iluz
1
@JamieB user875707 dice que establezca el parámetro del icono (la identificación del recurso del icono) en 0, no la identificación de la notificación (si es así, como dice la documentación, "startForeground" no funcionará).
wangqi060934
9
Por supuesto, debe asumir que esto no funcionará en futuras versiones de la plataforma.
hackbod
1
@JamieB De hecho, probé esto ahora mismo en Android 5.0.1 y todavía me funciona.
Lior Iluz
20

Esto ya no funciona a partir de Android 7.1 y puede violar las políticas para desarrolladores de Google Play .

En su lugar, haga que el usuario bloquee la notificación de servicio .


Aquí está mi implementación de la técnica en la respuesta de Lior Iluz .

Código

ForegroundService.java

public class ForegroundService extends Service {

    static ForegroundService instance;

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

        instance = this;

        if (startService(new Intent(this, ForegroundEnablingService.class)) == null)
            throw new RuntimeException("Couldn't find " + ForegroundEnablingService.class.getSimpleName());
    }

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

        instance = null;
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

}

ForegroundEnablingService.java

public class ForegroundEnablingService extends Service {

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if (ForegroundService.instance == null)
            throw new RuntimeException(ForegroundService.class.getSimpleName() + " not running");

        //Set both services to foreground using the same notification id, resulting in just one notification
        startForeground(ForegroundService.instance);
        startForeground(this);

        //Cancel this service's notification, resulting in zero notifications
        stopForeground(true);

        //Stop this service so we don't waste RAM.
        //Must only be called *after* doing the work or the notification won't be hidden.
        stopSelf();

        return START_NOT_STICKY;
    }

    private static final int NOTIFICATION_ID = 10;

    private static void startForeground(Service service) {
        Notification notification = new Notification.Builder(service).getNotification();
        service.startForeground(NOTIFICATION_ID, notification);
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

}

AndroidManifest.xml

<service android:name=".ForegroundEnablingService" />
<service android:name=".ForegroundService" />

Compatibilidad

Probado y trabajando en:

  • Emulador oficial
    • 4.0.2
    • 4.1.2
    • 4.2.2
    • 4.3.1
    • 4.4.2
    • 5.0.2
    • 5.1.1
    • 6.0
    • 7.0
  • Sony Xperia M
    • 4.1.2
    • 4.3
  • Samsung galaxia ?
    • 4.4.2
    • 5.X
  • Genymotion
    • 5,0
    • 6.0
  • CyanogenMod
    • 5.1.1

Ya no funciona a partir de Android 7.1.

Sam
fuente
2
Aún no lo he probado, ¡pero sería bueno tener al menos un permiso para eso! +1 para la parte "A Google", mis usuarios se quejan de la notificación requerida para mantener la aplicación ejecutándose en segundo plano, también me quejo de la notificación requerida para skype / llama y cualquier otra aplicación que exista
Rami Dabain
disculpe cómo se puede utilizar este ejemplo para escuchar alarmas. Estoy desarrollando una aplicación que configura alarmas con alarmManager y el problema que tengo es que cuando cierro la aplicación (Aplicaciones recientes-> Borrar) todas las alarmas se eliminan también y estoy tratando de encontrar una manera de evitar esto
Ares91
@ Ares91, intente hacer una pregunta StackOverflow separada para eso, y asegúrese de incluir tanta información como sea posible, incluido el código relevante. No sé mucho sobre alarmas y esta pregunta es solo sobre servicios.
Sam
@Sam qué servicio se debe llamar primero, ForegroundEnablingService o Forground
Android Man
@AndroidManForegroundService
Sam
14

Puede usar esto (como lo sugiere @Kristopher Micinski):

Notification note = new Notification( 0, null, System.currentTimeMillis() );
note.flags |= Notification.FLAG_NO_CLEAR;
startForeground( 42, note );

ACTUALIZAR:

Tenga en cuenta que esto ya no está permitido con las versiones de Android KitKat +. Y tenga en cuenta que esto viola más o menos el principio de diseño en Android que hace que las operaciones en segundo plano sean visibles para los usuarios, como lo menciona @Kristopher Micinski.

Snicolas
fuente
2
Tenga en cuenta que esto ya no parece ser aceptado con el SDK 17. El servicio no pasará a primer plano si el elemento extraíble pasado a la notificación es 0.
Snicolas
Fue un error y, con suerte, se solucionó. La idea de los servicios de primer plano es que son menos propensos a ser eliminados, y esta es una forma de garantizar que el usuario sea consciente de su existencia.
Martin Marconcini
4
Con Android 18, la notificación ya no es invisible, pero muestra que dice "La aplicación se está ejecutando, toque para obtener más información o para detener la aplicación"
Emanuel Moecklin
1
Captura de pantalla de uno de mis clientes: 1gravity.com/k10/Screenshot_2013-07-25-12-35-49.jpg . También tengo un dispositivo 4.3 y puedo confirmar ese hallazgo.
Emanuel Moecklin
2
Advertencia: estoy recibiendo informes de fallas de Sony Ericsson LT26i (Xperia S) con Android 4.1.2 (SDK 16): android.app.RemoteServiceException: Notificación incorrecta publicada desde el paquete ...: No se pudo crear el icono: StatusBarIcon (pkg = ... id = 0x7f020052 level = 0 visible = true num = 0) También he configurado el id del icono en 0 hasta el SDK 17. Desde SDK 18 lo he configurado como un recurso de diseño válido. ¡Quizás necesite una identificación de icono válida del SDK 16!
almisoft
13

Advertencia : aunque esta respuesta parece funcionar, de hecho evita silenciosamente que su servicio se convierta en un servicio en primer plano .

Respuesta original:


Simplemente establezca el ID de su notificación en cero:

// field for notification ID
private static final int NOTIF_ID = 0;

    ...
    startForeground(NOTIF_ID, mBuilder.build());
    NotificationManager mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
    mNotificationManager.cancel(NOTIF_ID);
    ...

Un beneficio que puede obtener es un Service que podrá ejecutarse en alta prioridad sin ser destruido por el sistema Android, a menos que haya mucha presión de memoria.

EDITAR

Para que funcione con Pre-Honeycomb y Android 4.4 y superior, asegúrese de utilizar el NotificationCompat.Builderque proporciona Support Library v7, en lugar de Notification.Builder.

Anggrayudi H
fuente
1
Esto funciona perfectamente para mí en 4.3 (18) @vlad sol. ¿Por qué todavía no hay otros votos positivos para esta respuesta, me pregunto?
Rob McFeely
Probé esto en Android N Preview 4 y no funcionó. Lo intenté Notification.Builder, Support Library v4 NotificationCompat.Buildery Support Library v7 NotificationCompat.Builder. La notificación no apareció en el cajón de notificaciones o en la barra de estado, pero el servicio no se estaba ejecutando en modo de primer plano cuando lo verifiqué usando getRunningServices()y dumpsys.
Sam
@Sam, entonces este error no aparece en la versión anterior, ¿correcto?
Anggrayudi H
@AnggrayudiH, por "este error", ¿te refieres a que la notificación no aparece? ¿O quiere decir que el servicio no pasa al modo de primer plano?
Sam
1
Esto no funciona para mi. El proceso se inicia como primer plano cuando se envía id! = 0, pero NO se inicia como primer plano cuando id = 0. Use adb shell dumpsys activity servicespara verificar.
beetree
8

Puede ocultar la notificación en Android 9+ usando un diseño personalizado con layout_height = "0dp"

NotificationCompat.Builder builder = new NotificationCompat.Builder(context, NotificationUtils.CHANNEL_ID);
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.custom_notif);
builder.setContent(remoteViews);
builder.setPriority(NotificationCompat.PRIORITY_LOW);
builder.setVisibility(Notification.VISIBILITY_SECRET);

custom_notif.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="0dp">
</LinearLayout>

Probado en Pixel 1, Android 9. Esta solución no funciona en Android 8 o menos

phnmnn
fuente
Buena solución. También creo que requiere un icono en Android 10. Usar una imagen de marcador de posición transparente para el icono resuelve el problema.
shyos
7

Actualización: esto ya no funciona en Android 4.3 y superior


Hay una solución. Intente crear una notificación sin configurar el icono y la notificación no se mostrará. No sé cómo funciona, pero funciona :)

    Notification notification = new NotificationCompat.Builder(this)
            .setContentTitle("Title")
            .setTicker("Title")
            .setContentText("App running")
            //.setSmallIcon(R.drawable.picture)
            .build();
    startForeground(101,  notification);
Vladimir Petrovski
fuente
3
En Android N Preview 4, esto no funciona. Android muestra una (YourServiceName) is runningnotificación en lugar de la suya propia cuando no especifica un icono. Según CommonsWare, este ha sido el caso desde Android 4.3: commonsware.com/blog/2013/07/30/…
Sam
1
Hice algunas pruebas hoy y confirmé que esto no funciona en Android 4.3 y superior.
Sam
5

Actualización: esto ya no funciona en Android 4.3 y superior


Establecí el parámetro de icono en el constructor para Notificación en cero, y luego pasé la notificación resultante a startForeground (). No hay errores en el registro y no aparece ninguna notificación. Sin embargo, no sé si el servicio se puso en primer plano correctamente. ¿Hay alguna forma de comprobarlo?

Editado: verificado con dumpsys, y de hecho el servicio está en primer plano en mi sistema 2.3. Aún no he comprobado con otras versiones del sistema operativo.

Alexander Pruss
fuente
9
Utilice el comando "adb shell dumpsys activity services" para comprobar si "isForeground" está establecido en true.
negro
1
O simplemente llame al Notification()constructor vacío .
johsin18
Esto funcionó para mí. La primera vez que logré que mi servicio se ejecute durante la noche, por lo que definitivamente está en primer plano (además, dumpsys dice que lo está) y no hay notificación.
JamieB
Tenga en cuenta que esto ya no funciona en Android 4.3 (API 18). El sistema operativo coloca una notificación de marcador de posición en el cajón de notificaciones.
Sam
4

Bloquear la notificación del servicio en primer plano

La mayoría de las respuestas aquí no funcionan, interrumpen el servicio de primer plano o violan las políticas de Google Play .

La única forma de ocultar la notificación de manera confiable y segura es hacer que el usuario la bloquee.

Android 4.1 - 7.1

La única forma es bloquear todas las notificaciones de su aplicación:

  1. Envíe al usuario a la pantalla de detalles de la aplicación:

    Uri uri = Uri.fromParts("package", getPackageName(), null);
    Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).setData(uri);
    startActivity(intent);
    
  2. Hacer que el usuario bloquee las notificaciones de la aplicación

Tenga en cuenta que esto también bloquea los brindis de su aplicación.

Android 8.0 - 8.1

No vale la pena bloquear la notificación en Android O porque el sistema operativo simplemente la reemplazará con una " ejecución en segundo plano " o " usando batería notificación de ".

Android 9+

Utilice un canal de notificación para bloquear la notificación de servicio sin afectar sus otras notificaciones.

  1. Asignar notificación de servicio al canal de notificación
  2. Enviar al usuario a la configuración del canal de notificación

    Intent intent = new Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS)
        .putExtra(Settings.EXTRA_APP_PACKAGE, getPackageName())
        .putExtra(Settings.EXTRA_CHANNEL_ID, myNotificationChannel.getId());
    startActivity(intent);
    
  3. Hacer que el usuario bloquee las notificaciones del canal.

Sam
fuente
2

versión 4.3 (18) y superior, no es posible ocultar la notificación del servicio, pero puede deshabilitar el icono, la versión 4.3 (18) y las siguientes pueden ocultar la notificación

Notification noti = new Notification();
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN) {
    noti.priority = Notification.PRIORITY_MIN;
}
startForeground(R.string.app_name, noti);
vlad sol
fuente
Si bien esta respuesta probablemente sea correcta y útil, se prefiere si incluye alguna explicación junto con ella para explicar cómo ayuda a resolver el problema. Esto se vuelve especialmente útil en el futuro, si hay un cambio (posiblemente no relacionado) que haga que deje de funcionar y los usuarios deben comprender cómo funcionaba antes.
Kevin Brown
Creo que Android 4.3 es en realidad API 18. Además, este método solo oculta el icono de la barra de estado; el icono todavía existe en la notificación.
Sam
para ocultar el icono de la notificación, puede agregar una imagen vacía mBuilder.setSmallIcon (R.drawable.blank);
vlad sol
1
Estoy mirando la fuente de kitkat y el método de identificación cero no parece que pueda funcionar. si el ID es cero, el registro de servicio se actualiza a NO primer plano.
Jeffrey Blattman
2

Descubrí que en Android 8.0 todavía es posible sin usar un canal de notificación.

public class BootCompletedIntentReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        if ("android.intent.action.BOOT_COMPLETED".equals(intent.getAction())) {

            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {

                Intent notificationIntent = new Intent(context, BluetoothService.class);    
                context.startForegroundService(notificationIntent);

            } else {
                //...
            }

        }
    }
}

Y en BluetoothService.class:

 @Override
    public void onCreate(){    
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {

            Intent notificationIntent = new Intent(this, BluetoothService.class);

            PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);

            Notification notification = new Notification.Builder(this)
                    .setContentTitle("Title")
                    .setContentText("App is running")
                    .setSmallIcon(R.drawable.notif)
                    .setContentIntent(pendingIntent)
                    .setTicker("Title")
                    .setPriority(Notification.PRIORITY_DEFAULT)
                    .build();

            startForeground(15, notification);

        }

    }

No se muestra una notificación persistente, sin embargo, verá la notificación de Android 'x aplicaciones se están ejecutando en segundo plano'.

Pete
fuente
En Android 8.1: "Notificación incorrecta para startForeground: java.lang.RuntimeException: canal no válido para notificación de servicio"
T-igra
Personalmente, creo que la apps are running in the backgroundnotificación es incluso peor que una notificación específica de la aplicación, por lo que no estoy seguro de que este enfoque valga la pena.
Sam
-2

Actualización: esto ya no funciona en Android 7.1 y superior


Aquí hay una manera de hacer que el oom_adj de su aplicación sea 1 (probado en el emulador SDK de ANDROID 6.0).Agregue un servicio temporal, en su llamada de servicio principal startForgroundService(NOTIFICATION_ID, notificion). Y luego inicie la llamada de servicio temporal startForgroundService(NOTIFICATION_ID, notificion)con la misma identificación de notificación nuevamente, después de un tiempo en la llamada de servicio temporal stopForgroundService (verdadero) para descartar la ontificación en curso.

Tshunglee
fuente
Esta es la única solución de trabajo que conozco. Sin embargo, ya está cubierto por otra respuesta .
Sam
-3

También puede declarar su aplicación como persistente.

<application
    android:icon="@drawable/icon"
    android:label="@string/app_name"
    android:theme="@style/Theme"
    *android:persistent="true"* >
</application>

Básicamente, esto establece su aplicación en una prioridad de memoria más alta, disminuyendo la probabilidad de que sea eliminada.

Habitante
fuente
5
Esto es incorrecto, solo las aplicaciones del sistema pueden ser persistentes: groups.google.com/forum/?fromgroups=#!topic/android-developers/…
Snicolas
-6

Desarrollé un reproductor multimedia simple hace un par de meses. Entonces, lo que creo es si estás haciendo algo como:

Intent i = new Intent(this, someServiceclass.class);

startService (i);

Entonces, el sistema no debería poder matar su servicio.

referencia : lea el párrafo que discute cuándo el sistema detiene el servicio

redM0nk
fuente
Leí en algunos artículos. Si solo hace startService (), entonces el sistema necesita más memoria. Entonces el sistema matará ese servicio. ¿eso es verdad? Simplemente no tengo tiempo para hacer experimentos en el servicio.
Muhammad Resna Rizki Pratama
1
Muhammad, eso es cierto - el propósito de startForeground () es efectivamente darle al servicio una "prioridad de memoria" más alta que los servicios en segundo plano para que pueda sobrevivir más tiempo. StartForeground () debería usar una notificación para que el usuario esté más consciente de un servicio más persistente / más difícil de eliminar que se está ejecutando.
DustinB
Esto simplemente no es cierto; es prácticamente imposible crear un servicio que no se pueda eliminar en Android. Android elimina fácilmente los servicios que no son de primer plano para liberar memoria.
Sam