Quero criar um serviço e executá-lo em primeiro plano.
A maioria dos códigos de exemplo contém notificações. Mas não quero mostrar nenhuma notificação. Isso é possível?
Você pode me dar alguns exemplos? Existem alternativas?
Meu serviço de aplicativo está fazendo mediaplayer. Como fazer com que o sistema não mate meu serviço, exceto que o próprio aplicativo o mate (como pausar ou parar a música por botão).
android
android-service
foreground
Muhammad Resna Rizki Pratama
fonte
fonte
Respostas:
Como recurso de segurança da plataforma Android, você não pode , sob nenhuma circunstância, ter um serviço em primeiro plano sem também ter uma notificação. Isso ocorre porque um serviço em primeiro plano consome uma quantidade maior de recursos e está sujeito a diferentes restrições de agendamento (ou seja, não é eliminado tão rapidamente) do que os serviços em segundo plano, e o usuário precisa saber o que está possivelmente consumindo sua bateria. Então, não faça isso.
Porém, é possível ter uma notificação "falsa", ou seja, pode-se fazer um ícone de notificação transparente (iirc). Isso é extremamente falso para seus usuários e você não tem motivo para fazer isso, a não ser acabar com a bateria e, assim, criar malware.
fonte
Atualização: Isso foi "consertado" no Android 7.1. https://code.google.com/p/android/issues/detail?id=213309
Desde a atualização 4.3, é basicamente impossível iniciar um serviço
startForeground()
sem mostrar uma notificação.Você pode, no entanto, ocultar o ícone usando APIs oficiais ... sem necessidade de um ícone transparente: (Use
NotificationCompat
para oferecer suporte a versões mais antigas)NotificationCompat.Builder builder = new NotificationCompat.Builder(context); builder.setPriority(Notification.PRIORITY_MIN);
Aceitei o fato de que a notificação em si ainda precisa estar lá, mas para quem ainda quiser escondê-la, posso ter encontrado uma solução alternativa para isso também:
startForeground()
com a notificação e tudo mais.startForeground()
(mesmo ID de notificação)stopSelf()
e ligar para onDestroystopForeground(true)
).Voilà! Nenhuma notificação e seu segundo serviço continua em execução.
fonte
Isso não funciona mais a partir do Android 7.1 e pode violar as políticas de desenvolvedor do Google Play .
Em vez disso, faça com que o usuário bloqueie a notificação de serviço .
Aqui está minha implementação da técnica na resposta 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" />
Compatibilidade
Testado e trabalhando em:
Não funciona mais a partir do Android 7.1.
fonte
ForegroundService
Você pode usar isso (conforme sugerido por @Kristopher Micinski):
Notification note = new Notification( 0, null, System.currentTimeMillis() ); note.flags |= Notification.FLAG_NO_CLEAR; startForeground( 42, note );
ATUALIZAR:
Observe que isso não é mais permitido com versões do Android KitKat +. E tenha em mente que isso viola mais ou menos o princípio de design do Android que torna as operações em segundo plano visíveis aos usuários, conforme mencionado por @Kristopher Micinski
fonte
Aviso : embora esta resposta pareça funcionar, ela na verdade impede silenciosamente que seu serviço se torne um serviço em primeiro plano .
Resposta original:
Basta definir o ID da sua notificação para zero:
// 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); ...
Um benefício que você pode obter é
Service
que será capaz de rodar em alta prioridade sem ser destruído pelo sistema Android, a menos que em alta pressão de memória.EDITAR
Para fazê-lo funcionar com Pre-Honeycomb e Android 4.4 e superior, certifique-se de usar o
NotificationCompat.Builder
fornecido pela Biblioteca de Suporte v7, em vez deNotification.Builder
.fonte
Notification.Builder
, Support Library v4NotificationCompat.Builder
e Support Library v7NotificationCompat.Builder
. A notificação não apareceu na gaveta de notificação ou na barra de status, mas o serviço não estava sendo executado no modo de primeiro plano quando o verifiquei usandogetRunningServices()
edumpsys
.adb shell dumpsys activity services
para verificar.Você pode ocultar a notificação no Android 9+ usando o layout personalizado com 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>
Testado no Pixel 1, android 9. Esta solução não funciona no Android 8 ou menos
fonte
Atualização: isso não funciona mais no Android 4.3 e superior
Existe uma solução alternativa. Tente criar uma notificação sem definir o ícone, e a notificação não será exibida. Não sei como funciona, mas funciona :)
Notification notification = new NotificationCompat.Builder(this) .setContentTitle("Title") .setTicker("Title") .setContentText("App running") //.setSmallIcon(R.drawable.picture) .build(); startForeground(101, notification);
fonte
(YourServiceName) is running
notificação em vez da sua própria quando você não especifica um ícone. De acordo com CommonsWare, esse tem sido o caso desde o Android 4.3: commonsware.com/blog/2013/07/30/…Atualização: isso não funciona mais no Android 4.3 e superior
Eu defini o parâmetro icon para o construtor de Notificação como zero e, em seguida, passei a notificação resultante para startForeground (). Nenhum erro no log e nenhuma notificação é exibida. Não sei, porém, se o serviço foi colocado em primeiro plano com êxito - há alguma maneira de verificar?
Editado: verificado com dumpsys e, de fato, o serviço está em primeiro plano no meu sistema 2.3. Ainda não verifiquei com outras versões do sistema operacional.
fonte
Notification()
construtor vazio .Bloquear a notificação de serviço em primeiro plano
A maioria das respostas aqui não funciona, quebra o serviço de primeiro plano ou viola as políticas do Google Play .
A única maneira de ocultar a notificação de maneira confiável e segura é fazer com que o usuário a bloqueie.
Android 4.1 - 7.1
A única maneira é bloquear todas as notificações do seu aplicativo:
Envie o usuário para a tela de detalhes do aplicativo:
Uri uri = Uri.fromParts("package", getPackageName(), null); Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).setData(uri); startActivity(intent);
Faça com que o usuário bloqueie as notificações do aplicativo
Observe que isso também bloqueia os toasts do seu aplicativo.
Android 8.0 - 8.1
Não vale a pena bloquear a notificação no Android O porque o SO irá apenas substituí-la por uma notificação " executando em segundo plano " ou " usando bateria ".
Android 9+
Use um canal de notificação para bloquear a notificação de serviço sem afetar suas outras notificações.
Enviar usuário para as configurações do canal de notificação
Intent intent = new Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS) .putExtra(Settings.EXTRA_APP_PACKAGE, getPackageName()) .putExtra(Settings.EXTRA_CHANNEL_ID, myNotificationChannel.getId()); startActivity(intent);
Faça com que o usuário bloqueie as notificações do canal
fonte
versão 4.3 (18) e superior ocultar notificação de serviço não é possível, mas você pode desativar o ícone, versão 4.3 (18) e abaixo é possível ocultar a notificação
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);
fonte
Descobri que no Android 8.0 ainda é possível não usar um canal de notificação.
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 { //... } } } }
E em 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); } }
Uma notificação persistente não é mostrada, no entanto, você verá que a notificação do Android 'x apps estão sendo executados em segundo plano'.
fonte
apps are running in the background
notificação é ainda pior do que uma notificação específica do aplicativo, então não tenho certeza se essa abordagem vale a pena.Atualização: isso não funciona mais no Android 7.1 e superior
Aqui está uma maneira de tornar o oom_adj do seu aplicativo em 1 (testado no emulador ANDROID 6.0 SDK).Adicionar um serviço temporário, em sua chamada de serviço principal
startForgroundService(NOTIFICATION_ID, notificion)
. E, em seguida, inicie a chamada de serviço temporáriastartForgroundService(NOTIFICATION_ID, notificion)
com o mesmo id de notificação novamente, após um tempo na chamada de serviço temporária stopForgroundService (true) para descartar a ontificação inicial.fonte
Você também pode declarar seu aplicativo como persistente.
<application android:icon="@drawable/icon" android:label="@string/app_name" android:theme="@style/Theme" *android:persistent="true"* > </application>
Isso basicamente define seu aplicativo com uma prioridade de memória mais alta, diminuindo a probabilidade de ele ser morto.
fonte
Desenvolvi um reprodutor de mídia simples alguns meses atrás. Então, o que eu acredito é se você estiver fazendo algo como:
Intent i = new Intent(this, someServiceclass.class);
startService (i);
Então, o sistema não deve ser capaz de encerrar seu serviço.
referência : leia o parágrafo que discute quando o sistema interrompe o serviço
fonte