【问题标题】:Android Service starting 2 times because of BindService and StartService由于 BindService 和 StartService,Android 服务启动 2 次
【发布时间】: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


【解决方案1】:

您的代码中没有任何 unbindService 调用。因此,每当 Activity 被销毁时,系统都会检测到它仍然绑定到 ServiceConnection 并且已被泄露。在 Fragment 中调用 bindService 时仍然如此。由于片段不继承自 Activity 或 Context,因此它们本身没有上下文引用,因此它们必须使用其父活动上下文。记住在销毁拥有的组件时始终调用 unbindService,无论它是 Fragment、Activity 还是另一个 Service。服务绑定到另一个服务并非闻所未闻。

如果您不希望在所有客户端解除绑定时销毁绑定的服务,则需要添加特殊逻辑来确定该服务是否应临时转换为已启动的服务,以免被操作系统杀死,并且当客户端重新绑定到服务时停止服务。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-12
    • 1970-01-01
    • 2011-09-17
    • 1970-01-01
    • 2013-06-14
    • 1970-01-01
    相关资源
    最近更新 更多