1

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

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() 函数对我的应用程序到底做了什么,以及它是否是停止线程的最佳方法我开始我自己。

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

4

4 回答 4

4

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

于 2013-08-20T03:02:53.147 回答
1

super.onDestroy请记住释放内存或完成线程后调用。否则它会抛出Exception

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

希望这可以帮助。

于 2013-08-20T04:30:24.807 回答
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().

于 2013-08-20T05:43:19.017 回答
0

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

private class thrd extends Thread {

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

@Override
protected void onDestroy() {
    super.onDestroy();
    thrd.interupt();
}
于 2013-08-20T06:12:26.463 回答