Android - implementando startForeground para um serviço?


124

Portanto, não tenho certeza de onde / como implementar esse método para executar meu serviço em primeiro plano. Atualmente, inicio o serviço da seguinte forma em outra atividade:

Intent i = new Intent(context, myService.class); 
context.startService(i);

E então no onCreate () de myServices, tento o startForeground () ...?

Notification notification = new Notification();
startForeground(1, notification);

Então, sim, estou um pouco perdido e inseguro de como implementar isso.


Bem, isso não funciona, pelo menos até onde posso dizer, meu serviço ainda funciona como serviço em segundo plano e é morto.
JDS

Respostas:


131

Eu começaria preenchendo completamente o arquivo Notification. Aqui está um projeto de amostra demonstrando o uso de startForeground().


8
É possível usar o startForeground () sem notificação? Ou podemos atualizar mais tarde a mesma notificação?
JRC

2
Existe algum motivo específico para você usar 1337?
Cody

33
@DoctorOreo: ele precisa ser único dentro do aplicativo, embora não seja necessariamente único no dispositivo. Eu escolhi 1337 porque, bem, é 1337 . :-)
CommonsWare 17/02

A pergunta do @JRC é boa. É possível usar o startForeground () sem notificação?
Snicolas

2
@Snicolas: Obrigado por apontar uma falha no Android. Vou trabalhar para corrigir isso.
CommonsWare

78

Na sua atividade principal, inicie o serviço com o seguinte código:

Intent i = new Intent(context, MyService.class); 
context.startService(i);

Em seu serviço, onCreate()você criaria sua notificação e a definiria como primeiro plano da seguinte maneira:

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

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

Notification notification = new NotificationCompat.Builder(this)
                .setSmallIcon(R.mipmap.app_icon)
                .setContentTitle("My Awesome App")
                .setContentText("Doing some work...")
                .setContentIntent(pendingIntent).build();

startForeground(1337, notification);

@ Mike como atualizar esta notificação da MainActivity?
precisa saber é o seguinte

1
@ Roon13 usando o ID, neste caso 1337 ... você deve ser capaz de construir uma nova notificação e chamar startForeground com o ID
mikebertiean

@ Roon13 confira esta questão stackoverflow.com/questions/5528288/...
mikebertiean

@mikebertiean Como posso chamar o startForeground de MainActivity? também como posso limpar a notificação da MainActvity quando o processo for concluído?
Roon13

@mikebertiean Eu tenho que chamar o startForeground novamente na classe Service, mas como? Preciso chamar startService () novamente?
precisa saber é o seguinte

30

Este é o meu código para definir o serviço em primeiro plano:

private void runAsForeground(){
    Intent notificationIntent = new Intent(this, RecorderMainActivity.class);
    PendingIntent pendingIntent=PendingIntent.getActivity(this, 0,
            notificationIntent, Intent.FLAG_ACTIVITY_NEW_TASK);

    Notification notification=new NotificationCompat.Builder(this)
                                .setSmallIcon(R.drawable.ic_launcher)
                                .setContentText(getString(R.string.isRecording))
                                .setContentIntent(pendingIntent).build();

    startForeground(NOTIFICATION_ID, notification);

}

Preciso criar uma notificação usando PendingIntent, para poder iniciar minha atividade principal a partir da notificação.

Para remover a notificação, basta chamar o stopForeground (true);

É chamado no onStartCommand (). Consulte o meu código em: https://github.com/bearstand/greyparrot/blob/master/src/com/xiong/richard/greyparrot/Mp3Recorder.java


Se você remover a notificação chamando stopForeground (true) você está cancelando o serviço startforeground
sdelvalle57

6
De onde você chama esse método?
Srujan Barai 23/11

7
Intent.FLAG_ACTIVITY_NEW_TASKnão é válido no contexto de PendingIntent.
mixel

30

Solução para Oreo 8.1

Encontrei alguns problemas, como RemoteServiceException, devido à identificação de canal inválida nas versões mais recentes do Android. Foi assim que eu resolvi:

Atividade :

override fun onCreate(savedInstanceState: Bundle?) {
    val intent = Intent(this, BackgroundService::class.java)

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        startForegroundService(intent)
    } else {
        startService(intent)
    }
}

BackgroundService:

