MediaPlayer,Doze模式,Wake Lock和前台服务

6
我已经阅读了几篇关于Android M中新的'Doze'模式的文章,也看了Android开发者网站上的文章:https://developer.android.com/training/monitoring-device-state/doze-standby.html,但我仍然在手机上(Android 6.0.1)遇到一个前台服务无法正确运行的问题。
问题:
我有一个服务,使用MediaPlayer对象处理播放音乐,该服务实现了onCompletion和onPrepared MediaPlayer接口。
伪代码:
    public class MusicService extends Service implements MediaPlayer.OnPreparedListener,
            MediaPlayer.OnCompletionListener, MediaPlayer.OnErrorListener {

            // Class Variables/Methods etc...

            @Override
            public void onPrepared(MediaPlayer mp) {

                   mp.start();

                   // update notification using startForeground()
            }


            @Override
                public void onCompletion(MediaPlayer mp) {

                   // Go and get another music track in separate thread
                   // (Have used AsyncTask and RX Java/RX Android to test if problem is here - works fine)
            }

            @Override
                public boolean onError(MediaPlayer mp, int what, int extra) {

                    // handleError

                    return false;
            }

    }

该应用程序在手机连接电源、屏幕开启或应用开启且屏幕开启时运行良好。然而,当手机处于空闲状态下播放音乐(介于5至40分钟之间),一旦onCompletion()被调用,该服务就无法检索到下一首曲目。
服务已设置并满足以下条件: - 服务通知已使用startForeground()方法启动。 - MediaPlayer对象具有部分wakelock PARTIAL_WAKE_LOCK。 - 服务在单独的进程中运行,与主应用程序/用户界面分离。 - 所有媒体使用prepareAsync(),获取下一曲uri的逻辑在后台线程中执行,仅在主线程中调用prepareAsync()方法。
我尝试过以下方法: - 在主线程中检索下一首歌曲(onCompletion()被调用后)而不是在后台线程中。 - 在onCompletion()中获取单独的Partial WakeLock,并在onPrepared()方法被调用时进行保持和释放。 - 还尝试了其他一些无关紧要的事情,但没有任何变化…
我有一个logcat输出,在手机长时间未使用、屏幕关闭且未连接电源时获取了logcat(一旦我重新插上电源并更新日志,便演示了这个问题)。
06-15 02:31:39.346 musicplayer:music_service E/MusicService: onPrepared : Tom Petty GH - 11 - You Got Lucky
06-15 02:31:39.548 musicplayer:music_service D/MusicService: Releasing wakelock
06-15 02:35:15.467 musicplayer:music_service E/MusicService: onCompletion called
06-15 02:35:15.467 musicplayer:music_service D/MusicService: Acquiring wakelock
06-15 02:35:15.514 musicplayer:music_service D/NextActionStrategy: Retrieving song - rx java - Main Ui Thread =false
06-15 02:35:15.870 musicplayer:music_service D/NextActionStrategy: Song Retrieved : Ten Years- Main Ui Thread = true
06-15 02:35:15.950 musicplayer:music_service E/MusicService: onPrepared : Ten Years
06-15 02:35:16.149 musicplayer:music_service D/MusicService: Releasing wakelock
06-15 02:38:59.002 musicplayer:music_service E/MusicService: onCompletion called
06-15 02:38:59.002 musicplayer:music_service D/MusicService: Acquiring wakelock
06-15 02:38:59.049 musicplayer:music_service D/NextActionStrategy: Retrieving song - rx java - Main Ui Thread =false
06-15 02:38:59.211 musicplayer:music_service D/NextActionStrategy: Song Retrieved : What Am I- Main Ui Thread = true
06-15 02:50:07.642 musicplayer:music_service E/MusicService: onPrepared : What Am I
06-15 02:50:07.976 musicplayer:music_service D/MusicService: Releasing wakelock
06-15 02:50:22.097 musicplayer:music_service D/MusicService: onStartCommand called : action.PAUSE

例程将是以下步骤:
  • 播放音轨
  • 调用onCompletion()
  • 获取唤醒锁并获取下一首曲目
  • PrepareAsync()
  • 调用onPrepared - 开始播放并使用startForeground更新通知
