当 Oreo 中的服务被销毁时,防止通知被驳回

2023-11-26

好的。我有一个关于在 Android Oreo 中保留媒体播放器服务的问题。基于这里的讨论:

Android Oreo:保持已启动的后台服务处于活动状态而不将其设置为前台(但有通知)?

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

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

private fun updateNotification(preparing: Boolean = false) {
    if(getPlaying()) { 
        startService(Intent(applicationContext, [email protected]))
        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())
    }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

当 Oreo 中的服务被销毁时,防止通知被驳回 的相关文章

  • Android 卷页动画

    我对 Android 动画有点陌生 目前我正在开发一个故事活动 需要像 iPhone 中那样使用卷页动画 我发现 iPhone 中有一种方法可以做到这一点 但我仍然找不到在android中做的方法 所以请帮我解决这个问题 谢谢大家 谷歌代码
  • 位图背景图像应支持哪些屏幕尺寸/密度组合?

    我正在编写一个应用程序 我需要一些全屏位图背景 基于我天真的阅读支持多屏 http developer android com guide practices screens support html在 Android 文档中 为了涵盖我的
  • 如何在Android 4.2中更改Action Bar选项菜单的背景颜色?

    我想更改 Android 4 2 中选项 溢出 菜单的背景颜色 我已经尝试了所有方法 但它仍然显示主题设置的默认颜色 我使用了以下代码和 XML 配置 MainActivity java public class MainActivity
  • 将JSON数据解析到Android ListView中

    我已经潜伏这个网站很长一段时间了 幸运的是 到目前为止我所有的问题都得到了解答 我希望你们中的一些人能够阐明我的问题 我可以成功地让 ListView 来显示字符串数组 但是当我尝试将 JSON 数据解析为数组时 我的程序只是强制关闭 而
  • Facebook4j API:搜索

    我正在使用 Facebook4j 通过关键字获取状态 facebook4j conf ConfigurationBuilder fac new facebook4j conf ConfigurationBuilder fac setDebu
  • Twowayview 滚动时自动添加内边距

    我在用双向视图 https github com lucasr twoway view在我的一个项目中 这是android的扩展回收者视图 https developer android com reference android supp
  • Android SQLite 从代码中转储数据库以进行错误报告

    我正在为我的一个 Android 程序开发一种诊断工具 本质上 如果用户遇到问题 我想做一些类似 SQLite 的事情dump 参考 http www sqlite org sqlite html http www sqlite org s
  • 如何在 Jetpack compose 中制作 FlipCard 动画

    我有一个现有的应用程序 我在其中使用 XML 中的 Objectanimator 实现了 FlipCard 动画 如下所示 如果我点击一张卡片 它会水平翻转 但现在我想将其迁移到 jetpack compose 那么jetpack comp
  • org.apache.http.conn.HttpHostConnectException:在 android 中连接到 http://localhost 被拒绝

    我正在制作一个应用程序 在执行它时将图像上传到服务器并将其数据库更新到android中的服务器 它显示错误 Connection to http localhost refused 还有更多错误 我研究了这个问题 发现不是提供 URL 连接
  • 如何为 flutter 绘图应用实现橡皮擦功能

    有一个关于通过 flutter 创建绘图应用程序的视频 YouTube https www youtube com watch v yyHhloFMNNA 它支持当用户点击屏幕时绘制线 点 但我找不到像 Android 本机那样擦除用户绘制
  • 分离 Fragment 和删除 Fragment 有什么区别?

    在 Android 文档中碎片交易 http developer android com reference android app FragmentTransaction html我注意到两种非常相似的方法 detach and remo
  • 如何关闭导航抽屉以使用返回主页图标按钮?

    我也将操作栏与搜索栏一起使用 并且我需要像后退按钮一样使用 ActionBar ico 但我也在使用导航抽屉 如何关闭 隐藏 禁用导航抽屉菜单以使用后退按钮 我的 ActionBar 代码 Override public boolean o
  • 推特更新状态

    我正在通过 twitter4j 将 Twitter 集成到 Android 我可以成功阅读我发布的推文 现在我试图用它发布推文 但我不能 我收到如下奇怪的警告 02 01 16 28 43 298 WARN System err 729 4
  • 可用屏幕的尺寸

    我使用的是 Nexus 7 1280x800 android 4 2 2 API 17 我想获取屏幕的大小 将其划分为相同高度和宽度的正方形部分 我正在使用 FrameLayout 我的方块是 ImageView 的子类 我这样做 cont
  • 如何从一个活动检索 Double 值到另一活动?

    我制作了一个包含 2 个活动的应用程序 其中第一个活动包含一些 EditText 十进制数字 另一个活动也包含一些 EditText 十进制 现在我想将一个 EditText 的值传递给另一个 但作为 双 而不是作为一个字符串 因为该值将用
  • 如何从图库动态卸载图像?

    我有自定义 ImageView public class ShadowedImageView extends ImageView private Paint mPaint public Bitmap bitmap null private
  • 如果我的应用程序安装在 SD 卡上,私人数据也在那里吗?

    我假设应用程序的私有数据 例如 SharedPreferences 和 SQLite 数据库 位于手机的内部存储而不是 SD 卡上 即使应用程序本身安装在 SD 卡上 我在任何地方都找不到对此的简单明确的确认 有人可以确认一下吗 是的 私有
  • Android Studio 中自动打开“运行设备”选项卡

    在 Android Studio Flamingo 中有一个名为跑步设备并且它会在每次之后自动打开运行应用程序 有办法禁止这个自动打开吗 我尝试禁用启用物理 Android 设备的镜像选项 但选项卡仍然自动打开 基于此issue https
  • Android - 如何简单地拖放按钮?

    我在这里找到了一个适合初学者的教程 http androidrox wordpress com 2011 05 13 android sample app drag and drop image using touch http andro
  • Android Google 地图无法在当前主题中找到样式“mapViewStyle”

    添加谷歌地图视图时 我扩展了MapView 使用xml编辑器将其添加到活动中 并将我的谷歌地图api密钥手动添加到布局xml文件中 我的权限在清单文件中允许互联网 我想知道的是 在 xml 编辑器中 我收到错误 无法在当前主题中找到样式 m

随机推荐