Question

I'm learning android development, and what I'm trying to do is to have a label that counts down from 40 minutes, and when it reaches 0 it would stop counting and do something else. This is my code:

@Override
protected void onStart() {
        super.onStart();
        count = 2400;
        final Timer t = new Timer();//Create the object
        t.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                minLeft = (int) Math.floor(count / 60);
                secLeft = count - minLeft * 60;
                counter = minLeft + ":" + secLeft;
                TextView tv = (TextView) findViewById(R.id.timer);

                Log.i(MainActivity.TAG,minLeft+", "+secLeft+", "+counter);
                tv.setText(counter);
                count--;
                if (minLeft <= 0 && secLeft <= 0) {
                    t.cancel();
                    count = 2400;
                    onFinish();
                }
            }
        }, 1000, 1000);
    }

But, when I go to that activity by clicking a button in the main activity, the label has the text "Timer" (its original text), and after a few seconds the app crashes with CalledFromWrongThreadException, but the line that causes the problem seems to be the one where I set the text of the TextView.

Please help, thanks in advance.

Was it helpful?

Solution

Your scheduled task runs on the background thread. And you try to set the text to the textview from this background thread. However in Android all view related operations have to be done on the main thread.

That is way in your scheduled task you have to use something like:

@Override
protected void onStart() {
    super.onStart();
    count = 2400;
    final Timer t = new Timer();//Create the object
    t.scheduleAtFixedRate(new TimerTask() {
        @Override
        public void run() {
            minLeft = (int) Math.floor(count / 60);
            secLeft = count - minLeft * 60;
            counter = minLeft + ":" + secLeft;
            // the name of your actual activity
            MyActivity.this.runOnUiThread(new Runnable() {
               @Override
               public void run() {
                 TextView tv = (TextView) findViewById(R.id.timer);
                 Log.i(MainActivity.TAG,minLeft+", "+secLeft+", "+counter);
                 tv.setText(counter);
               }
            });

            count--;
            if (minLeft <= 0 && secLeft <= 0) {
                t.cancel();
                count = 2400;
                onFinish();
            }
        }
    }, 1000, 1000);
}

Please also note, that this code can be written more elegantly, without all/so many anonymous classes, but it should do the trick.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top