Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android: Viewpager, thread and orientation change

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
    }
}
like image 342
Iodestar Avatar asked Apr 13 '26 18:04

Iodestar


1 Answers

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));
        }
    }
    ...
}
like image 64
gkiko Avatar answered Apr 15 '26 10:04

gkiko



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!