我正在研究绑定服务、前台服务以及如何使用它们进行音频播放。
基于这个例子,我设置了一个前台服务来播放音频。在我尝试绑定服务之前,这非常适合我的用例,因为我需要在 Activity 和服务之间来回传递一些数据,例如我想要实现的搜索栏的播放位置。
我已经阅读了几篇 StackOverflow 帖子,试图找到解决方案。我知道我应该在启动它之前绑定服务,这样它就不会与它绑定的 Activity 一起被杀死。但这仍然会发生,只要我包含绑定机制。
当我让设备进入睡眠状态时,我在 logcat 中看到的唯一错误是:
2019-11-12 10:49:24.553 812-871/? W/InputDispatcher: channel '7fc2cb2 com.example.android.meditationhub/com.example.android.meditationhub.ui.PlayActivity (server)' ~ Consumer closed input channel or an error occurred. events=0x9
2019-11-12 10:49:24.553 812-871/? E/InputDispatcher: channel '7fc2cb2 com.example.android.meditationhub/com.example.android.meditationhub.ui.PlayActivity (server)' ~ Channel is unrecoverably broken and will be disposed!
所以,我需要弄清楚为什么频道被打破了,但我现在很难过还有什么地方可以看。…</p>
这是我当前的PlayActivity设置:
//called from onCreate()
private void initializeUI() {
playerBinding.playbackControlBt.setPlayListener(new AnimatePlayButton.OnButtonsListener() {
@Override
public boolean onPlayClick(View view) {
mediaPlayerServiceInt = new Intent(PlayActivity.this, MediaPlayerService.class);
mediaPlayerServiceInt.setAction(Constants.START_ACTION);
mediaPlayerServiceInt.putExtra(Constants.URI, medUri);
startService(mediaPlayerServiceInt);
bindMediaPlayerService();
return true;
}
@Override
public boolean onPauseClick(View view) {
Intent pausePlayback = new Intent(PlayActivity.this, MediaPlayerService.class);
pausePlayback.setAction(Constants.PAUSE_ACTION);
PendingIntent pendingPausePlayback = PendingIntent.getService(PlayActivity.this,
0, pausePlayback, PendingIntent.FLAG_UPDATE_CURRENT);
try {
pendingPausePlayback.send();
} catch (PendingIntent.CanceledException e) {
e.printStackTrace();
}
return true;
}
@Override
public boolean onResumeClick(View view) {
Intent resumePlayback = new Intent(PlayActivity.this, MediaPlayerService.class);
resumePlayback.setAction(Constants.PLAY_ACTION);
PendingIntent pendingResumePlayback = PendingIntent.getService(PlayActivity.this,
0, resumePlayback, PendingIntent.FLAG_UPDATE_CURRENT);
try {
pendingResumePlayback.send();
} catch (PendingIntent.CanceledException e) {
e.printStackTrace();
}
return true;
}
@Override
public boolean onStopClick(View view) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
stopService(mediaPlayerServiceInt);
}
return true;
}
});
}
//monitor state of the service
private ServiceConnection mediaPlayerConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mediaPlayerService = ((MediaPlayerService.MyBinder) service).getService();
serviceIsBound = true;
}
@Override
public void onServiceDisconnected(ComponentName name) {
mediaPlayerService = null;
serviceIsBound = false;
}
};
//unbind service so the audio continues to play
private void unbindMediaPlayerService() {
unbindService(mediaPlayerConnection);
serviceIsBound = false;
Timber.v("Service unbound");
}
//bind service so the display can follow the audio playback
private void bindMediaPlayerService() {
if (!serviceIsBound) {
Intent bindInt = new Intent(this, MediaPlayerService.class);
serviceIsBound = bindService(bindInt, mediaPlayerConnection, Context.BIND_AUTO_CREATE);
Timber.v("Service bound");
} else {
Timber.v("no Service to bind");
}
}
@Override
protected void onStart() {
super.onStart();
if (MediaPlayerService.getState() != Constants.STATE_NOT_INIT) {
bindMediaPlayerService();
}
@Override
protected void onStop() {
super.onStop();
if (MediaPlayerService.getState() != Constants.STATE_NOT_INIT) {
unbindMediaPlayerService();
}
}
}
// Binder given to Activity
private final IBinder binder = new MyBinder();
/**
* Class used for the client Binder. The Binder object is responsible for returning an instance
* of {@link MediaPlayerService} to the client.
*/
public class MyBinder extends Binder {
public MediaPlayerService getService() {
// Return this instance of MyService so clients can call public methods
return MediaPlayerService.this;
}
}
@Override
public IBinder onBind(Intent arg0) {
return binder;
}
@Override
public void onCreate() {
super.onCreate();
(…)
stateService = Constants.STATE_NOT_INIT;
notMan = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
}
@Override
public int onStartCommand(final Intent intent, int flags, int startId) {
if (intent == null || intent.getAction() == null) {
stopForeground(true);
stopSelf();
return START_NOT_STICKY;
}
switch (intent.getAction()) {
case Constants.START_ACTION:
if(intent.getExtras() != null) {
medUri = (Uri) intent.getExtras().get(Constants.URI);
}
stateService = Constants.STATE_PREPARE;
startForeground(Constants.NOTIFICATION_ID_FOREGROUND_SERVICE, prepareNotification());
destroyPlayer();
initPlayer();
play();
break;
case Constants.PAUSE_ACTION:
stateService = Constants.STATE_PAUSE;
notMan.notify(Constants.NOTIFICATION_ID_FOREGROUND_SERVICE, prepareNotification());
destroyPlayer();
handler.postDelayed(delayedShutdown, Constants.DELAY_SHUTDOWN_FOREGROUND_SERVICE);
break;
case Constants.PLAY_ACTION:
stateService = Constants.STATE_PREPARE;
notMan.notify(Constants.NOTIFICATION_ID_FOREGROUND_SERVICE, prepareNotification());
destroyPlayer();
initPlayer();
play();
break;
case Constants.STOP_ACTION:
Timber.i("Received Stop Intent");
destroyPlayer();
stopForeground(true);
stopSelf();
break;
default:
stopForeground(true);
stopSelf();
}
return START_NOT_STICKY;
}
@Override
public void onDestroy() {
Timber.d("onDestroy()");
destroyPlayer();
stateService = Constants.STATE_NOT_INIT;
try {
timerUpdateHandler.removeCallbacksAndMessages(null);
} catch (Exception e) {
e.printStackTrace();
}
super.onDestroy();
}
您可以在 GitHub 上找到完整的代码链接。
目的是让用户在收听时让他们的设备进入睡眠状态,或者让它自行进入睡眠状态。服务的绑定是必要的,以便在设备打开或活动被带到前台时显示播放进度(通过搜索栏和计数器)。
我哪里出错了或者我忽略了什么?还是有更好的方法我应该考虑?提前感谢您的任何指示和帮助。
ETA:我已经切换到未过滤的 logCat 并发现了一个新错误。我将开始粘贴保存外州且服务未绑定的位置。
2019-11-14 09:57:42.942 8326-8326/com.example.android.meditationhub V/PlayActivity: all outstates saved
2019-11-14 09:57:42.943 8326-8326/com.example.android.meditationhub V/PlayActivity: all outstates saved
2019-11-14 09:57:42.944 8326-8326/com.example.android.meditationhub V/PlayActivity: all outstates saved
2019-11-14 09:57:42.950 8326-8326/com.example.android.meditationhub V/PlayActivity: Service unbound
2019-11-14 09:57:42.951 8326-8326/com.example.android.meditationhub V/PlayActivity: Service unbound
2019-11-14 09:57:42.953 225-1957/? I/BufferQueueProducer: [ColorFade](this:0xaab2a000,id:5867,api:1,p:812,c:225) new GraphicBuffer needed
2019-11-14 09:57:42.954 225-1957/? I/[MALI][Gralloc]: usage1: 0xf02, format: 1 stride: 720 vertical_stride: 1280 size: 3686400
2019-11-14 09:57:42.954 8326-8326/com.example.android.meditationhub V/PlayActivity: Service unbound
2019-11-14 09:57:42.955 225-1957/? D/GraphicBuffer: alloc, handle(0xac1921e0) (w:720 h:1280 s:720 f:0x1 u:0x000f02) err(0)
2019-11-14 09:57:42.958 812-859/? D/GraphicBuffer: register, handle(0x88e7d160) (w:720 h:1280 s:720 f:0x1 u:0x000f02)
2019-11-14 09:57:42.979 225-369/? I/BufferQueueProducer: [ColorFade](this:0xaab2a000,id:5867,api:1,p:812,c:225) new GraphicBuffer needed
2019-11-14 09:57:42.979 225-369/? I/[MALI][Gralloc]: usage1: 0xf02, format: 1 stride: 720 vertical_stride: 1280 size: 3686400
2019-11-14 09:57:42.980 225-369/? D/GraphicBuffer: alloc, handle(0xac1922a0) (w:720 h:1280 s:720 f:0x1 u:0x000f02) err(0)
2019-11-14 09:57:42.983 812-859/? D/GraphicBuffer: register, handle(0x88e789a0) (w:720 h:1280 s:720 f:0x1 u:0x000f02)
2019-11-14 09:57:42.994 812-3148/? D/PowerManagerNotifier: onWakeLockReleased: flags=1, tag="ActivityManager-Sleep", packageName=android, ownerUid=1000, ownerPid=812, workSource=null
2019-11-14 09:57:42.995 258-748/? D/Sunwave: sw_sensor 896:send cancel sem
2019-11-14 09:57:42.995 258-747/? I/Sunwave: sw-hal 281:irq canceled!
2019-11-14 09:57:42.995 258-747/? D/Sunwave: IC8221 1237:wait irq cancel
2019-11-14 09:57:42.995 258-747/? D/Sunwave: IC8221 712:soft reset
2019-11-14 09:57:42.998 258-733/? D/Sunwave: sw_sensor 225:irq receive wake, but g_bIrqEnable = 0
2019-11-14 09:57:43.000 258-747/? D/Sunwave: IC8221 2573:warning: fp irq canceled
2019-11-14 09:57:43.005 258-747/? D/Sunwave: FpFinger 2405:keyFinger cancel:1
2019-11-14 09:57:43.006 812-859/? D/DisplayPowerController: ABC configure: enabledInDoze=false, lowDimmingProtectionEnabled=false, adjustment=0.7919922, state=2, brightness=-1
2019-11-14 09:57:43.006 812-859/? D/AutomaticBrightnessController: getAutomaticScreenBrightness: brightness=255, dozing=false, factor=1.0
2019-11-14 09:57:43.007 812-859/? D/DisplayPowerController: Unfinished business...
2019-11-14 09:57:43.023 8326-8849/com.example.android.meditationhub D/FA: Logging event (FE): user_engagement(_e), Bundle[{ga_event_origin(_o)=auto, engagement_time_msec(_et)=5780, ga_screen_class(_sc)=PlayActivity, ga_screen_id(_si)=7992583200822585152}]
2019-11-14 09:57:43.024 8326-8326/com.example.android.meditationhub E/JavaBinder: !!! FAILED BINDER TRANSACTION !!! (parcel size = 895384)
2019-11-14 09:57:43.025 8326-8326/com.example.android.meditationhub D/AndroidRuntime: Shutting down VM
2019-11-14 09:57:43.026 943-943/? D/SystemServicesProxy: getTopMostTask: tasks: 2600
--------- beginning of crash
2019-11-14 09:57:43.027 8326-8326/com.example.android.meditationhub E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.android.meditationhub, PID: 8326
java.lang.RuntimeException: android.os.TransactionTooLargeException: data parcel size 895384 bytes
at android.app.ActivityThread$StopInfo.run(ActivityThread.java:3865)
at android.os.Handler.handleCallback(Handler.java:836)
at android.os.Handler.dispatchMessage(Handler.java:103)
at android.os.Looper.loop(Looper.java:203)
at android.app.ActivityThread.main(ActivityThread.java:6251)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1063)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:924)
Caused by: android.os.TransactionTooLargeException: data parcel size 895384 bytes
at android.os.BinderProxy.transactNative(Native Method)
at android.os.BinderProxy.transact(Binder.java:622)
at android.app.ActivityManagerProxy.activityStopped(ActivityManagerNative.java:3708)
at android.app.ActivityThread$StopInfo.run(ActivityThread.java:3857)
at android.os.Handler.handleCallback(Handler.java:836)
at android.os.Handler.dispatchMessage(Handler.java:103)
at android.os.Looper.loop(Looper.java:203)
at android.app.ActivityThread.main(ActivityThread.java:6251)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1063)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:924)
2019-11-14 09:57:43.034 812-1424/? W/ActivityManager: Force finishing activity com.example.android.meditationhub/.ui.PlayActivity
“交易太大”似乎是关键,但我对什么交易感到困惑。因为我还没有在服务和活动之间进行通信。我开始怀疑timerUpdateHandler
在服务中。当设备进入睡眠状态时,我是否应该也停止通知?