Search code examples
androidservicebind

How to unBind MusicService


I am new in android, I'm working on a game app and I want to play Music in background, the music has to be played in all activitys. In addition, I am taking the songs from the user's device and present them in a ListView. The user chooses a song in the ListMusic Activity, then he go on to others Activity and my purpose is that the music will continue in the background.

I hope you understood the process, I added the JAVA also.

Now, I've got this error:

Activity com.example.summerproject.FirstActivity has leaked ServiceConnection com.example.summerproject.ListMusic$1@42116d20 that was originally bound here

From what I understood, the problem is that I am not UnBind the connection on onDestroy() of each activity. Someone know how to fix it? I want the music to continue over all the activitys so it might be a problem to unBind the Service each time.

MusicService:

   public class MusicService extends Service implements
   MediaPlayer.OnPreparedListener, MediaPlayer.OnErrorListener,
   MediaPlayer.OnCompletionListener
{
     private MediaPlayer player; //media player
     private ArrayList<Song> valuesList; //songs List
     private int songPosn;  //current position
     private final IBinder musicBind = new MusicBinder();
     
    public void onCreate()
     {
        
        super.onCreate(); 
        songPosn=0;
        player= new MediaPlayer();
        initMusicPlayer();
        
        player.setOnPreparedListener(this);
        player.setOnCompletionListener(this);
        player.setOnErrorListener(this);
     }

    @Override
    public IBinder onBind(Intent intent) 
    {
        //בעת התחברות 
        return musicBind;
        
    }
    
    @Override
    public boolean onUnbind(Intent intent)
    {
      player.stop();
      player.release();
      return false;
    }
    
    public void initMusicPlayer() 
    {
          //set player properties
        player.setWakeMode(getApplicationContext(),
                PowerManager.PARTIAL_WAKE_LOCK);
                player.setAudioStreamType(AudioManager.STREAM_MUSIC);
    }
    
    public void playSong()
    {
         if(player != null) //אם נוצר כבר
            player.reset();
         
        Song songToPlay = valuesList.get(songPosn);
        long songId = songToPlay.getId();
        
        Uri trackUri = ContentUris.withAppendedId(
          android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
          songId);
        
        try{
              player.setDataSource(getApplicationContext(), trackUri);
            }
            catch(Exception e)
            {
              Log.e("MUSIC SERVICE", "Error setting data source", e);
            }
        player.setLooping(true);
        player.prepareAsync();
          
    }
    public void setList(ArrayList<Song> theSongs){
          valuesList=theSongs;
        }
    
    public class MusicBinder extends Binder 
    {
          MusicService getService() 
          {
            return MusicService.this;
          }
    }
    
    public void setSong(int songIndex)
    {
          songPosn=songIndex;
    }

    @Override
    public void onPrepared(MediaPlayer mp) 
    {
        // TODO Auto-generated method stub
         mp.start();        
    }

    @Override
    public void onCompletion(MediaPlayer mp) {
        // TODO Auto-generated method stub
        
    }

    @Override
    public boolean onError(MediaPlayer mp, int what, int extra) {
        // TODO Auto-generated method stub
        return false;
    }
    


}

ListActivity( the user chooses songs from here):

 public class ListMusic extends Activity implements OnItemClickListener
 {

  private static ArrayList<Song> valuesList;
private ListView list;
private MySongsAdapter songAdt;
private static MusicService MusicService;
private Intent playIntent;
private static boolean musicBound=false;

@Override
protected void onCreate(Bundle savedInstanceState)
{
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_music);
    
    list = (ListView)findViewById(R.id.song_list);
    valuesList = new ArrayList<Song>();
    
    getSongList(); //update the valuesList
    
    songAdt = new MySongsAdapter(this, valuesList);
    list.setAdapter(songAdt);

    list.setOnItemClickListener(this);


}

//connect to the service

