2012-03-20 12 views
6

Uczę się, jak używać wątków w Androidzie, a do tego stworzyłem małą aplikację, która odtwarza serię notatek. Chodzi o to, że jest przycisk Start i przycisk kończący, i że (oczywiście) jeśli naciśniesz przycisk Start zacznie grać muzyka, a jeśli naciśniesz przycisk zakończenia, to się zatrzyma. Przycisk start działa dobrze, ale problem polega na tym, że przycisk zakończenia nie działa. Nie mam pojęcia, dlaczego, więc może niektórzy z was mogą mi pomóc. Jest to kod:Nic nie przeszkadza

public class PressAndPlay extends Activity { 
    private volatile Thread initBkgdThread; 

    @Override 
    public void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.main); 

     Button startButton = (Button) findViewById(R.id.trigger); 
     startButton.setOnClickListener(new View.OnClickListener() { 
      public void onClick(View view) { 

       // create thread 
       initBkgdThread = new Thread(new Runnable() { 
        public void run() { 
         play_music(); 
        } 
       }); 
       initBkgdThread.start(); 
      } 
     }); 

     Button endButton = (Button) findViewById(R.id.end); 
     endButton.setOnClickListener(new View.OnClickListener() { 
      public void onClick(View view) { 
       end_music(); 
      } 
     }); 
    } 

    int[] notes = {R.raw.c5, R.raw.b4, R.raw.a4, R.raw.g4}; 
    int NOTE_DURATION = 400; 
    MediaPlayer m_mediaPlayer; 

    private void play_music() { 
     for(int ii=0; ii<12; ii++) { 
      //check to ensure main activity is not paused 
      if(!paused) { 
       if (m_mediaPlayer != null) {m_mediaPlayer.release();} 
       m_mediaPlayer = MediaPlayer.create(this, notes[ii%4]); 
       m_mediaPlayer.start(); 
       try { 
        Thread.sleep(NOTE_DURATION); 
       } catch (InterruptedException e) { 
        e.printStackTrace(); 
       } 
      } 
     } 
    } 

    private void end_music() { 
     if(initBkgdThread != null) { 
      initBkgdThread.interrupt(); 
      initBkgdThread = null; 
     } 
    } 

    boolean paused = false; 
    @Override 
    protected void onPause() { 
     paused = true; 
     super.onPause(); 
    } 
    @Override 
    protected void onResume() { 
     super.onResume(); 
     paused = false; 
    } 
} 

Odpowiedz

7

Telefonujesz interrupt() na wątku gry, ale jest to prawdopodobnie czeka w sleep w tym czasie. Spowoduje to spanie, aby rzucić InterruptedException. Trzeba złapać tego wyjątku i wyjść z pętli do zatrzymania gry:

try { 
     Thread.sleep(NOTE_DURATION); 
    } catch (InterruptedException e) { 
     // XXX need to stop playing here, maybe return or break? 
     return; 
    } 

Od interrupt() może również pochodzić w różnym czasie, trzeba sprawdzić status przerwania i zakończyć swoją pętlę:

if (!paused && !Thread.currentThread().isInterrupted()) { 
    ... 

Ponadto wszystkie zmienne, które są wspólne dla dwóch wątków, muszą być również synchronized lub oznaczone jako volatile. paused flaga powinna prawdopodobnie volatile tutaj:

volatile boolean paused = false 

Wreszcie, dla potomności, kiedy złapiesz InterruptedException, czyści status przerwania wątku. Zwykle dobrą praktyką jest natychmiastowe ustawienie flagi przerwania na wątku, aby inni mogli przetestować:

try { 
     Thread.sleep(NOTE_DURATION); 
    } catch (InterruptedException e) { 
     // re-establish the interrupt condition 
     Thread.currentThread.interrupt(); 
     ... 
    } 
+0

Zadziałało! Dziękuję za pomoc:] – Zero

+0

Nie otrzymałem ostatniego punktu, jaka jest różnica między wykonaniem 'return;' i ponownym ustanowieniem warunku przerwania. –

+1

Gdy tylko zauważysz wyjątek "InterruptedException", status przerwania w wątku zostanie wyczyszczony, więc powinieneś ponownie przerwać wątek. Jest to ważny wzór, więc jeśli kodujesz w bibliotece, osoba dzwoniąca zostanie powiadomiona, że ​​wątek został przerwany. To tylko wzór, o którym zawsze wspominam @MuhammedRefaat. – Gray