什么是在android中停止线程的好方法

时间:2013-08-20 02:55:22

标签: java android

我试图将从麦克风获得的输入传递给扬声器(目标是能够在将来实时执行音频处理)。这是代码:

public class MainActivity extends Activity {
    AudioManager am = null;
    AudioRecord record =null;
    AudioTrack track =null;
    final int SAMPLE_FREQUENCY = 44100;
    final int SIZE_OF_RECORD_ARRAY = 1024;  // 1024 ORIGINAL
    final int WAV_SAMPLE_MULTIPLICATION_FACTOR = 1;
    int i= 0;
    boolean isPlaying = true;
    class MyThread extends Thread{
        @Override
        public void run(){
            recordAndPlay();
        }
    }

    MyThread newThread;

    private void init() {
        int min = AudioRecord.getMinBufferSize(SAMPLE_FREQUENCY, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT);
        record = new AudioRecord(MediaRecorder.AudioSource.VOICE_COMMUNICATION, SAMPLE_FREQUENCY, AudioFormat.CHANNEL_IN_MONO,
                                 AudioFormat.ENCODING_PCM_16BIT, min);
        int maxJitter = AudioTrack.getMinBufferSize(SAMPLE_FREQUENCY, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT);
        track = new AudioTrack(AudioManager.MODE_IN_COMMUNICATION, SAMPLE_FREQUENCY, AudioFormat.CHANNEL_OUT_MONO,
                               AudioFormat.ENCODING_PCM_16BIT, maxJitter, AudioTrack.MODE_STREAM);
     }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        setVolumeControlStream(AudioManager.MODE_IN_COMMUNICATION);
        init();
        newThread = new MyThread();
        newThread.start();
    }

    @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;
    }

    private void recordAndPlay() {
        short[] lin = new short[SIZE_OF_RECORD_ARRAY];
        int num = 0;
        am = (AudioManager) this.getSystemService(Context.AUDIO_SERVICE);
        am.setMode(AudioManager.MODE_IN_COMMUNICATION);
        record.startRecording();
        track.play();
        while (true) {
            num = record.read(lin, 0, SIZE_OF_RECORD_ARRAY);
            for(i=0;i<lin.length;i++)
                lin[i] *= WAV_SAMPLE_MULTIPLICATION_FACTOR; 
            track.write(lin, 0, num);
        }
    }

    public void passStop(View view){
        Button playBtn = (Button) findViewById(R.id.playBtn);  
        // /*
        if(!isPlaying){
            record.startRecording();
            track.play();
            isPlaying = true;
            playBtn.setText("Pause");
        }
        else{
           record.stop();
           track.pause();
           isPlaying=false;
           playBtn.setText("Pass through");
        }
        // */
    }

/*
    @SuppressWarnings("deprecation")
    @Override
    public void onDestroy(){
        newThread.stop();
    }
    */

    @Override
    protected void onDestroy() {
        android.os.Process.killProcess(android.os.Process.myPid());
        // killProcess(android.os.Process.myPid());
    }
}  

简要概述:
while(true) {}函数中的recordAndPlay()无限循环连续从麦克风读取原始音频样本,并将原始样本输出到扬声器。从recordAndPlay()函数中启动的线程调用onCreate()。因此,一旦程序启动,它就开始将麦克风上的输入发送到扬声器(实际上在几秒钟之后实际上滞后但我认为这种延迟是不可避免的)。我还有一个按钮,可以暂停和恢复此传递。现在,如果线程未停止,即使我退出应用程序或应用程序失去焦点,传递也会继续(因此即使手机在桌面上,它也会继续进行直通)。

@SuppressWarnings("deprecation")
@Override
public void onDestroy(){
    newThread.stop();
}  

此代码会导致应用在退出时崩溃(为什么?),所以我使用了

@Override
protected void onDestroy() {
    android.os.Process.killProcess(android.os.Process.myPid());
    // killProcess(android.os.Process.myPid());
}  

我在stackoverflow中找到了某个地方(我忘了哪里)。它似乎做了我现在想要的,但我想知道这是否是正确的方法来停止线程。它做我需要的,也就是说,当我退出应用程序时,它会停止直通,但我不确定killProcess()函数究竟对我的整个应用程序做了什么,如果它是阻止线程的最佳方式我我自己开始了

此外,如果我在暂停直通时退出我的应用程序(或松散焦点),我可以获得相同的效果。但我认为这意味着Thread仍在运行,这意味着无限循环也在不断运行。这样做是个好主意,也就是说,只要我的整个程序按照我的意愿行事,就让线程继续运行?如果我有很多线程或其他后台进程在运行怎么办?如果应用程序变得太大,这种做法会在将来导致内存问题吗?

4 个答案:

答案 0 :(得分:4)

线程应定期检查其循环中的某些shouldTerminate标志,然后从UI线程中设置此标志并(可选)等待,直到线程正常终止。不要忘记volatile或正确的字段同步。

答案 1 :(得分:1)

请记得在释放记忆或完成线程后致电super.onDestroy。否则它将抛出Exception

@Override
protected void onDestroy() {
    // You code here to finish the thread
    super.onDestroy(); // Please call THIS too
}

希望这有帮助。

答案 2 :(得分:1)

将Thread类更改为以下内容:

class MyThread extends Thread {

    private volatile boolean finished = false;

    @Override
    public void run() {

        while (!finished) {
            // do stuff on thread
        }

    }

    public void stopThread() {
        finished = true;
    }

}

在你的onDestroy方法中调用stopThread()。

@Override
protected void onDestroy() {
    newThread.stopThread();
    super.onDestroy();
}

如果您愿意,还可以使用此方法等待线程停止:

    private void joinThread(Thread thread) {
        boolean retry = true;
        while (retry) {
            try {
                thread.join();
                retry = false;
            } catch (InterruptedException e) {
                // to be handled as you wish
            }
        }
    }

将此方法放入您的活动中,并在newThread.stopThread()之后调用它。

答案 3 :(得分:0)

已经提供了中断标志。将您的while循环更正为以下内容。然后调用interrupt();在onDestroy或任何地方。

private class thrd extends Thread {

    @Override
    public void run() {
        super.run();
        while (!isInterrupted()) {
            //TODO
        }
    }
}

@Override
protected void onDestroy() {
    super.onDestroy();
    thrd.interupt();
}