0

I'm trying to write a sound recorder app. The whole app consists of two components: Main Activity which only contains a fragment called RecordFragment, and RecordThread class in charge of recording the audio.

RecordThread is extended from HandlerThread.

The idea is that user presses a button and a handler which is attached to RecordThread looper sends an empty message to start recording. Then the thread is supposed to handle that message and start recording until the user presses the button again to stop the recording, which again handler sends another message (I think thread's message queue) to stop recording.

Problem #1: The RecordThread never starts recording or even handling the messages from queue.

Problem #2: How I'm suppose to handle messages in recordThread?

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();
    }


}

RecordFragment.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>

Soroush
  • 93
  • 1
  • 9
  • you have two `Handler`s: `mHandler` and `mRecordHandler` - thats why it will never work - see [this](https://stackoverflow.com/a/25096981/2252830) for some sample code – pskink Dec 10 '17 at 22:47
  • @pskink can you explain a little more? I'm confused. You mean I have to delete the handler in HandlerThread class? Then how I'm supposed to use methods of RecordThread methods? – Soroush Dec 11 '17 at 04:14
  • just call `h = new Handler(ht.getLooper()) { @Override public void handleMessage(Message msg) { ...` and handle there each message sent via `h.sendEmptyMessage(....)` – pskink Dec 11 '17 at 08:41
  • Thanks for your reply @pskink. you mean I should create RecordThread Handler in fragment and override 'handleMessage' there? If true, how I'm supposed to access the methods of RecordThead class? How I'm supposed to access its fields? – Soroush Dec 11 '17 at 14:01
  • you dont need any class that `extends HandlerThread` - all you need is a class that `extends Handler` or `implaements Handler.Callback` – pskink Dec 11 '17 at 17:57

0 Answers0