-1

My OnStart() method should not get called because my onCreate() method will run infinitely. When OnCreate method runs, the runTimer() method gets called. This runTimer method will run infinitely because of handler.postDelayed. How is it that Android Studio is able to finish the infinite loop of the runTimer() method and then call OnStart() method?

    // MY STOPWATCH APP        
    


    public class MainActivity extends AppCompatActivity {
    private int seconds = 0;
    private boolean running;
    private boolean wasRunning;

    @Override
    // ON RUNNING MY APP FOR THE FIRST TIME THIS METHOD WILL GET CALLED FIRST.
    protected void onCreate(Bundle savedInstanceState) { 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        if (savedInstanceState != null) {
            seconds = savedInstanceState.getInt("seconds");
            running = savedInstanceState.getBoolean("running");
            wasRunning = savedInstanceState.getBoolean("wasRunning");
        }
        
        runTimer();
    }

    @Override
    public void onSaveInstanceState(Bundle savedInstanceState) {
        super.onSaveInstanceState(savedInstanceState);
        savedInstanceState.putInt("seconds", seconds);
        savedInstanceState.putBoolean("running", running);
        savedInstanceState.putBoolean("wasRunning", wasRunning);
    }

    @Override
    protected void onStop() {
        super.onStop();
        wasRunning = running;
        running = false;
    }

    @Override
    protected void onStart() {
        super.onStart();
        if (wasRunning) {
            running = true;
        }
    }

    //Start the stopwatch running when the Start button is clicked.
    public void onClickStart(View view) {
        running = true;
    }

    //Stop the stopwatch running when the Stop button is clicked.
    public void onClickStop(View view) {
        running = false;
    }

    //Reset the stopwatch when the Reset button is clicked.
    public void onClickReset(View view) {
        running = false;
        seconds = 0;
    }

    //Sets the number of seconds on the timer.
    private void runTimer() {
        final TextView timeView = (TextView)findViewById(R.id.tv_time_view);
        final Handler handler = new Handler();
        handler.post(new Runnable() {
            @Override
            public void run() {
                int hours = seconds/3600;
                int minutes = (seconds%3600)/60;
                int secs = seconds%60;
                String time = String.format(Locale.getDefault(),
                        "%d:%02d:%02d", hours, minutes, secs);
                timeView.setText(time);
                if (running) {
                    seconds++;
                }
                handler.postDelayed(this, 1000);
            }
        });
    }
}

2 Answers2

3

Why do you think that runTimer() will run indefinitely?

handler.post() and handler.postDelayed() add an object (your new Runnable() {}) to the message queue so the objects run() method can be executed some time later.

handler.post() returns after adding the object to the message queue and therefore the runTimer() and onCreate() methods finish normally.


Why "Message Queue"? Because the documentation says so:

https://developer.android.com/reference/android/os/Handler:

A Handler allows you to send and process Message and Runnable objects associated with a thread's MessageQueue

https://developer.android.com/reference/android/os/Handler#post(java.lang.Runnable):

Causes the Runnable r to be added to the message queue. The runnable will be run on the thread to which this handler is attached.

https://developer.android.com/reference/android/os/Handler#postDelayed(java.lang.Runnable,%20long):

Causes the Runnable r to be added to the message queue, to be run after the specified amount of time elapses.

The whole point of using a Handler is to allow the Runnables that are post()ed to execute at some later point in time so that the current thread can continue its work.

If the handler would execute the code within the Runnable directly (and blocking) then you could have written your code as new Runnable() { /*...*/ }.run();

Thomas Kläger
  • 17,754
  • 3
  • 23
  • 34
  • Message Queue? Can you please elaborate? Being a beginner in Android Development, it's hard to understand certain things. Don't consider me as a noob, I have done Java. But still, please elaborate – Darshil Shah Aug 24 '21 at 03:19
  • Now I understand. Thank You! When I run this app for the first time, the variable wasRunning is not initialized. Don't you think this should cause error in the if statement of onStart() method? Can you please help me with this query? – Darshil Shah Aug 24 '21 at 12:09
  • 1
    @DarshilShah `wasRunning` is not a (local) variable - it is a field. And in Java all fields have default values (null for reference field, 0 for numeric fields and false for boolean fields) – Thomas Kläger Aug 24 '21 at 12:13
  • Thank you sir. I got it. – Darshil Shah Aug 25 '21 at 17:26
1

The handler.post will execute the Runnable on the handler thread, so runTimer() will exist after the call to handler.post()

David Lilljegren
  • 1,799
  • 16
  • 19
  • Yeah I get that but what I am asking is that don't you think handler.post() will run infinitely because at the end of the code of runTimer() method there is handler.postDelayed(). As a result, onStart() method should not start at all. What's exactly happening? Can you please elaborate? – Darshil Shah Aug 24 '21 at 03:16
  • 1
    The first time runTimer() is called fron onCreate() it runs in the main thread. At the end it will post a call to run runTimer() one second later on the handler thread. The main thread exist the method onCreate() and continues. runTimer() is not a loop. – David Lilljegren Aug 25 '21 at 07:47
  • Thank you, sir. I understand it now. – Darshil Shah Aug 25 '21 at 17:27