0

I am making an android application as part of a school assignment.

I need to be able to display a timer in hh:mm:ssand update the timer every second.

I have managed to come up with the code i think will produce the results i need, but i cannot get the AsyncTask to execute every second. This is my current code:

TimerHand timerHand = new TimerHandling(timerTextView);
Timer timer = new Timer();
    timer.scheduleAtFixedRate(new TimerTask() {

            public void run() {
                timerHand.execute();

            } },1000, 1000); // 1000ms == 1 second

This is the Async task this is a nested class inside the class which holds the above code, and the declaration of the TextView tv:

   class TimerHandling  extends AsyncTask<Void, Void, Void>{

        private long secs, mins, hours, millis;
        private String timeString;
        private TextView tv;
        private long startTime;
        private boolean stop;
        private Handler handler;

        public TimerHandling(TextView tv){
            this.tv = tv;
            startTime = System.currentTimeMillis();
            stop = false;
            handler = new Handler();

        }

        @Override
        protected Void doInBackground(Void... params) {
            while(stop != true){
                millis = System.currentTimeMillis() - startTime;
                hours += TimeUnit.MILLISECONDS.toHours(millis);
                mins +=  TimeUnit.MILLISECONDS.toMinutes(millis) - TimeUnit.HOURS.toMinutes(TimeUnit.MILLISECONDS.toHours(millis));
                secs += TimeUnit.MILLISECONDS.toSeconds(millis) - TimeUnit.MILLISECONDS.toSeconds(TimeUnit.MILLISECONDS.toMinutes(millis));     

                timeString = String.format("%02d:%02d:%02d",hours, mins, secs);
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        tv.setText(timeString);
                    }
                });
            }
            return null;
        }

        public void setStopCondition(boolean condition){

            this.stop = condition;
        }
    }
        }

It produces the following logCat output:

01-23 18:00:27.865: E/AndroidRuntime(14458): FATAL EXCEPTION: AsyncTask #5
01-23 18:00:27.865: E/AndroidRuntime(14458): Process: dcs.aber.ac.uk.cs211.group02, PID: 14458
01-23 18:00:27.865: E/AndroidRuntime(14458): java.lang.RuntimeException: An error occured while executing doInBackground()
01-23 18:00:27.865: E/AndroidRuntime(14458):    at android.os.AsyncTask$3.done(AsyncTask.java:300)
01-23 18:00:27.865: E/AndroidRuntime(14458):    at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:355)
01-23 18:00:27.865: E/AndroidRuntime(14458):    at java.util.concurrent.FutureTask.setException(FutureTask.java:222)
01-23 18:00:27.865: E/AndroidRuntime(14458):    at java.util.concurrent.FutureTask.run(FutureTask.java:242)
01-23 18:00:27.865: E/AndroidRuntime(14458):    at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231)
01-23 18:00:27.865: E/AndroidRuntime(14458):    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
01-23 18:00:27.865: E/AndroidRuntime(14458):    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
01-23 18:00:27.865: E/AndroidRuntime(14458):    at java.lang.Thread.run(Thread.java:841)
01-23 18:00:27.865: E/AndroidRuntime(14458): Caused by: android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
01-23 18:00:27.865: E/AndroidRuntime(14458):    at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:6094)
01-23 18:00:27.865: E/AndroidRuntime(14458):    at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:824)
01-23 18:00:27.865: E/AndroidRuntime(14458):    at android.view.View.requestLayout(View.java:16431)
01-23 18:00:27.865: E/AndroidRuntime(14458):    at android.view.View.requestLayout(View.java:16431)
01-23 18:00:27.865: E/AndroidRuntime(14458):    at android.view.View.requestLayout(View.java:16431)
01-23 18:00:27.865: E/AndroidRuntime(14458):    at android.view.View.requestLayout(View.java:16431)
01-23 18:00:27.865: E/AndroidRuntime(14458):    at android.widget.RelativeLayout.requestLayout(RelativeLayout.java:352)
01-23 18:00:27.865: E/AndroidRuntime(14458):    at android.view.View.requestLayout(View.java:16431)
01-23 18:00:27.865: E/AndroidRuntime(14458):    at android.widget.TextView.checkForRelayout(TextView.java:6600)
01-23 18:00:27.865: E/AndroidRuntime(14458):    at android.widget.TextView.setText(TextView.java:3813)
01-23 18:00:27.865: E/AndroidRuntime(14458):    at android.widget.TextView.setText(TextView.java:3671)
01-23 18:00:27.865: E/AndroidRuntime(14458):    at android.widget.TextView.setText(TextView.java:3646)

