在Oreo中销毁服务时保持通知不被解雇

时间:2018-03-04 21:10:23

标签: android service notifications media-player

好。我有一个问题,即在Android Oreo中保留媒体播放器服务。基于此处的讨论:

Android Oreo: Keep started background service alive without setting it foreground (but with a notification)?

在Android Oreo中处理媒体播放器服务的正确方法似乎是在媒体播放器暂停时存储状态信息,因此如果它被销毁,按下播放将创建一个新的媒体播放器并从它停止的地方开始。

我的问题是如何创建一个通知,当启动它的服务被销毁时不会被销毁。我没有运行任何代码来解除通知,但是当服务被破坏时,它仍然会自动被解雇。正如你在我的代码中看到的那样,我可以重建通知onDestroy,但我不喜欢这种看起来的方式,因为用户可以看到它再次解雇和重建。

这是我的通知代码:

private void buildNotification() {
        Cat.d("building notification");
        final MediaPlayerService context = this;

        RequestBuilder<Bitmap> requestBuilder = Glide.with(this).asBitmap()
                .load(R.mipmap.ic_launcher);

        NotificationCompat.Action action;
        if (mediaPlayer != null && mediaPlayer.isPlaying())
            action = generateAction(R.drawable.ic_pause, "Pause");
        else
            action = generateAction(R.drawable.ic_play_arrow, "Play");

        int[] actionsInCompact;

        builder = new NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID)
                .setSmallIcon(R.drawable.ic_navbooks_icon_black)
                //.setOnlyAlertOnce(true)
                .setContentTitle(book.getTitle())
                .setContentText(book.getAuthor())
                .setAutoCancel(false)
                .setContentIntent(PendingIntentHelper.getOpenMainActivityIntent(context, book.getId()))
                .setDeleteIntent(PendingIntentHelper.getStopServiceIntent(context));

        if (SettingsUtil.GetNotificationSkip(context)) {
            builder.addAction(R.drawable.ic_skip_backward, "Skip Previous",
                    PendingIntentHelper.getSkipBackwardIntent(context))
                    .addAction(R.drawable.ic_backward, "Rewind",
                            PendingIntentHelper.getSeekBackwardIntent(context))
                    .addAction(action)
                    .addAction(R.drawable.ic_forward, "Fast Forward",
                            PendingIntentHelper.getSeekForwardIntent(context))
                    .addAction(R.drawable.ic_skip_forward, "Skip Next",
                            PendingIntentHelper.getSkipForwardIntent(context));
            actionsInCompact = new int[]{0, 1, 2, 3, 4};
        } else {
            builder.addAction(R.drawable.ic_backward, "Rewind",
                    PendingIntentHelper.getSeekBackwardIntent(context))
                    .addAction(action)
                    .addAction(R.drawable.ic_forward, "Fast Forward",
                            PendingIntentHelper.getSeekForwardIntent(context));
            actionsInCompact = new int[]{0, 1, 2};
        }
        builder.setStyle(new android.support.v4.media.app.NotificationCompat.MediaStyle()
                .setMediaSession(mediaSession.getSessionToken())
                .setShowActionsInCompactView(actionsInCompact));

        // Load a cover image if there isn't one loaded yet or the cover has changed
        if(cover == null || !book.getCover().equals(lastCover)) {
            lastCover = book.getCover();
            mediaSession.setMetadata(new MediaMetadataCompat.Builder()
                    .putString(MediaMetadataCompat.METADATA_KEY_ARTIST, book.getTitle())
                    .putString(MediaMetadataCompat.METADATA_KEY_TITLE, book.getAuthor())
                    .build());
            Glide.with(this)
                    .asBitmap()
                    .error(requestBuilder)
                    .load(book.getCover())
                    .into(new SimpleTarget<Bitmap>() {
                        @Override
                        public void onResourceReady(Bitmap largeIcon, Transition transition) {
                            Cat.d("Finished loading");
                            cover = largeIcon;
                            // initBuilder
                            builder.setLargeIcon(largeIcon);
                            mediaSession.setMetadata(new MediaMetadataCompat.Builder()
                                    .putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, largeIcon)
                                    .putString(MediaMetadataCompat.METADATA_KEY_ARTIST, book.getTitle())
                                    .putString(MediaMetadataCompat.METADATA_KEY_TITLE, book.getAuthor())
                                    .build());
                            startNotification();
                        }
                    });
        } else {
            mediaSession.setMetadata(new MediaMetadataCompat.Builder()
                    .putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, cover)
                    .putString(MediaMetadataCompat.METADATA_KEY_ARTIST, book.getTitle())
                    .putString(MediaMetadataCompat.METADATA_KEY_TITLE, book.getAuthor())
                    .build());
            if(cover != null)
                builder.setLargeIcon(cover);
        }

        startNotification();
    }

    private NotificationCompat.Action generateAction(int icon, String title) {
        return new NotificationCompat.Action.Builder( icon, title, PendingIntentHelper.getPlayPauseIntent(this)).build();
    }

    private void startNotification() {
        mediaSession.setPlaybackState(new PlaybackStateCompat.Builder()
                .setActions(MEDIA_SESSION_ACTIONS)
                .setState(mediaPlayer.isPlaying() ? PlaybackStateCompat.STATE_PLAYING :
                        PlaybackStateCompat.STATE_PAUSED, book.getLastPosition(), 1)
                .build());

        if(mediaPlayer.isPlaying())
            startForeground(NOTIFICATION_ID, builder.build());
        else
        {
            NotificationManager notificationManager =
                    (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
            if(notificationManager != null) {
                notificationManager.notify(NOTIFICATION_ID, builder.build());
            }
            stopForeground(false);
        }
    }

以下是服务销毁时会发生的事情:

@Override
public void onDestroy() {
    //super.onDestroy();
    handleDestroy();
}

private void handleDestroy() {
        if(book != null)
            sendProgressUpdate();
        else
            book = new BookDBHelper(this).getBook(SettingsUtil.GetLastPlayedBook(this));
        if(mediaPlayer != null) {
            book.setLastPosition(this, getPosition());
            SyncHelper.SendProgressUpdate(this, book);
        }
        if(mediaSession != null)
            mediaSession.release();

        if(noisyRegistered) {
            unregisterReceiver(becomingNoisyReceiver);
            unregisterReceiver(shakeReceiver);
        }
        buildNotification();
    }

1 个答案:

答案 0 :(得分:7)

好。我发现stopForeground函数有一个标志的可选参数而不是布尔值。可以给它一个标志STOP_FOREGROUND_DETACH,即使在调用stopForeground之后也需要从服务中分离通知,这样当服务被销毁时,通知将不会被解除,并且不需要构建新的通知。以下是我更新的启动通知代码:

private fun updateNotification(preparing: Boolean = false) {
    if(getPlaying()) { 
        startService(Intent(applicationContext, this@MediaPlayerService.javaClass))
        startForeground(NOTIFICATION_ID, getNotification())
    }else {
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
            stopForeground(Service.STOP_FOREGROUND_DETACH)
        else
            stopForeground(false)
        notificationManager.notify(NOTIFICATION_ID, getNotification())
    }
}