【发布时间】:2018-02-09 13:05:55
【问题描述】:
我一直在开发音乐播放器应用。我正在使用服务来运行 MediaPlayer。从片段中,我使用 startService(Intent) 启动服务,然后将其绑定到我的活动。至少那是我打算做的。问题是我的应用程序在终止后尝试再次启动服务,并且由于应用程序已经终止,服务会引发异常。
E/ActivityThread: Activity com.veloxigami.myapplication.MainActivity has leaked ServiceConnection com.veloxigami.myapplication.MainFragment$1@d8b488c that was originally bound here
android.app.ServiceConnectionLeaked: Activity com.veloxigami.myapplication.MainActivity has leaked ServiceConnection com.veloxigami.myapplication.MainFragment$1@d8b488c that was originally bound here.
我的 onStartCommand() 被调用了 2 次。虽然我已经能够通过在onStartCommand() 中返回START_NOT_STICKY 来阻止崩溃消息,正如this link 中所建议的那样。我想了解这里的实际问题是什么。
如果有人想检查代码,我的项目可以在我的 GitHub 上找到。 Music-Player-App.
我在 MainActivity 中使用了一个片段来处理该服务。下面的代码是我在 MainFragment 和 MediaPlayerService 之间工作的地方。
主片段
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
MediaPlayerService.LocalBinder binder = (MediaPlayerService.LocalBinder) service;
playerService = binder.getService();
serviceBound = true;
Toast.makeText(getActivity(), "Media Player Active", Toast.LENGTH_SHORT).show();
}
@Override
public void onServiceDisconnected(ComponentName name) {
serviceBound = false;
}
};
public void playAudio(int audioIndex) {
currentFile = audioIndex;
if (!serviceBound) {
// storage = new DataStorage(getActivity());
/* storage.storeAudio(playlist);
storage.storeAudioIndex(audioIndex);*/
serviceBound = true;
Log.v("TAG", "Creating new instance");
Intent playerIntent = new Intent(getActivity(), MediaPlayerService.class);
getActivity().startService(playerIntent);
getActivity().bindService(playerIntent, serviceConnection, Context.BIND_AUTO_CREATE);
} else {
//storage = new DataStorage(getActivity());
/*storage.storeAudio(playlist);
storage.storeAudioIndex(audioIndex);*/
Intent broadcastIntent = new Intent(Broadcast_PLAY_NEW_AUDIO);
Log.v("TAG", "Broadcasting");
getActivity().sendBroadcast(broadcastIntent);
}
Intent playingBroadcast = new Intent(Broadcast_PLAY_BTN_CHANGE);
getActivity().sendBroadcast(playingBroadcast);
Intent nextPlayingBroadcastMain = new Intent(Broadcast_SONG_TEXT_CHANGE);
getActivity().sendBroadcast(nextPlayingBroadcastMain);
}
媒体播放器服务
private void initMediaPlayer(){
mediaPlayer = new MediaPlayer();
mediaPlayer.setOnBufferingUpdateListener(this);
mediaPlayer.setOnCompletionListener(this);
mediaPlayer.setOnErrorListener(this);
mediaPlayer.setOnInfoListener(this);
mediaPlayer.setOnPreparedListener(this);
mediaPlayer.setOnSeekCompleteListener(this);
mediaPlayer.reset();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
try{
mediaPlayer.setDataSource(currentMedia.getData());
currentFileIndex = MainFragment.currentFile;
MainActivity.durationText.setText(currentMedia.getDuration());
Toast.makeText(getApplicationContext(),"Playlist Size: "+MainFragment.playlist.size() +"\nSong No.: "+(currentFileIndex+1) ,Toast.LENGTH_SHORT).show();
} catch (IOException e) {
e.printStackTrace();
stopSelf();
}
mediaPlayer.prepareAsync();
}
@Override
public void onCreate() {
super.onCreate();
callStateListener();
registerAudioOutputChange();
register_playNewAudio();
registerStopMediaBroadcast();
registerUpdatePlaylistReceiver();
registerPlayButtonBroadcast();
registerPrevButtonBroadcast();
registerNextButtonBroadcast();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
try{
playList = new ArrayList<>();
playList = MainFragment.playlist;
currentMedia = MainFragment.playlist.get(MainFragment.currentFile);
}catch (NullPointerException e){
e.printStackTrace();
stopSelf();
}
if(requestAudioFocus() == false)
stopSelf();
if (currentMedia.getData() != null && currentMedia.getData() !="") {
initMediaPlayer();
}
return START_NOT_STICKY;
}
@Override
public void onDestroy() {
super.onDestroy();
if (mediaPlayer!=null){
stopMedia();
mediaPlayer.release();
}
removeAudioFocus();
if(phoneStateListener != null){
telephonyManager.listen(phoneStateListener,PhoneStateListener.LISTEN_NONE);
}
unregisterReceiver(audioOutputChange);
unregisterReceiver(playNewAudio);
unregisterReceiver(stopMediaBroadcast);
unregisterReceiver(updatePlaylistReceiver);
unregisterReceiver(playButtonBroadcast);
unregisterReceiver(prevButtonBroadcast);
unregisterReceiver(nextButtonBroadcast);
//new DataStorage(getApplicationContext()).clearCachedAudioPlaylist();
}
【问题讨论】:
-
您能否同时发布 MainActivity 和 Service 的代码...此外,服务不能多次启动。我的意思更准确地说,如果您的服务没有启动并且您调用 startService(...) 它只会调用一次 onCreate ,并且已经启动的后续调用只会调用 onStartCommand() 但不会创建新实例.放置证明这样的日志记录。如果您在服务中看到 onCreate 被多次调用,则 onDestory() 也必须被调用。
-
我已经为这两个类添加了代码。
-
在 onStartCommand()、initMediaPlayer() 和 onDestory() 的 try/catch 语句的 catch 块内放置一个日志语句。你想看看你 stopSelf 是否被调用。
-
onStartCommand 被多次调用。每次调用 startService(...) 时都会调用它,并为该服务传递一个 Intent。这并不意味着服务实际上被启动了这么多次,它只是意味着 startService 已经被调用了几次。只有导致 onCreate 被调用的第一个调用才能真正启动服务。多次调用 startService 来控制服务是与已启动服务通信的推荐方式。如果这让您感到困扰,请改为绑定到 Service 并直接调用 Service 实例上的方法。
-
当我关闭我的应用程序时,服务会尝试重新启动。这就是
leaked ServiceConnection的消息出现的时候。getActivity().startService(playerIntent); getActivity().bindService(playerIntent, serviceConnection, Context.BIND_AUTO_CREATE);如您所见,我正在绑定以及启动服务,这会导致任何问题吗?
标签: android android-service audio-player android-music-player