Reputation: 1027
I'm programming an tuner. The tuner is a fragment. My goal is to keep the tuner running when the phone switches between landscape and portrait mode. Therefore I save the actual status with onSaveInstanceState() and restore it with onViewStateRestored(). That actually works very fine. But I've one big problem: When the fragment is rebuilt onDestroy(), onCreateView() and onViewStateRestored() are executed a second time. That causes an crash. Any ideas how to fix it?
Below the code, please ask if you need more detailed code:
public class TunerFragment extends BasicFragment
{
//View Elemente
public boolean controlButtonUnclicked = true;
private static final String LOG_TAG = "FFTTEST";
private final Semaphore bufferZugriff = new Semaphore(1, true);
//Objecte zur Aufnahme
private int channel_config = AudioFormat.CHANNEL_IN_MONO;
private int format = AudioFormat.ENCODING_PCM_16BIT;
private int sampleRate = 44100;
private int bufferSize = AudioRecord.getMinBufferSize(sampleRate, channel_config, format)*8; // je größer der Buffer, desto genauer die FFT!!
private AudioRecord audioInput = null; //new AudioRecord(AudioSource.MIC, sampleSize, channel_config, format, bufferSize);
private short[] audioBuffer = new short[bufferSize];
//Attribute zur Steuerung
private Thread readingThread = null,writingThread=null;
private boolean isRecording = false;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View view;
view = inflater.inflate(R.layout.fragment_tuner, container, false);
TextView tv = (TextView) view.findViewById(R.id.textview_referencenote);
SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this.getActivity());
tv.setText(sharedPrefs.getString("value_a", "440"));
Button mButton = (Button) view.findViewById(R.id.controlButton);
mButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
onControlButton(v);
}
});
return view;
}
@Override
public void onDestroy() {
super.onDestroy();
if(isRecording)
{
isRecording = false;
while(this.readingThread.isAlive());
try
{
audioInput.stop();
audioInput.release();
}
catch (Exception e)
{}
while(this.writingThread.isAlive());
audioInput= null;
readingThread = null;
writingThread = null;
}
}
@Override
public void onStop() {
super.onStop();
if(isRecording)
{
isRecording = false;
while(this.readingThread.isAlive());
try
{
audioInput.stop();
audioInput.release();
}
catch (Exception e)
{}
while(this.writingThread.isAlive());
audioInput= null;
readingThread = null;
writingThread = null;
}
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean("isRecording", isRecording);
}
@Override
public void onViewStateRestored (Bundle savedInstanceState) {
super.onViewStateRestored (savedInstanceState);
if(savedInstanceState!=null)
{
if(savedInstanceState.getBoolean("isRecording"))
{
Button mPlayButton = (Button) getView().findViewById(R.id.controlButton);
//Recorder starten
controlButtonUnclicked = false;
mPlayButton.setText("Stop");
startRecording();
isRecording = true;
}
}
}
public void onControlButton(View v)
{
//...
}
private void onPlay(boolean start) {
//starts and stops the tuner, depending on the actual state
}
private void startRecording()
{
//prepares and starts the worker threads (Thread for reading the Mic.-buffer and Thread for processing the signal)
}
private int maxFrequenzToIndex (int MaxFreq)
{
return (MaxFreq * 1 * bufferSize) / (1 * sampleRate);
}
private void calculate()
{
while(isRecording)
{
//processing the signal ...
//post result to main thread
final String notennamef = notenname;
final int differenzf=differenz;
((TextView) getView().findViewById(R.id.editNote)).post(new Runnable() {
@Override
public void run() {
try
{
TextView View = (TextView) getView().findViewById(R.id.editNote);
View.setText(""+notennamef);
View = (TextView) getView().findViewById(R.id.editAbweichung);
View.setText(""+differenzf);
}
catch (NullPointerException e)
{
System.out.println("Exception in calculate() -> run()");
System.out.println(e);
}
}
});
try
{
Thread.sleep(100); //Wie oft wird die Anzeige aktualisiert?
}
catch (InterruptedException e)
{}
}
// restore neutral view
((TextView) getView().findViewById(R.id.editNote)).post(new Runnable() {
@Override
public void run() {
try
{
TextView View = (TextView) getView().findViewById(R.id.editNote);
View.setText("");
View = (TextView) getView().findViewById(R.id.editAbweichung);
View.setText("");
}
catch (NullPointerException e)
{
System.out.println("Exception in calculate() -> nach while");
System.out.println(e);
}
}
});
}
private int calculateCent(double referenz, double frequenz)
{
return (int) (1200*(Math.log(frequenz/referenz)/Math.log(2))); // die Formel entspricht 1200*log2(frequenz/referenz)
}
private void readAudioToBuffer()
{
while(isRecording)
{
try
{
bufferZugriff.acquire();
audioInput.read(audioBuffer, 0,bufferSize);
bufferZugriff.release();
}
catch (Exception e )
{
System.out.println("Fehler beim schreiben in den Audiobuffer");
}
}
}
private void stopRecording()
{
isRecording = false;
try
{
audioInput.stop();
audioInput.release();
}
catch (Exception e)
{}
audioInput= null;
readingThread = null;
writingThread = null;
getView().findViewById(R.id.controlButton).setKeepScreenOn(false);
}
}
Upvotes: 1
Views: 1797
Reputation: 569
The problem is that when the phone switches between landscape and portrait mode the activity is recreated.
so you need to make sure on activity recreation that you don't create new fragment by overriding the activity's onCreate method like this :
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_layout);
if (savedInstanceState == null) {
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
Fragment fragment = new TunerFragment();
transaction.add(R.id.fragment_container, fragment);
transaction.commit();
}
}
Upvotes: 7