Android - впровадження startForeground для послуги?


124

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

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

А потім у моєму сервісі onCreate () я пробую startForeground () ...?

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

Так що я трохи розгублений і не знаю, як це здійснити.


Ну, це не працює, принаймні, наскільки я можу сказати, моя служба як і раніше працює як фонова служба і вбивається.
JDS

Тема пов'язана з: stackoverflow.com/questions/10962418 / ...
Snicolas

Відповіді:


131

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


8
Чи можна використовувати startForeground () без повідомлення? Або ми можемо пізніше оновити те саме повідомлення?
JRC

2
Чи є певна причина, яку ви використовували 1337?
Коді

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 (правда);

Він викликається в onStartCommand (). Зверніться до мого коду за адресою: https://github.com/bearstand/greyparrot/blob/master/src/com/xiong/richard/greyparrot/Mp3Recorder.java


Якщо ви видалите сповіщення про виклик stopForeground (true), ви скасовуєте послугу
startforeground

6
Звідки ви називаєте цей метод?
Srujan Barai

7
Intent.FLAG_ACTIVITY_NEW_TASKне діє в контексті PendingIntent.
міксель

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
}

РІВНОВАЛЬНА ДЖАВА

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
Хтось десь

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

Глибше розуміння

Що відбувається, коли сервіс стає службою переднього плану

Це трапляється

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

Що таке Служба переднього плану?

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

  • гарантує, що користувач активно усвідомлює, що щось відбувається у фоновому режимі, надаючи сповіщення.

  • (головне) не вбивається системою, коли вона втрачає пам'ять

Випадок використання послуги переднього плану

Реалізація функції завантаження пісні в програмі Music


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 () або пов'язані з ним методи.

Значення, що передається істинним або хибним, вказане, якщо ви хочете видалити сповіщення чи ні.

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
}

Виконайте завдання, коли при знищенні викликається 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
Farid

@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 # / Ксамарін):

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.