override fun onCreate() {
    super.onCreate()
    startForeground()
}

private fun startForeground() {

    val service = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
    val channelId =
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                createNotificationChannel()
            } else {
                // If earlier version channel ID is not used
                // https://developer.android.com/reference/android/support/v4/app/NotificationCompat.Builder.html#NotificationCompat.Builder(android.content.Context)
                ""
            }

    val notificationBuilder = NotificationCompat.Builder(this, channelId )
    val notification = notificationBuilder.setOngoing(true)
            .setSmallIcon(R.mipmap.ic_launcher)
            .setPriority(PRIORITY_MIN)
            .setCategory(Notification.CATEGORY_SERVICE)
            .build()
    startForeground(101, notification)
}


@RequiresApi(Build.VERSION_CODES.O)
private fun createNotificationChannel(): String{
    val channelId = "my_service"
    val channelName = "My Background Service"
    val chan = NotificationChannel(channelId,
            channelName, NotificationManager.IMPORTANCE_HIGH)
    chan.lightColor = Color.BLUE
    chan.importance = NotificationManager.IMPORTANCE_NONE
    chan.lockscreenVisibility = Notification.VISIBILITY_PRIVATE
    val service = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
    service.createNotificationChannel(chan)
    return channelId
}

JAVA EQUIVALENT

public class YourService extends Service {

    // Constants
    private static final int ID_SERVICE = 101;

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        super.onStartCommand(intent, flags, startId);
        return START_STICKY;
    }

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

        // do stuff like register for BroadcastReceiver, etc.

        // Create the Foreground Service
        NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        String channelId = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ? createNotificationChannel(notificationManager) : "";
        NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, channelId);
        Notification notification = notificationBuilder.setOngoing(true)
                .setSmallIcon(R.mipmap.ic_launcher)
                .setPriority(PRIORITY_MIN)
                .setCategory(NotificationCompat.CATEGORY_SERVICE)
                .build();

        startForeground(ID_SERVICE, notification);
    }

    @RequiresApi(Build.VERSION_CODES.O)
    private String createNotificationChannel(NotificationManager notificationManager){
        String channelId = "my_service_channelid";
        String channelName = "My Foreground Service";
        NotificationChannel channel = new NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_HIGH);
        // omitted the LED color
        channel.setImportance(NotificationManager.IMPORTANCE_NONE);
        channel.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
        notificationManager.createNotificationChannel(channel);
        return channelId;
    }
}

8
Você pode usar ContextCompat.startForegroundService(Context,Intent)em sua atividade que fará a coisa certa. ( developer.android.com/reference/android/support/v4/content/… )
Simon Featherstone

3
você provavelmente vai querer usar .setCategory(NotificationCompat.CATEGORY_SERVICE), em vez de Notification.CATEGORY_SERVICEse seu min API é <21
Alguém em algum lugar

6
Observe que os aplicativos que segmentam Build.VERSION_CODES.P(nível 28 da API) ou posterior devem solicitar a permissão Manifest.permission.FOREGROUND_SERVICEpara usar startForeground()- consulte developer.android.com/reference/android/app/…
Vadim Kotov

21

Além da resposta da RAWA , esta paz de código:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    startForegroundService(intent)
} else {
    startService(intent)
}

Você pode mudar para:

ContextCompat.startForegroundService(context, yourIntent);

Se você olhar dentro deste método, poderá ver que esse método faz todas as verificações funcionarem para você.


9

Se você deseja tornar o IntentService um serviço em primeiro plano

então você deve substituir onHandleIntent()assim

Override
protected void onHandleIntent(@Nullable Intent intent) {


    startForeground(FOREGROUND_ID,getNotification());     //<-- Makes Foreground

   // Do something

    stopForeground(true);                                // <-- Makes it again a normal Service                         

}

Como fazer a notificação?

simples. Aqui está o getNotification()método

public Notification getNotification()
{

    Intent intent = new Intent(this, SecondActivity.class);
    PendingIntent pendingIntent = PendingIntent.getActivity(this,0,intent,0);


    NotificationCompat.Builder foregroundNotification = new NotificationCompat.Builder(this);
    foregroundNotification.setOngoing(true);

    foregroundNotification.setContentTitle("MY Foreground Notification")
            .setContentText("This is the first foreground notification Peace")
            .setSmallIcon(android.R.drawable.ic_btn_speak_now)
            .setContentIntent(pendingIntent);


    return foregroundNotification.build();
}

