Android UI线程和子线程使用synchronized,UI线程阻塞但无法获取锁
使用AudioRecord录音时,需要在UI线程的StopRecord方法、数据读取线程的while代码块之间加上锁,我使用了synchronized(byte[])。 但是按下停止录音按钮之后,StopRecord被阻塞了,while代码块却仍然在循环执行。
下面是抽象出来的简单代码,可以直接运行,同样出现了UI线程阻塞但无法获取锁的结果:
public class MainActivity extends ActionBarActivity implements View.OnClickListener{
final byte[] lock = new byte[0];
boolean isRecording = false;
RecorderThread recorderThread;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button buttonStart = (Button)findViewById(R.id.button_start);
Button buttonStop = (Button)findViewById(R.id.button_stop);
buttonStart.setOnClickListener(this);
buttonStop.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.button_start:
if(!isRecording){
// Initialize AudioRecord and startRecording
isRecording = true;
recorderThread = new RecorderThread();
recorderThread.start();
}
break;
case R.id.button_stop:
if(isRecording){
synchronized (lock){
// Stop AudioRecord and release
isRecording = false;
}
}
break;
}
}
private class RecorderThread extends Thread{
@Override
public void run() {
while (isRecording){
synchronized (lock){
try{
// AudioRecord.read()
Thread.sleep(200);
Log.d("TAG","read a sample data");
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
}
}
}
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:id="@+id/button_start"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="Start"/>
<Button
android:id="@+id/button_stop"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="Stop"/>
</LinearLayout>
Reborn伪
9 years, 10 months ago
Answers
第一,阻塞UI线程永远都不是一个好的选择。
第二,完全看不出你的程序需要同步的点。完全可以写成这样:
public class MainActivity extends ActionBarActivity implements View.OnClickListener{
final byte[] lock = new byte[0];
volatile boolean isRecording = false;//关于volatile关键字看下面的链接
RecorderThread recorderThread;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button buttonStart = (Button)findViewById(R.id.button_start);
Button buttonStop = (Button)findViewById(R.id.button_stop);
buttonStart.setOnClickListener(this);
buttonStop.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.button_start:
if(!isRecording){
// Initialize AudioRecord and startRecording
isRecording = true;
recorderThread = new RecorderThread();
recorderThread.start();
}
break;
case R.id.button_stop:
if(isRecording){
// Stop AudioRecord and release
isRecording = false;
}
break;
}
}
private class RecorderThread extends Thread{
@Override
public void run() {
while (isRecording){
try{
// AudioRecord.read()
Thread.sleep(200);
Log.d("TAG","read a sample data");
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
}
}
第三, Android中有个MediaRecorder类,你只需要start和stop,android会自动把它放到后台去运行,不用自己写线程啊。
PS:java volatile关键字: http://tutorials.jenkov.com/java-concurrency/volatile.html
追加:
如果题主一定要同步的话,可以这样写(本着不阻塞UI线程的原则):
public class MainActivity extends ActionBarActivity implements View.OnClickListener{
RecorderHelper recorderHelper = new RecorderHelper();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button buttonStart = (Button)findViewById(R.id.button_start);
Button buttonStop = (Button)findViewById(R.id.button_stop);
buttonStart.setOnClickListener(this);
buttonStop.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.button_start:
new Thread(new Runnable(){
public void run(){
recorderHelper.startRecord();
}
}).start();
break;
case R.id.button_stop:
new Thread(new Runnable(){
public void run(){
recorderHelper.stopRecord();
}
}).start();
break;
}
}
private class RecorderHelper {
final byte[] lock = new byte[0];
volatile boolean isRecording = false;
AudioRecorder recorder;
public RecorderHelper(){
//没有过AudioRecorder,这里就是初始化
recorder = new AudioRecorder();
}
public void startRecord(){
isRecording = true;
while(isRecording){
synchronized(lock){
if(recorder == null){
return;
}
//do something
}
}
}
public void stopRecord(){
isRecording = false;
synchronized(lock){
//release
recorder = null;
}
}
}
}
最后再说一下volatile,因为线程有的时候会cache一份变量,一个线程中更新变量以后,另一个线程可能还在用自己cache的那一份,列如上面代码中的isRecording。为了让变量的改变及时通知到每个线程,就用volatile关键字,保证每个线程都从内存中读,不会有自己的缓存。
bigger
answered 9 years, 10 months ago