0

Je suis en train de construire une application de lecteur de musique android comme décrit dans github.com/spragucm/MusicPlayerE/WindowManager: Erreur android.view.WindowLeaked

Alors que je suis capable de jouer de la musique, sur en cliquant sur l'icône du lecteur à partir du bac de notification, je suis erreur

E/WindowManager: android.view.WindowLeaked: .MainActivity has leaked window com.android.internal.policy.impl.PhoneWindow$DecorView. 

L'erreur est levée lors de l'appel de MediaController.show() après avoir appelé le lecteur à partir de la zone de notification.

Je passais beaucoup de temps à parcourir différentes questions similaires, mais rien ne correspond à mon scénario. Toute aide est très appréciée. Je vous remercie.

Activité principale:

public class MainActivity extends Activity implements MediaPlayerControl { 

    //song list variables 
    private ArrayList<Song> songList; 
    private ListView songView; 

    //service 
    private MusicService musicSrv; 
    private Intent playIntent; 
    //binding 
    private boolean musicBound=false; 

    //controller 
    private MusicController controller; 

    //activity and playback pause flags 
    private boolean paused=false, playbackPaused=false; 

    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.activity_main); 

     //retrieve list view 
     songView = (ListView)findViewById(R.id.song_list); 
     //instantiate list 
     songList = new ArrayList<Song>(); 
     //get songs from device 
     getSongList(); 
     //sort alphabetically by title 
     Collections.sort(songList, new Comparator<Song>(){ 
      public int compare(Song a, Song b){ 
       return a.getTitle().compareTo(b.getTitle()); 
      } 
     }); 
     //create and set adapter 
     SongAdapter songAdt = new SongAdapter(this, songList); 
     songView.setAdapter(songAdt); 

     //setup controller 
     setController(); 
    } 

    //connect to the service 
    private ServiceConnection musicConnection = new ServiceConnection(){ 

     @Override 
     public void onServiceConnected(ComponentName name, IBinder service) { 
      MusicBinder binder = (MusicBinder)service; 
      //get service 
      musicSrv = binder.getService(); 
      //pass list 
      musicSrv.setList(songList); 
      musicBound = true; 
     } 

     @Override 
     public void onServiceDisconnected(ComponentName name) { 
      musicBound = false; 
     } 
    }; 

    //start and bind the service when the activity starts 
    @Override 
    protected void onStart() { 
     super.onStart(); 
     if(playIntent==null){ 
      playIntent = new Intent(this, MusicService.class); 
      bindService(playIntent, musicConnection, Context.BIND_AUTO_CREATE); 
      startService(playIntent); 
     } 
    } 

    //user song select 
    public void songPicked(View view){ 
     musicSrv.setSong(Integer.parseInt(view.getTag().toString())); 
     musicSrv.playSong(); 
     if(playbackPaused){ 
      setController(); 
      playbackPaused=false; 
     } 
     controller.show(0); 
    } 

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

    @Override 
    public boolean onOptionsItemSelected(MenuItem item) { 
     //menu item selected 
     switch (item.getItemId()) { 
     case R.id.action_shuffle: 
      musicSrv.setShuffle(); 
      break; 
     case R.id.action_end: 
      stopService(playIntent); 
      musicSrv=null; 
      System.exit(0); 
      break; 
     } 
     return super.onOptionsItemSelected(item); 
    } 

    //method to retrieve song info from device 
    public void getSongList(){ 
     //query external audio 
     ContentResolver musicResolver = getContentResolver(); 
     Uri musicUri = android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; 
     Cursor musicCursor = musicResolver.query(musicUri, null, null, null, null); 
     //iterate over results if valid 
     if(musicCursor!=null && musicCursor.moveToFirst()){ 
      //get columns 
      int titleColumn = musicCursor.getColumnIndex 
        (android.provider.MediaStore.Audio.Media.TITLE); 
      int idColumn = musicCursor.getColumnIndex 
        (android.provider.MediaStore.Audio.Media._ID); 
      int artistColumn = musicCursor.getColumnIndex 
        (android.provider.MediaStore.Audio.Media.ARTIST); 
      //add songs to list 
      do { 
       long thisId = musicCursor.getLong(idColumn); 
       String thisTitle = musicCursor.getString(titleColumn); 
       String thisArtist = musicCursor.getString(artistColumn); 
       songList.add(new Song(thisId, thisTitle, thisArtist)); 
      } 
      while (musicCursor.moveToNext()); 
     } 
    } 

    //set the controller up 
    private void setController(){ 
     controller = new MusicController(this); 
     //set previous and next button listeners 
     controller.setPrevNextListeners(new View.OnClickListener() { 
      @Override 
      public void onClick(View v) { 
       playNext(); 
      } 
     }, new View.OnClickListener() { 
      @Override 
      public void onClick(View v) { 
       playPrev(); 
      } 
     }); 
     //set and show 
     controller.setMediaPlayer(this); 
     controller.setAnchorView(findViewById(R.id.song_list)); 
     controller.setEnabled(true); 
    } 

    private void playNext(){ 
     musicSrv.playNext(); 
     if(playbackPaused){ 
      setController(); 
      playbackPaused=false; 
     } 
     controller.show(0); 
    } 

    private void playPrev(){ 
     musicSrv.playPrev(); 
     if(playbackPaused){ 
      setController(); 
      playbackPaused=false; 
     } 
     controller.show(0); 
    } 

    @Override 
    protected void onResume(){ 
     super.onResume(); 
     if(paused){ 
      setController(); 
      paused=false; 
     } 
    } 

    @Override 
    protected void onDestroy() { 
     stopService(playIntent); 
     musicSrv=null; 
     super.onDestroy(); 
    } 
} 

contrôleur Musique:

public class MusicController extends MediaController { 