Compreensão mais profunda

O que acontece quando um serviço se torna um serviço em primeiro plano

Isto acontece

insira a descrição da imagem aqui

O que é um serviço em primeiro plano?

Um serviço em primeiro plano,

  • garante que o usuário esteja ciente de que algo está acontecendo em segundo plano, fornecendo a notificação.

  • (o mais importante) não é eliminado pelo sistema quando fica com pouca memória

Um caso de uso de serviço em primeiro plano

Implementando a funcionalidade de download de músicas em um aplicativo de música


5

Adicione o código fornecido Classe de serviço para "OS> = Build.VERSION_CODES.O" em onCreate ()

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

     .................................
     .................................

    //For creating the Foreground Service
    NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
    String channelId = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ? getNotificationChannel(notificationManager) : "";
    NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, channelId);
    Notification notification = notificationBuilder.setOngoing(true)
            .setSmallIcon(R.mipmap.ic_launcher)
           // .setPriority(PRIORITY_MIN)
            .setCategory(NotificationCompat.CATEGORY_SERVICE)
            .build();

    startForeground(110, notification);
}



@RequiresApi(Build.VERSION_CODES.O)
private String getNotificationChannel(NotificationManager notificationManager){
    String channelId = "channelid";
    String channelName = getResources().getString(R.string.app_name);
    NotificationChannel channel = new NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_HIGH);
    channel.setImportance(NotificationManager.IMPORTANCE_NONE);
    channel.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
    notificationManager.createNotificationChannel(channel);
    return channelId;
}

Adicione esta permissão no arquivo de manifesto:

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

1

Manipule a intenção no startCommand do serviço usando.

 stopForeground(true)

Essa chamada removerá o serviço do estado de primeiro plano , permitindo que seja eliminado se for necessária mais memória. Isso não impede a execução do serviço . Para isso, é necessário chamar stopSelf () ou métodos relacionados.

Valor de passagem verdadeiro ou falso indicado se você deseja remover a notificação ou não.

val ACTION_STOP_SERVICE = "stop_service"
val NOTIFICATION_ID_SERVICE = 1
...  
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
    super.onStartCommand(intent, flags, startId)
    if (ACTION_STOP_SERVICE == intent.action) {
        stopForeground(true)
        stopSelf()
    } else {
        //Start your task

        //Send forground notification that a service will run in background.
        sendServiceNotification(this)
    }
    return Service.START_NOT_STICKY
}

Lide com sua tarefa quando em destruição for chamado por stopSelf () .

override fun onDestroy() {
    super.onDestroy()
    //Stop whatever you started
}

Crie uma notificação para manter o serviço em execução em primeiro plano.

//This is from Util class so as not to cloud your service
fun sendServiceNotification(myService: Service) {
    val notificationTitle = "Service running"
    val notificationContent = "<My app> is using <service name> "
    val actionButtonText = "Stop"
    //Check android version and create channel for Android O and above
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        //You can do this on your own
        //createNotificationChannel(CHANNEL_ID_SERVICE)
    }
    //Build notification
    val notificationBuilder = NotificationCompat.Builder(applicationContext, CHANNEL_ID_SERVICE)
    notificationBuilder.setAutoCancel(true)
            .setDefaults(NotificationCompat.DEFAULT_ALL)
            .setWhen(System.currentTimeMillis())
            .setSmallIcon(R.drawable.ic_location)
            .setContentTitle(notificationTitle)
            .setContentText(notificationContent)
            .setVibrate(null)
    //Add stop button on notification
    val pStopSelf = createStopButtonIntent(myService)
    notificationBuilder.addAction(R.drawable.ic_location, actionButtonText, pStopSelf)
    //Build notification
    val notificationManagerCompact = NotificationManagerCompat.from(applicationContext)
    notificationManagerCompact.notify(NOTIFICATION_ID_SERVICE, notificationBuilder.build())
    val notification = notificationBuilder.build()
    //Start notification in foreground to let user know which service is running.
    myService.startForeground(NOTIFICATION_ID_SERVICE, notification)
    //Send notification
    notificationManagerCompact.notify(NOTIFICATION_ID_SERVICE, notification)
}