根据日志,您可以看到在02:38:59.xxx时调用了onCompletion,找到了一首曲目,并返回到主线程,在那里调用了prepareAsync(),但是接下来12分钟什么都没有发生,直到我手动唤醒手机,此时代码继续执行并播放音轨。
我不认为这是设备的问题,因为我使用的其他音乐应用程序在这种情况下工作正常,但是我只能在一个真实设备上进行测试 :(
如果您已经阅读到这里,感谢您的耐心!任何建议将不胜感激(希望是关于解决方案而不是我的音乐品味!)。

1
@Opiatefuchs - 这是我在其他媒体播放器应用程序中不必做的事情 - 这是唯一的方法吗? - Mark
兄弟,我跟那个东西斗争了好久。我有一部华为Ascend Mate 7,运行的是棒棒糖系统。一切都很顺利,因为我知道如何使用电池节能模式并处理它。然后,更新到MM系统后,什么都不好使了。结果发现这是华为的一个bug,后来他们更新了MM系统,然后就好了。那么,你用的是什么设备和安卓版本呢?另外,我发现像WhatsApp这样的应用程序在设置中有一些特殊功能,而其他应用程序没有,所以这必须是供应商实现的,开发人员无法处理。 - Opiatefuchs
1
不,针对API22不会解决这个问题。因为无法使用可用方法让闹钟管理器执行。你应该尝试使用闹钟管理器,因为文档中指出唤醒锁被忽略了。而应该使用setAndAllowWhileIdle()setExactAndAllowWhileIdle()方法来代替。此外,使用setAlarmClock也可以,但是在设备顶部会有一个时钟图标,并且闹钟通过系统时间显示出来。 - Opiatefuchs
@Mark Keen 我曾经遇到过类似的问题,我的后台“Service”在(6.0.1)上空闲10到15分钟后总是停止。我研究了Doze模式,但那不是问题所在。我通过确保我的“Service”使用了“startForeground()”(就像你一样),并确保“onStartCommand”返回START_NOT_STICKY来解决了这个问题。我在我的“onCreate()”方法中放置了一个PARTIAL_WAKE_LOCK。这对我有用,现在服务在后台运行超过2小时而没有任何问题。 - midiwriter
@MikeLudwig 您好,感谢您的建议。我的“Service”设置为“START_NOT_STICKY”,同时在“MediaPlayer.OnCompletionListener”回调方法“onCompletion(MediaPlayer mp)”中执行任务时,我会持有一个“PARTIAL_WAKE_LOCK”,并在MediaPlayer对象获得自己的“PARTIAL_WAKE_LOCK”并播放歌曲后释放唤醒锁。我确信问题是Doze模式,因为当设备连接到电源时,这个问题就不存在了。根据文档:如果用户将设备未插电且静止一段时间且屏幕关闭,则设备进入Doze模式。 - Mark
显示剩余15条评论
1个回答

0

我只是想让你看看我如何实现我的startForeground()方法。也许这可以帮助你:

 void setUpAsForeground(String text) {
    mNotificationManager = (NotificationManager)      this.getSystemService(Context.NOTIFICATION_SERVICE);
    PendingIntent contentIntent = PendingIntent.getActivity(this, 0, new Intent(this, Main.class), 0);

    NotificationCompat.Builder noti = new NotificationCompat.Builder(this)
            .setContentTitle( "My App Title")
            .setContentText(text)
            .setSmallIcon(R.drawable.ic_stat_recording)
            .setStyle(new NotificationCompat.BigTextStyle()
                    .bigText(text))

            .setPriority(Notification.PRIORITY_MAX)
            .setContentIntent(contentIntent)
            .setOngoing(false);
    final Notification notification = noti.build();
    //mNotificationManager.notify(NOTIFICATION_ID, noti.build());
    startForeground(NOTIFICATION_ID, notification);

}

onCreate()中:

 @Override
 public void onCreate() {

    PowerManager powerManager = (PowerManager)      getSystemService(POWER_SERVICE);
    PowerManager.WakeLock wakeLock =      powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
            "MyWakelockTag");
    wakeLock.acquire();

    ***********
}

问题不在于PARTIAL_WAKE_LOCK - 媒体播放器对象在播放时会自行获取wakelock。问题在于Doze模式,根据文档,如果手机处于空闲状态且未插入电源时进入Doze模式,则会忽略唤醒锁定,这意味着即使持有唤醒锁定,Doze模式也会覆盖它们。来源-https://developer.android.com/training/monitoring-device-state/doze-standby.html#understand_doze中的Doze限制-正如我所说,只有在拔下电源并保持静止时才会出现问题-感谢您的帮助。 - Mark
1
你真的找到了解决方案吗?@MarkKeen - Luis Pereira

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接