我一直在研究音乐播放器应用。我正在使用一项服务来运行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次。虽然我已按照this link中建议的START_NOT_STICKY
返回onStartCommand()
来停止崩溃邮件。我想了解这里的实际问题是什么。
我的项目可以在我的GitHub上找到,如果有人想检查代码。 Music-Player-App
我在MainActivity中使用片段来处理服务。下面的代码是我在MainFragment和MediaPlayerService之间工作的地方。
MainFragment
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);
}
MediaPlayerService
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();
}
答案 0 :(得分:2)
您的代码中的任何位置都没有unbindService调用。因此,每当Activity被销毁时,系统都会检测到它仍然绑定到ServiceConnection并且已经泄露。在Fragment中调用bindService时仍然如此。由于片段不从Activity或Context继承,因此它们本身没有上下文引用,因此它们必须使用其父Activity上下文。记住在拥有组件被销毁时,总是调用unbindService,它是片段,活动,甚至是另一个服务。将服务绑定到其他服务并不是闻所未闻...... 如果您不希望在所有客户端解除绑定时销毁绑定服务,则需要添加特殊逻辑以确定服务是否应临时转换为已启动服务,以便操作系统不会将其终止,并停止服务当客户重新绑定它时。