Android - реализация startForeground для службы?


124

Поэтому я не уверен, где и как реализовать этот метод, чтобы моя служба работала на переднем плане. В настоящее время я начинаю свою службу следующим образом в другом действии:

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

А затем в onCreate () myServices я пробую startForeground () ...?

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

Так что да, я немного растерялся и не знаю, как это реализовать.


Что ж, это не работает, по крайней мере, насколько я могу судить, моя служба по-прежнему работает как фоновая и погибает.
JDS

Ответы:


131

Я бы начал с полного заполнения Notification. Вот пример проекта, демонстрирующего использование startForeground().


8
Можно ли использовать startForeground () без уведомления? Или мы можем позже обновить такое же уведомление?
JRC

2
Есть ли конкретная причина, по которой вы использовали 1337?
Cody

33
@DoctorOreo: он должен быть уникальным в приложении, но не обязательно уникальным на устройстве. Я выбрал 1337, потому что это 1337 год . :-)
CommonsWare

@JRC вопрос хороший. Можно ли использовать startForeground () без уведомления?
Snicolas

2
@Snicolas: Спасибо, что указали на недостаток в Android. Я буду работать над тем, чтобы это исправить.
CommonsWare

78

Из вашего основного действия запустите службу с помощью следующего кода:

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

Затем в вашем сервисе onCreate()вы создадите свое уведомление и установите его как передний план следующим образом:

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 как обновить это уведомление от MainActivity?
Roon13

1
@ Roon13 с использованием идентификатора, в данном случае 1337 ... вы должны иметь возможность создать новое уведомление и вызвать startForeground с идентификатором
mikebertiean

@ Roon13, прочтите этот вопрос stackoverflow.com/questions/5528288/…
mikebertiean

@mikebertiean Как я могу вызвать startForeground из MainActivity? также как я могу очистить уведомление от MainActvity по завершении процесса?
Roon13

@mikebertiean Я понял, что мне нужно снова вызвать startForeground в классе обслуживания, но как? Мне нужно снова вызвать startService ()?
Roon13

30

Это мой код для установки службы на передний план:

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

}

Мне нужно создать уведомление с помощью PendingIntent, чтобы я мог начать свое основное действие из уведомления.

Чтобы удалить уведомление, просто вызовите stopForeground (true);

Он вызывается в onStartCommand (). Пожалуйста, обратитесь к моему коду по адресу: https://github.com/bearstand/greyparrot/blob/master/src/com/xiong/richard/greyparrot/Mp3Recorder.java


Если вы удалите уведомление, вызывающее stopForeground (true), вы отменяете службу startforeground
sdelvalle57,

6
Откуда вы вызываете этот метод?
Сружан Барай,

7
Intent.FLAG_ACTIVITY_NEW_TASKнедействителен в контексте PendingIntent.
mixel

30

Решение для Oreo 8.1

Я столкнулся с некоторыми проблемами, такими как RemoteServiceException, из-за недопустимого идентификатора канала в самых последних версиях Android. Вот как я это решил:

Деятельность :

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

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
Вы можете использовать ContextCompat.startForegroundService(Context,Intent)в своей деятельности то, что будет делать правильно. ( developer.android.com/reference/android/support/v4/content/… )
Саймон Фезерстоун

3
вы, вероятно, захотите использовать .setCategory(NotificationCompat.CATEGORY_SERVICE)вместо этого, Notification.CATEGORY_SERVICEесли ваш min API <21
Someone Somewhere

6
Пожалуйста , обратите внимание , что приложения , ориентированные Build.VERSION_CODES.P(уровень API 28) или более поздней версии должен запросить разрешение Manifest.permission.FOREGROUND_SERVICE, чтобы использовать startForeground()- см developer.android.com/reference/android/app/...
Вадим Котов

21

В дополнение к ответу RAWA этот кусок кода:

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

Вы можете изменить на:

ContextCompat.startForegroundService(context, yourIntent);

Если вы посмотрите внутрь этого метода, то увидите, что этот метод выполняет всю работу по проверке за вас.


9

Если вы хотите сделать IntentService службой переднего плана

тогда вы должны переопределить onHandleIntent()это

Override
protected void onHandleIntent(@Nullable Intent intent) {


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

   // Do something

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

}

Как сделать уведомление?

просто. Вот getNotification()метод

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

Более глубокое понимание

Что происходит, когда сервис становится сервисом переднего плана

Это случилось

введите описание изображения здесь

Что такое служба переднего плана?

Служба переднего плана,

  • гарантирует, что пользователь активно осведомлен о том, что что-то происходит в фоновом режиме, предоставляя уведомление.

  • (самое главное) не убивается системой, когда ей не хватает памяти

Пример использования службы переднего плана

Реализация функции загрузки песен в музыкальном приложении


5

Добавьте данный код Сервисный класс для "OS> = Build.VERSION_CODES.O" в 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;
}

Добавьте это разрешение в файл манифеста:

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

1

Обработайте намерение по команде startCommand службы с помощью.

 stopForeground(true)

Этот вызов удалит службу из состояния переднего плана , позволяя убить ее, если потребуется больше памяти. Это не останавливает работу службы . Для этого вам нужно вызвать stopSelf () или связанные методы.

При передаче значения true или false указывается, хотите ли вы удалить уведомление или нет.

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
}

Обработайте свою задачу, когда функция destroy вызывается stopSelf () .

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

Создайте уведомление, чтобы служба продолжала работать на переднем плане.

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

Дайте кнопку остановки при уведомлении, чтобы остановить службу, когда это необходимо пользователю.

/**
 * 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

Примечание. Если ваше приложение нацелено на уровень API 26 или выше, система накладывает ограничения на использование или создание фоновых служб, если само приложение не находится на переднем плане.

Если приложению необходимо создать службу переднего плана, оно должно вызвать startForegroundService(). Этот метод создает фоновую службу, но метод сигнализирует системе, что служба будет продвигать себя на передний план.

После того, как служба была создана, служба должна вызвать ее startForeground() method within five seconds.


1
Надеюсь, вы говорите о текущем вопросе. В противном случае в сообществе Stackoverflow такого правила нет
Фарид

@RogerGusmao в готовом к эксплуатации коде среды не всегда спасет ваш проект. Кроме того - есть много отличных примеров с кодом ниже и выше моего ответа .. У моего проекта были проблемы во время выпуска именно из-за того, что я не знал о startForegroundServiceметоде
Андрей Ковальчук

0

В моем случае все было совершенно иначе, поскольку у меня не было активности по запуску службы в Oreo.

Ниже приведены шаги, которые я использовал для решения этой проблемы службы переднего плана.

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

И после этого, чтобы запустить эту службу, я вызвал ниже cmd -


adb -s "+ serial_id +" оболочка am startforegroundservice -n com.test.socket.sample / .SocketService


Так что это помогает мне запускать сервис без активности на устройствах Oreo :)


0

Решение @mikebertiean почти помогло, но у меня возникла проблема с дополнительным поворотом - я использую систему Gingerbread, и я не хотел добавлять дополнительный пакет только для запуска уведомления. Наконец я нашел: https://android.googlesource.com/platform/frameworks/support.git+/f9fd97499795cd47473f0344e00db9c9837eea36/v4/gingerbread/android/support/v4/app/NotificationCompatGingerbread.java

тогда я столкнулся с дополнительной проблемой - уведомление просто убивает мое приложение при запуске (как решить эту проблему: Android: как избежать этого, щелкнув по уведомлению, вызывающему onCreate () ), поэтому в целом мой код в службе выглядит так (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);
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.