0

I wrote an android program that detects if the room is noisy or quite. But whenever I rotate the phone , the text message and image won't get updated any more and the noise decibel is printed - infinity and its correct value. I think that the every time I rotate the phone a new thread being created. Because amplitudeDb amount is being printed one more time with a value of - infinity if I rotate my phone. How Can I prevent this from happening? How can I keep the initial thread running without a new thread being generated each time I rotate the phone?

here is my code

public class EnvironmentalNoise extends AppCompatActivity {

Context mContext;
private MediaRecorder mRecorder = null;
double soundLevel;
SoundMeter sm;
ImageView noiseImage;
TextView noiseTv;
double amplitudeDb;
boolean mediaRecorderExist;
boolean isTreadRunning;


@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_environmental_noise);
    noiseImage = findViewById(R.id.noiseImage);
    noiseTv = findViewById(R.id.noiseTv);
    mContext = getApplicationContext();
    PackageManager PM= this.getPackageManager();
    sm = new SoundMeter();
    final boolean microphone = PM.hasSystemFeature(PackageManager.FEATURE_MICROPHONE);
    amplitudeDb =0;
    mediaRecorderExist = false;
    isTreadRunning =false;
    if (microphone){
        sm.start();
    }
    else{
        noiseTv.setText("Sorry !!! This device is not equipped to microphone to detect environmental noise");
    }

}


@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    Log.d("SaveState", "onSaveInstanceState called");

    //save current amplitudeDb value in bundle key - value
    outState.putDouble("SAVED_STATE_COUNT_KEY", amplitudeDb);
    outState.putBoolean("MEDIA_RECORDER_EXIST",mediaRecorderExist);
}

@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
    super.onRestoreInstanceState(savedInstanceState);
    Log.d("SaveState", "onRestoreInstanceState called");

    //retrieve current counter value from bundle based on key
    int retrievedNoiseDecibel = savedInstanceState.getInt("SAVED_STATE_COUNT_KEY");
    boolean retrievedMediaRecorder = savedInstanceState.getBoolean("MEDIA_RECORDER_EXIST");
    if(retrievedMediaRecorder){
        retrievedMediaRecorder = false;
    }

    //update text view
    if(retrievedNoiseDecibel>60){
        noiseTv.setText("This room is noisy!!!");
        noiseImage.setBackgroundResource(R.drawable.noise);
    }
    else{
        noiseTv.setText("This room is quiet!!!");
        noiseImage.setBackgroundResource(R.drawable.quiet);
    }


    Log.d("SaveState", "retrieved counter value:" + retrievedNoiseDecibel);
    //Toast.makeText(this, "retrieved counter value:" + retrievedCounter, Toast.LENGTH_LONG).show();

    amplitudeDb = retrievedNoiseDecibel; //update total number of clicks
    mediaRecorderExist = retrievedMediaRecorder;
}


//start of refrencing
// found this piece of code from here : https://stackoverflow.com/questions/31305163/getmaxamplitude-always-returns-0
public class SoundMeter {

    private MediaRecorder mRecorder = null;

    public void start() {
        if(mediaRecorderExist){
            mRecorder.stop();
            mRecorder.release();
            mRecorder = null;
        }
        if (mRecorder == null) {
            mRecorder = new MediaRecorder();
            mediaRecorderExist =true;
            mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
            mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
            mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
            Timer timer = new Timer();
            //timer.schedule(new EnvironmentalNoise.RecorderTask(mRecorder), 0);
            timer.scheduleAtFixedRate(new RecorderTask(mRecorder), 0, 500);
            mRecorder.setOutputFile("/dev/null");
            try {
                mRecorder.prepare();
                mRecorder.start();
            } catch (IOException e) {
                e.printStackTrace();
            }
            catch (RuntimeException e){
                e.printStackTrace();
            }
        }
    }

private class RecorderTask extends TimerTask {
    //TextView sound = (TextView) findViewById(R.id.decibel);
    private MediaRecorder recorder;

    public RecorderTask(MediaRecorder recorder) {
        this.recorder = recorder;
    }

    public void run() {
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    int peakAmplitude = recorder.getMaxAmplitude();
                    //double amplitudeDb = 20 * Math.log10((double)Math.abs(peakAmplitude));
                    amplitudeDb = 20 * Math.log10((double)Math.abs(peakAmplitude));
                    Log.i("sound","amplitudeDb" + amplitudeDb);
                    if (amplitudeDb>60){
                        noiseTv.setText("This room is noisy!!!");
                        noiseImage.setBackgroundResource(R.drawable.noise);
                    }
                    else{
                        noiseTv.setText("This room is quiet!!!");
                        noiseImage.setBackgroundResource(R.drawable.quiet);
                    }
                }
            });
        }
    }
}// end of referencing
user1836957
  • 427
  • 2
  • 9
  • 24

2 Answers2

1

you can add android:configChanges="orientation|screenSize" for this activity in AndroidManifest. In this case activity will not recreated, and you thread will not be created second time

0

Setting android:configChanges="orientation|screenSize" works (in your case) but is not a good solution and not the idiomatic way it should be done.

This is because if you have different layouts for portait and landscape orientations they will not be loaded correctly when you rotate the phone.

Here are some options:

  1. Do what @toshkinl suggested and use a service. This has the benefit that the Thread will run even if the application is closed. However this might be out of scope for your app.
  2. Put the Thread in your Application class and access it in the activity:

    MyApplication app = (MyApplication) getApplication();

    This has the benefit that it's easy to implement and your Thread will survive configuration changes and also changing of the activity. Note that the android OS can kill your app when it's not used, so Thread reference will be set to null inside your application class when your app is restarted, in which case you just re-create the Thread.

Minas Mina
  • 2,058
  • 3
  • 21
  • 35