Android:如何在手机旋转后阻止创建新线程

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android:如何在手机旋转后阻止创建新线程相关的知识,希望对你有一定的参考价值。

我写了一个android程序来检测房间是否嘈杂或相当。但每当我旋转手机时,短信和图像都不会再更新,并且会打印噪音分贝 - 无穷大及其正确值。我认为每次旋转手机时都会创建一个新线程。因为如果我旋转手机,则会再次打印amplitudeDb数量值 - 无穷大。我怎样才能防止这种情况发生?每次旋转手机时,如何在没有生成新线程的情况下保持初始线程运行?

这是我的代码

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
答案

你可以在AndroidManifest中为这个活动添加android:configChanges =“orientation | screenSize”。在这种情况下,不会重新创建活动,并且不会再次创建线程

另一答案

设置android:configChanges="orientation|screenSize"有效(在你的情况下),但不是一个好的解决方案,而不是它应该做的惯用方式。

这是因为如果您具有不同的纵向和横向布局,则在旋转手机时将无法正确加载它们。

以下是一些选项:

  1. 做@toshkinl建议并使用服务。这样做的好处是即使应用程序关闭,Thread也会运行。但是,这可能超出了您的应用范围。
  2. 将线程放在Application类中并在活动中访问它: MyApplication app = (MyApplication) getApplication(); 这样做的好处是它易于实现,并且您的线程将在配置更改和更改活动后继续存在。请注意,Android操作系统可以在未使用时终止您的应用程序,因此当您的应用程序重新启动时,应用程序类中的线程引用将设置为null,在这种情况下,您只需重新创建线程。

以上是关于Android:如何在手机旋转后阻止创建新线程的主要内容,如果未能解决你的问题,请参考以下文章

如何阻止 Android 应用程序旋转? [复制]

如何阻止我的 UI 线程锁定?

部分Android或IOS手机拍照后照片被旋转的问题

如何避免在旋转端口登陆Android4.X后在智能手机中剪切/复制/粘贴?

触墙后如何阻止播放器旋转?

如何在 Android 上解除对 InputStream.read() 的阻止?