I'm still pretty new to Android programming and I'm now struggling with screen orientation changes.
I'm using a viewPager with FragmentPagerAdapter as described on Android Developers to display two fragments that are accessed with tabs.
In Fragment1, a new thread is started when a button is pressed. This thread computes data and constantly updates the UI (using the UI thread). When the button is pressed again, a flag is set to false, stopping the thread. When sliding to Fragment2 or pressing the home button, the thread continues to run. When the back button is pressed, the activity and fragments are destroyed and so is the thread (I think?). That would be the intended behavior.
I would like to understand what happens exactly when the screen changes orientation. What happens to the fragments and my thread if it is running ? Can you point me to a link explaining how I should proceed to keep the thread running and updating the UI after an orientation change ?
Edit: I found a way to get the behavior I wanted (described below). I'm still not sure about what really happens behind the scene though.
I simply had to put this line in the fragment constructor:
savedInstanceState(true);
With this line, the UI is still updated after the screen is rotated. Since the button text is changed when it is activated, I also had to put something like this in the fragment onCreateView() method:
if(flag) {
mStartButton.setText();
}
These are the questions that helped me:
setRetainInstance not retaining the instance
Fragment not re-added after orientation change
Edit 2: Here is some additional information about my code. In my fragment OnCreateView(), I have this listener for a simple button:
// Start-Stop button listener
mStartButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (!mIsRunning) { // START
// New thread
Thread myThread = new Thread(new Runnable() {
public void run() {
runFunction();
}
});
myThread.start();
mIsRunning = true;
mStartButton.setText(getString(R.string.stop));
}
else { // STOP
mIsRunning = false;
mStartButton.setText(getString(R.string.start));
}
}
});
Where runFunction() is defined like this:
private void runFunction() {
while(mIsRunning) {
// Do something
}
}
Handling the thread interruption
I suggest to change the onClick and runFunction() like this(source):
// in fragments oncreate
myThread = new Thread(new Runnable() {
public void run() {
runFunction();
}
});
// Start-Stop button listener
mStartButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (!mIsRunning) { // START
myThread.start();
mStartButton.setText(getString(R.string.stop));
} else { // STOP
myThread.interrupt();
mStartButton.setText(getString(R.string.start));
}
mIsRunning = !mIsRunning
}
});
Where runFunction() is defined like this:
private void runFunction() {
while(!Thread.currentThread().isInterrupted()) {
// Do something
}
}
There is no guarantee that the thread will stop immediately you click the button. There is a race condition between the gui thread and myThread. In your version imagine that you clicked the button and a context switch happened. Your thread was activated at line
while(mIsRunning) {
and the boolean value was checked to be true. A context switch happens and gui thread makes the value false. Context switch happens one more time and the thread goes on without checking the boolean value. It only stops after a single iteration. This kind of situation may be rare and you don't need to worry about this problem.
Handling orientation change
When you change the orientation onSaveInstanceState is called. Here you save the variables you want to use after recreating the activity.
public void onSaveInstanceState(Bundle savedState) {
super.onSaveInstanceState(savedState);
savedState.putBool("mIsRunning", mIsRunning);
}
After the orientation is changed onCreate is called again
public void onCreate (Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
myThread = new Thread(new Runnable() {
public void run() {
runFunction();
}
});
if (savedInstanceState != null) {
mIsRunning = savedInstanceState.getBool("mIsRunning");
if (mIsRunning) {
myThread.start();
mStartButton.setText(getString(R.string.stop));
}
}
...
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With