I understand that is is caused because the nested class has a different View so it cannot update the TextView, how can i overcome this?

When i run my code, i can see it executing and after about 2 seconds the app will crash wth the posted error message.

Update:

public void startCountingTimer() {

final Handler handler = new Handler();
final Runnable task = new Runnable() 

{ 
    @Override
    public void run() {

        millis = System.currentTimeMillis() - startTime;
        hours += TimeUnit.MILLISECONDS.toHours(millis);
        mins +=  TimeUnit.MILLISECONDS.toMinutes(millis) - TimeUnit.HOURS.toMinutes(TimeUnit.MILLISECONDS.toHours(millis));
        secs += TimeUnit.MILLISECONDS.toSeconds(millis) - TimeUnit.MILLISECONDS.toSeconds(TimeUnit.MILLISECONDS.toMinutes(millis));     

        timeString = String.format("%02d:%02d:%02d",hours, mins, secs);
        handler.postDelayed(task, 1000); //problem on this line
    }
};
task.run();   

}

I have pointed out the problem with a single line comment, "task" is undeclared.

Jon Miles
  • 9,605
  • 11
  • 46
  • 66
chris edwards
  • 1,332
  • 4
  • 13
  • 31

1 Answers1

1
Caused by: android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

You are trying to update ui from a non ui thread which is not possible. You should update ui from the ui thread.

Timer runs on a different thread.

Also read threading rules @ http://developer.android.com/reference/android/os/AsyncTask.html

 I need to be able to display a timer in hh:mm:ss and update the timer every second.

You can use a Handler or a CountDownTimer

Handler is associated with the thread in which it is created.

http://developer.android.com/reference/android/os/Handler.html

Handler m_handler;
Runnable m_handlerTask ; 
m_handler = new Handler(); 
m_handlerTask = new Runnable() 
{ 
@Override
public void run() {
   // do something
  m_handler.postDelayed(m_handlerTask, 1000); 
 }
 };
 m_handlerTask.run();   

For the count down timer

Countdowntimer in minutes and seconds

Edit:

public class MainActivity extends Activity {

      Handler handler;
      Runnable task ;
      @Override
      protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        startCountingTimer();

}
      public void startCountingTimer() {

       handler = new Handler();
       task = new Runnable() 
          { 
              @Override
              public void run() {
                      // do something
                  handler.postDelayed(task, 1000); 
              }
          };
          task.run();   
          }  
}
Community
  • 1
  • 1
Raghunandan
  • 132,755
  • 26
  • 225
  • 256
  • when i do this, postDelayed(m_handlerTask,1000) tells me m_handlerTask is not initiated, and i have a line of code that says m_handler = new Runnable ..... etc. any ideas? – chris edwards Jan 23 '14 at 18:41
  • @chrisedwards post how you tried. "When do i do this" when you want something to run repeatedly. or better use a count down timer – Raghunandan Jan 23 '14 at 18:43
  • @Raghunandan You might have misread what he put. He's wondering why he's getting the warning. @chrisedwards, your variable declarations need an assignment. Java doesn't like a variable declaration without them being set. Just put `m_handler = null;`. OR move the assignments next to the declarations. – DavidAndroidDev Jan 23 '14 at 18:46
  • i have done so: the code is m_mhandlerTask = new Runnable () { the code ......... and m_handler = new Handler(); is this not correct? – chris edwards Jan 23 '14 at 18:53
  • @chrisedwards can you edit your post and post the same it would be much easier – Raghunandan Jan 23 '14 at 18:54
  • @chrisedwards your task in not initialized properly – Raghunandan Jan 23 '14 at 19:05
  • @Raghunandan my code now compiles, but it freezes my app, but it does increment the timer it says at 00:00:00. i tried putting it in a while loop, but it just froze my app. any ideas? – chris edwards Jan 23 '14 at 19:30
  • @chrisedwards the frezzing may be a different issue altogether. I suggest you start a new question with the relevant details. You don't require loop here. just write the code that you want to repeat every second in the run method and make sure you cancel the run in onPause of the activity – Raghunandan Jan 23 '14 at 19:33