我试图将从麦克风获得的输入传递给扬声器(目标是能够在将来实时执行音频处理)。这是代码:
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仍在运行,这意味着无限循环也在不断运行。这样做是个好主意,也就是说,只要我的整个程序按照我的意愿行事,就让线程继续运行?如果我有很多线程或其他后台进程在运行怎么办?如果应用程序变得太大,这种做法会在将来导致内存问题吗?
答案 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();
}