在Oreo中销毁服务时避免取消通知 [英] Keeping notification from dismissing when service is destroyed in Oreo

查看:89
本文介绍了在Oreo中销毁服务时避免取消通知的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

Okay. I had a question regaurding keeping Media Player Services in Android Oreo. Based on the discussion here:

Android Oreo:保持启动的后台服务活着而没有将其设置为前台(但带有通知)?

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

It seems the proper way to handle Media player services in Android Oreo is to store state information when the media player is paused so if it is destroyed, pressing play will create a new media player and start where it left off.

我的问题是我如何创建一个在启动它的服务被销毁时不会被销毁的通知.我没有运行任何代码来关闭该通知,但是当该服务被破坏时,它仍然会自动被关闭.如您在我的代码中所见,我可以在onDestroy上重建通知,但是我不喜欢这种外观,因为用户可以看到它关闭并再次重建.

My question is how do I create a notification that won't get destroyed when the service that started it is destroyed. I am not running any code to dismiss the notification but it is still being dismissed automatically when the service is destoryed. As you can see in my code, I can rebuild the notification onDestroy, but I don't like the way this looks as the user can see it dismissing and rebuilding again.

这是我的通知代码:

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

这是服务销毁时发生的事情:

And here is what happens when the service is destroyed:

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

推荐答案

好的.我发现stopForeground函数具有标志的可选参数,而不是布尔值.可以给它一个标志STOP_FOREGROUND_DETACH,即使在调用stopForeground之后也需要将该标志从服务中分离出来,这样,当服务被破坏时,该通知将不会被取消,并且不必构建新的通知.这是我更新的用于启动通知的代码:

Okay. I found out that the stopForeground function has an optional parameter of a flag instead of a boolean. It can be given a flag STOP_FOREGROUND_DETACH which is necessary to detach the notification from the service even after calling stopForeground so that when the service is destroyed, the notification won't be dismissed and a new notification doesn't have to be built. Here is my updated code for starting the notification:

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

这篇关于在Oreo中销毁服务时避免取消通知的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