    public MusicController(Context c){ 
     super(c); 
    } 

    public void hide(){} 

} 

Service Musique:

public class MusicService extends Service implements 
MediaPlayer.OnPreparedListener, MediaPlayer.OnErrorListener, 
MediaPlayer.OnCompletionListener { 

    //media player 
    private MediaPlayer player; 
    //song list 
    private ArrayList<Song> songs; 
    //current position 
    private int songPosn; 
    //binder 
    private final IBinder musicBind = new MusicBinder(); 
    //title of current song 
    private String songTitle=""; 
    //notification id 
    private static final int NOTIFY_ID=1; 
    //shuffle flag and random 
    private boolean shuffle=false; 
    private Random rand; 

    public void onCreate(){ 
     //create the service 
     super.onCreate(); 
     //initialize position 
     songPosn=0; 
     //random 
     rand=new Random(); 
     //create player 
     player = new MediaPlayer(); 
     //initialize 
     initMusicPlayer(); 
    } 

    public void initMusicPlayer(){ 
     //set player properties 
     player.setWakeMode(getApplicationContext(), 
       PowerManager.PARTIAL_WAKE_LOCK); 
     player.setAudioStreamType(AudioManager.STREAM_MUSIC); 
     //set listeners 
     player.setOnPreparedListener(this); 
     player.setOnCompletionListener(this); 
     player.setOnErrorListener(this); 
    } 

    //pass song list 
    public void setList(ArrayList<Song> theSongs){ 
     songs=theSongs; 
    } 

    //binder 
    public class MusicBinder extends Binder { 
     MusicService getService() { 
      return MusicService.this; 
     } 
    } 

    //activity will bind to service 
    @Override 
    public IBinder onBind(Intent intent) { 
     return musicBind; 
    } 

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

    //play a song 
    public void playSong(){ 
     //play 
     player.reset(); 
     //get song 
     Song playSong = songs.get(songPosn); 
     //get title 
     songTitle=playSong.getTitle(); 
     //get id 
     long currSong = playSong.getID(); 
     //set uri 
     Uri trackUri = ContentUris.withAppendedId(
       android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, 
       currSong); 
     //set the data source 
     try{ 
      player.setDataSource(getApplicationContext(), trackUri); 
     } 
     catch(Exception e){ 
      Log.e("MUSIC SERVICE", "Error setting data source", e); 
     } 
     player.prepareAsync(); 
    } 

    //set the song 
    public void setSong(int songIndex){ 
     songPosn=songIndex; 
    } 

    @Override 
    public void onCompletion(MediaPlayer mp) { 
     //check if playback has reached the end of a track 
     if(player.getCurrentPosition()>0){ 
      mp.reset(); 
      playNext(); 
     } 
    } 

    @Override 
    public boolean onError(MediaPlayer mp, int what, int extra) { 
     Log.v("MUSIC PLAYER", "Playback Error"); 
     mp.reset(); 
     return false; 
    } 

    @Override 
    public void onPrepared(MediaPlayer mp) { 
     //start playback 
     mp.start(); 
     //notification 
     Intent notIntent = new Intent(this, MainActivity.class); 
     notIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); 
     PendingIntent pendInt = PendingIntent.getActivity(this, 0, 
       notIntent, PendingIntent.FLAG_UPDATE_CURRENT); 

     NotificationCompat.Builder builder = new NotificationCompat.Builder(this); 

     builder.setContentIntent(pendInt) 
     .setSmallIcon(R.drawable.play) 
     .setTicker(songTitle) 
     .setOngoing(true) 
     .setContentTitle("Playing") 
     .setContentText(songTitle); 
     Notification not = builder.build(); 
     startForeground(NOTIFY_ID, not); 
    } 

    //playback methods 
    public int getPosn(){ 
     return player.getCurrentPosition(); 
    } 

    public int getDur(){ 
     return player.getDuration(); 
    } 

    public boolean isPng(){ 
     return player.isPlaying(); 
    } 

    public void pausePlayer(){ 
     player.pause(); 
    } 

    public void seek(int posn){ 
     player.seekTo(posn); 
    } 

    public void go(){ 
     player.start(); 
    } 

    //skip to previous track 
    public void playPrev(){ 
     songPosn--; 
     if(songPosn<0) songPosn=songs.size()-1; 
     playSong(); 
    } 

    //skip to next 
    public void playNext(){ 
     if(shuffle){ 
      int newSong = songPosn; 
      while(newSong==songPosn){ 
       newSong=rand.nextInt(songs.size()); 
      } 
      songPosn=newSong; 
     } 
     else{ 
      songPosn++; 
      if(songPosn>=songs.size()) songPosn=0; 
     } 
     playSong(); 
    } 

    @Override 
    public void onDestroy() { 
     stopForeground(true); 
    } 

    //toggle shuffle 
    public void setShuffle(){ 
     if(shuffle) shuffle=false; 
     else shuffle=true; 
    } 
} 
+0

Afficher les codes connexes. – fluffyBatman

+0

Piste d'où vous fuyez classe MainActivity'. – azizbekian

+0

@azizbekian pourquoi avez-vous suggéré ainsi? – Adi

Répondre

0

Votre classe MusicController est un candidat gentil de votre fuite MainActivity. Regardez cette ligne:

controller.setMediaPlayer(this); 

Que va-t-il se passer après un changement d'orientation? Une nouvelle activité est créée, mais votre contrôleur peut toujours référencer l'activité précédente et ne laissera pas le garbage collector supprimer l'activité précédente du tas, ce qui peut entraîner une fuite de mémoire. Pour être sûr de mon hypothèse, vous pouvez installer Leak Canary, qui vous indiquera la cause de la fuite.

Voir aussi this post pour d'autres types de fuites de mémoire dans Android et comment les surmonter.