我正在尝试编写一个录音机应用程序。整个应用程序由两个组件组成:Main Activity,它只包含一个名为 RecordFragment 的片段,以及负责录制音频的 RecordThread 类。
RecordThread 是从 HandlerThread 扩展而来的。
这个想法是用户按下一个按钮,并且附加到 RecordThread looper 的处理程序发送一条空消息以开始录制。然后线程应该处理该消息并开始录制,直到用户再次按下按钮停止录制,该处理程序再次发送另一条消息(我认为线程的消息队列)以停止录制。
问题#1:RecordThread 从不开始记录甚至处理来自队列的消息。
问题 #2:我应该如何处理 recordThread 中的消息?
记录线程.java
public class RecordThread extends HandlerThread {
private final String TAG = "RecordThread";
private final int RECORD_START = 1;
private final int RECORD_STOP = 3;
private MediaRecorder mRecorder;
private String mFilePath = null;
private String mFileName = null;
private Handler mHandler;
public RecordThread(String name) {
super(name);
}
@Override
protected void onLooperPrepared() {
super.onLooperPrepared();
mHandler = new Handler(getLooper()) {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case RECORD_START:
startRecording();
Log.d(TAG, "Recording");
break;
case RECORD_STOP:
stopRecording();
Log.d(TAG, "Stopping");
break;
}
}
};
}
// prepares the media recorder and starts recording
private void startRecording() {
seuUpFilePath();
mRecorder = new MediaRecorder();
mRecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT);
mRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
mRecorder.setOutputFile(mFilePath);
mRecorder.setAudioChannels(1);
try {
mRecorder.prepare();
mRecorder.start();
} catch (IOException e) {
Log.e(TAG, "Error while preparing Media Recorder" + e.toString());
}
}
/**
* create file's name and path for storing it on external storage
*/
private void seuUpFilePath() {
// building file's name
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd-HH:mm:ss",
new Locale("en", "IR"));
Date date = Calendar.getInstance().getTime();
String currentTime = dateFormat.format(date);
mFileName = "Record" + currentTime + ".mp4";
// creating file's path
mFilePath = Environment.getExternalStorageDirectory().getAbsolutePath();
mFilePath += "/Sound Recorder/" + mFileName;
Log.d(TAG, "FilePath: " + mFilePath);
}
private void stopRecording() {
mRecorder.stop();
mRecorder.reset();
mRecorder.release();
mRecorder = null;
}
@RequiresApi(api = Build.VERSION_CODES.N)
private void resumeRecorder() {
mRecorder.resume();
}
@RequiresApi(api = Build.VERSION_CODES.N)
private void pauseRecording() {
mRecorder.pause();
}
}
记录片段.java
public class RecordFragment extends Fragment {
private static final String TAG = "RecordFragment";
private final String THREAD_RECORDING = "recording thread";
private final int RECORD_START = 1;
private final int RECORD_STOP = 3;
int count = 1;
private FloatingActionButton mRecordFab;
private boolean isRecording = false;
private boolean paused = false;
private RecordThread mRecordThread;
private Handler mRecordHandler;
private Chronometer mChronometer;
private TextView mMessage;
private long timeWhenPaused;
public RecordFragment() {
// Required empty public constructor
}
public static RecordFragment newInstance() {
return new RecordFragment();
}
/**
* getting reference to views and setting basic attributes
*/
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_record, container, false);
mChronometer = view.findViewById(R.id.chronometer);
mMessage = view.findViewById(R.id.recording_status_tv);
mMessage.setText("Tap the button to start recording");
mRecordFab = view.findViewById(R.id.record_fab);
return view;
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mChronometer.setOnChronometerTickListener(new Chronometer.OnChronometerTickListener() {
@Override
public void onChronometerTick(Chronometer chronometer) {
switch (count) {
case 1:
mMessage.setText("Recording.");
count++;
break;
case 2:
mMessage.setText("Recording..");
count++;
break;
case 3:
mMessage.setText("Recording...");
count = 1;
break;
}
}
});
mRecordFab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// stop recording
if (isRecording) {
Log.d(TAG, "Stopping");
mRecordFab.setImageResource(R.drawable.ic_mic_white_36dp);
mChronometer.stop();
mChronometer.setBase(SystemClock.elapsedRealtime());
mMessage.setText("Tap the button to start recording");
mRecordHandler.sendEmptyMessage(RECORD_STOP);
Log.d(TAG, "thread state:" + mRecordThread.getState());
Log.d(TAG, "thread interrupted?: " + mRecordThread.isInterrupted());
Log.d(TAG, "thread is alive? " + mRecordThread.isAlive());
isRecording = false;
}
// start recording
else {
// changing views attributes
mRecordFab.setImageResource(R.drawable.ic_media_stop);
mChronometer.setBase(SystemClock.elapsedRealtime());
mChronometer.start();
isRecording = true;
Log.d(TAG, "handler succeeded? " + mRecordHandler.sendEmptyMessage(RECORD_START));
Log.d(TAG, "Recording");
Log.d(TAG, "thread interrupted?: " + mRecordThread.isInterrupted());
Log.d(TAG, "thread state:" + mRecordThread.getState());
Log.d(TAG, "thread is alive? " + mRecordThread.isAlive());
}
}
@Override
public void onResume() {
super.onResume();
mRecordThread = new RecordThread(THREAD_RECORDING);
mRecordThread.start();
mRecordHandler = new Handler(mRecordThread.getLooper());
mRecordHandler.sendEmptyMessage(RECORD_START);
}
@Override
public void onDestroy() {
super.onDestroy();
mRecordThread.quit();
}
}
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>