Dê um botão de parada na notificação para interromper o serviço quando o usuário precisar.

/**
 * Function to create stop button intent to stop the service.
 */
private fun createStopButtonIntent(myService: Service): PendingIntent? {
    val stopSelf = Intent(applicationContext, MyService::class.java)
    stopSelf.action = ACTION_STOP_SERVICE
    return PendingIntent.getService(myService, 0,
            stopSelf, PendingIntent.FLAG_CANCEL_CURRENT)
}

1

Nota: Se o seu aplicativo atingir a API nível 26 ou superior, o sistema impõe restrições ao uso ou criação de serviços em segundo plano, a menos que o próprio aplicativo esteja em primeiro plano.

Se um aplicativo precisar criar um serviço em primeiro plano, ele deverá chamar startForegroundService(). Esse método cria um serviço em segundo plano, mas o método sinaliza ao sistema que o serviço se promoverá em primeiro plano.

Após a criação do serviço, ele deve chamar seu startForeground() method within five seconds.


1
Espero que você esteja falando sobre a pergunta atual. Caso contrário, não existe essa regra na comunidade Stackoverflow
Farid

O @RogerGusmao no código do ambiente pronto para produção nem sempre salva seu projeto. Além disso - há muitos grandes exemplos com código abaixo e acima da minha resposta .. Meu projeto teve problemas durante o lançamento exatamente porque eu não sabia sobre startForegroundServicemétodo
Andrii Kovalchuk

0

No meu caso, era totalmente diferente, pois eu não estava tendo atividade para lançar o serviço no Oreo.

Abaixo estão as etapas que eu usei para resolver esse problema de serviço em primeiro plano -

public class SocketService extends Service {
    private String TAG = this.getClass().getSimpleName();

    @Override
    public void onCreate() {
        Log.d(TAG, "Inside onCreate() API");
        if (Build.VERSION.SDK_INT >= 26) {
            NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this);
            mBuilder.setSmallIcon(R.drawable.ic_launcher);
            mBuilder.setContentTitle("Notification Alert, Click Me!");
            mBuilder.setContentText("Hi, This is Android Notification Detail!");
            NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

            // notificationID allows you to update the notification later on.
            mNotificationManager.notify(100, mBuilder.build());
            startForeground(100, mBuilder.mNotification);
        }
        Toast.makeText(getApplicationContext(), "inside onCreate()", Toast.LENGTH_LONG).show();
    }


    @Override
    public int onStartCommand(Intent resultIntent, int resultCode, int startId) {
        Log.d(TAG, "inside onStartCommand() API");

        return startId;
    }


    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "inside onDestroy() API");

    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO Auto-generated method stub
        return null;
    }
}

E depois disso, para iniciar esse serviço, eu acionei abaixo do cmd -


shell adb -s "+ serial_id +" sou startforegroundservice -n com.test.socket.sample / .SocketService


Portanto, isso me ajuda a iniciar o serviço sem atividade nos dispositivos Oreo :)


0

A solução @mikebertiean quase fez o truque, mas eu tive esse problema com um toque adicional - eu uso o sistema Gingerbread e não queria adicionar um pacote extra apenas para executar a notificação. Finalmente encontrei: https://android.googlesource.com/platform/frameworks/support.git+/f9fd97499795cd47473f0344e00db9c9837eea36/v4/gingerbread/android/support/v4/app/NotificationCompatGingerbread.java

então eu acertei um problema adicional - a notificação simplesmente mata meu aplicativo quando ele é executado (como resolver esse problema: Android: como evitar que o clique em uma Notificação chame onCreate () ), portanto, no total, meu código no serviço se parece com isso (C # / Xamarin):

Intent notificationIntent = new Intent(this, typeof(MainActivity));
// make the changes to manifest as well
notificationIntent.SetFlags(ActivityFlags.ClearTop | ActivityFlags.SingleTop);
PendingIntent pendingIntent = PendingIntent.GetActivity(this, 0, notificationIntent, 0);
Notification notification = new Notification(Resource.Drawable.Icon, "Starting service");
notification.SetLatestEventInfo(this, "MyApp", "Monitoring...", pendingIntent);
StartForeground(1337, notification);
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.