public static ServiceConnection musicConnection= new ServiceConnection()
{
 
  @Override
  public void onServiceConnected(ComponentName name, IBinder service) 
  {
    MusicBinder binder = (MusicBinder)service;
    //get service
    MusicService = binder.getService();
//  Toast.makeText(getApplicationContext(), "Music Service Is Not Null", Toast.LENGTH_LONG).show();
    //pass list
    MusicService.setList(valuesList);
    musicBound = true;
  }
 
  @Override
  public void onServiceDisconnected(ComponentName name) {
    musicBound = false;
  }
};      
@Override
protected void onStart() 
{
    //
  super.onStart();
  Toast.makeText(getApplicationContext(), "Start", Toast.LENGTH_LONG).show();
  
    
  if(playIntent ==null)
  {
    playIntent = new Intent(this, MusicService.class);
    bindService(playIntent, musicConnection, Context.BIND_AUTO_CREATE);
    startService(playIntent);
  }
  
}

public void songPicked(View view, int position)
{
    //כאשר שיר נבחר
    MusicService.setSong(position);
    MusicService.playSong();
}

public void getSongList() 
{ 
    //אחזור רשימת השירים מהמכשיר
  ContentResolver musicResolver = getContentResolver();
  Uri musicUri = android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
  Cursor musicCursor = musicResolver.query(musicUri, null, null, null, null);
  
  long thisId;
  int titleColumn, idColumn, artistColumn;
  String thisTitle, thisArtist;
  
  if(musicCursor!=null && musicCursor.moveToFirst())
  {
      //get columns
      titleColumn = musicCursor.getColumnIndex
        (android.provider.MediaStore.Audio.Media.TITLE);
      idColumn = musicCursor.getColumnIndex
        (android.provider.MediaStore.Audio.Media._ID);
      artistColumn = musicCursor.getColumnIndex
        (android.provider.MediaStore.Audio.Media.ARTIST);
      //add songs to list
      
      while (musicCursor.moveToNext())
      {
        thisId = musicCursor.getLong(idColumn);
        thisTitle = musicCursor.getString(titleColumn);
        thisArtist = musicCursor.getString(artistColumn);
        valuesList.add(new Song(thisId, thisTitle, thisArtist));
      }

    }
  
}

@Override
public void onItemClick(AdapterView<?> parent, View view, int position,
        long id)
{
     //באירוע של לחיצה על שיר
    songPicked(view, position);
    
}

@Override
public boolean onCreateOptionsMenu(Menu menu) 
{
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.music, menu);
    return true;
}

@Override
public boolean onOptionsItemSelected (MenuItem item) 
{
    // Handle action bar item clicks here. The action bar will
    // automatically handle clicks on the Home/Up button, so long
    // as you specify a parent activity in AndroidManifest.xml.

        int id= item.getItemId();
        switch(id)
        {
        case R.id.btnExit:
              stopService(playIntent);
              MusicService=null;
            finish();
              break;

        }
        return true;
    
}

The connection to MainActivity:

   playIntent = new Intent(this, MusicService.class);
    bindService(playIntent, ListMusic.musicConnection,    Context.BIND_AUTO_CREATE);
       startService(playIntent);

Please, if anyone has an idea what is the problem, I would be grateful!


Solution

  • I want the music to continue over all the activitys so it might be a problem to unBind the Service each time.

    This is only a problem because you do this

    @Override
    public boolean onUnbind(Intent intent)
    {
         player.stop();
         player.release();
         return false;
    }
    

    Now, every time something calls unbindService(), the music stops. It is fine to call unbindService() in an activity's onDestroy() or onPause(), but then you should not stop the player in the onUnbind() method in the service.

    You could give the service a public static method that you can call when you ned to stop the music instead:

    @Override
    public boolean onUnbind(Intent intent)
    {
         return false;
    }
    
    public static void stopPlayMusic()
    {
         player.stop();
         player.release();
    }
    

    This way the music will keep on playing even if something unbinds from your service, while still having a way to stop the music from playing. To make this work, you have to make the player a static variable as well.