3

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:

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
    }
}
Community
  • 1
  • 1
Iodestar
  • 198
  • 2
  • 13
  • Can you please add AcrivityMain code how you handle orientation change you how you start the thread – gkiko Apr 15 '14 at 04:59
  • I updated my first post with more information about my fragment. For the orientation change, I do nothing special other than what I've posted. What would you like to see exactly in ActivityMain? It is pretty much a copy/paste from the android documentation. – Iodestar Apr 17 '14 at 01:55
  • Great searches Iodestar. Also, these posts might interest you: [Understanding Fragment's setRetainInstance(boolean)](http://stackoverflow.com/questions/11182180/), [Why use Fragment#setRetainInstance(boolean)?](http://stackoverflow.com/questions/11160412/), [Android Activity/Fragment life cycle analysis](http://ideaventure.blogspot.com.au/2014/01/android-activityfragment-life-cycle.html), [Best practice to handle orientation change: Android](http://stackoverflow.com/questions/20514926/). – Blo Apr 17 '14 at 07:58

1 Answers1

2

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));
        }
    }
    ...
}
Community
  • 1
  • 1
gkiko
  • 2,283
  • 3
  • 30
  • 50
  • Thanks a lot for your answer! If I understand correctly, using `Thread.interrupt()` will do the same thing as my flag, but in a "cleaner" way. About the orientation change, is what are the pros (and/or cons) of using the method you suggest vs using `savedInstanceState(true);`? – Iodestar Apr 17 '14 at 21:26
  • You are right. Race condition will be in both cases. Do you mean `setRetainState(true)`? If so, see [this](http://stackoverflow.com/questions/11182180/understanding-fragments-setretaininstanceboolean#comment16809452_11318942) comment and answer. I don't have much information about this. sorry :/ – gkiko Apr 18 '14 at 09:55
  • `savedInstanceState(true)` really is what I'm currently using. I'll have a bit more reading to do concerning this function. Thanks again for your help! – Iodestar Apr 18 '14 at 14:22