0

I tried to change my UI from my TimerTask and I recognized that it is impossible, because the TimerTask runs in a background thread and background threads are not allowed to change the UI.
Now my question is: I was changing a ProgressBar and a TextView from the TimerTask, and it worked for the ProgressBar but not for the TextView. Why is it possible for me to change the ProgressBar?

Why doesn't the ProgressBar have any problems with this?

timer.scheduleAtFixedRate(new TimerTask() {
        @Override
        public void run() {
            int time = playerTimer(time);

            progressbar.setProgress(time);
            playerTime.setText(time);

        }

    }, 0, 1000);
Alireza MH
  • 573
  • 8
  • 25

4 Answers4

2

Its because when you see the method refreshProgress(int id, int progress, boolean fromUser) in the source code of ProgressBar.java then you will see that Progress Bar is updated inside the current thread itself that is the UI Thread. And if its not the UI Thread then Progress Bar is update using a Runnable.

Also, if you want to update UI from a non-UI thread once then you can use runOnUIThread(Runnable action)

Lalit Poptani
  • 67,150
  • 23
  • 161
  • 242
1

Not all UI manipulations have to be done on the main thread. The ones that can be called from another thread schedule a layout and/or draw of the View or View tree on the main thread. In practice it's not really a good idea to try to discern which methods are safe to call from non-UI thread and which are not.

To get around this, you should use AsyncTask and do all your UI manipulation from inside the callbacks that run on the UI thread: onPreExecute(), onPostExecute(), and onProgressUpdate().

Karakuri
  • 38,365
  • 12
  • 84
  • 104
1

Since the setProgress method is synchronized, it seems to be designed to be called from another thread. The documentation does not clearly state that though.

Henry
  • 42,982
  • 7
  • 68
  • 84
1

ProgressBar class is designed to work with it. Here's the code that does it:

  if (mUiThreadId == Thread.currentThread().getId()) {
        doRefreshProgress(id, progress, fromUser, true);
    } else {
        if (mRefreshProgressRunnable == null) {
            mRefreshProgressRunnable = new RefreshProgressRunnable();
        }

        final RefreshData rd = RefreshData.obtain(id, progress, fromUser);
        mRefreshData.add(rd);
        if (mAttached && !mRefreshIsPosted) {
            post(mRefreshProgressRunnable); //--posted to view's thread --
            mRefreshIsPosted = true;
        }
    }

This is checking if you are on the UI thread. If not its posting a runnable on UI thread. My guess is that this design helps it to update progressBar from multiple places.

For other views you have to do:

runonUiThread(new Runnable(){

 @Override
 public void run(){
   //--- change the view--
 }

});
S.D.
  • 29,290
  • 3
  • 79
  • 130