Android application life cycle to achieve a simple stopwatch App

1. Functional analysis

1.1 stopwatch function interface

1.2 App structure

  • 1 Activity: MainActivity
  • 1 Layout: activity_main.xml

2. Development view layout

2.1,activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center_horizontal"
        android:orientation="vertical">

        <TextView
            android:id="@+id/time_view"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center_horizontal"
            android:text="0:00:00"
            android:textAppearance="@style/TextAppearance.AppCompat.Large"
            android:textSize="80sp"/>

        <Button
            android:id="@+id/button_start"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:text="@string/start"
            android:onClick="onClickStart"/>

        <Button
            android:id="@+id/button_stop"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:text="@string/stop"
            android:onClick="onClickStop"/>

        <Button
            android:id="@+id/button_reset"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:text="@string/reset"
            android:onClick="onClickReset"/>
    </LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

2.2,string.xml

<resources>
    <string name="app_name">Stopwatch</string>
    <string name="start">start</string>
    <string name="stop"> Stop</string>
    <string name="reset">Reset</string>
    <string name="time">Time</string>
</resources>

3. Activity implementation

3.1. MainActivity class

public class MainActivity extends AppCompatActivity {

    //Seconds timed
    private  int seconds = 0;
    //Status of timing
    private  boolean running = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //When the Activity starts, runTimer() is started in the onCreate() method
        runTimer();
    }

    //Start timing
    public void onClickStart(View view){
        running = true;
    }
    //Stop timing
    public void onClickStop(View view){
        running = false;
    }
    //Reset stopwatch
    public void onClickReset(View view){
        running = false;
        seconds = 0;
    }
  	//Cycle timing method
    private void runTimer(){
        final TextView timeView = findViewById(R.id.time_view);
        //Create handler of UI thread for message processing
        final Handler handler = new Handler();
        handler.post(new Runnable() {//Hand in a Runnable immediately, and the task is in the run() method of Runnable
            @Override
            public void run() {
                int hours = seconds/3600;
                int minutes = (seconds%3600)/60;
                int secs = seconds%60;
                String time = String.format("%d:%02d:%02d", hours, minutes, secs);
                timeView.setText(time);
                if(running){
                    seconds++;
                }            
                //Submit the task repeatedly every 1000ms
                handler.postDelayed(this,1000);
            }
        });
    }
}

4. Application of life cycle

4.1 problem analysis

  • Question 1: when the screen is rotated, Android detects a change in the screen direction, and the timing will be reset
  • Problem 2: the App is switched to the background, and the stopwatch cannot pause

4.2. Activity running process

4.2. The screen rotates and the timing does not reset

  • When the device configuration changes, such as the screen rotates, the state needs to be saved and restored when restarting

  • After running and before destroying (onDestroy()), onSaveInstanceState() will be called to save the state to the Bundle

    • The onSaveInstanceState() method needs to be overridden to save the state

    • Bundle stores key value pairs

      bundle.put*("name",value)

    • Store running and seconds in the Bundle

      @Override
      public void onSaveInstanceState(Bundle savedInstanceState){
          super.onSaveInstanceState(savedInstanceState);
          
          savedInstanceState.putInt("seconds",seconds);
          savedInstanceState.putBoolean("running",running);
      }
      
  • Recover in onCreate() method, with Bundle as its parameter, and null when the process creates an Activity for the first time,
    The Bundle saved later for onSaveInstanceState()

    • Take the key value pair from the Bundle

      bundle.get*("name");

    • Take out running and seconds from the Bundle

      @Override
      protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          Log.d("life cycle","onCreate");
          setContentView(R.layout.activity_main);
          
          if(savedInstanceState!=null){
              seconds = savedInstanceState.getInt("seconds");
              running = savedInstanceState.getBoolean("running");
          }
          runTimer();
      }
      
  • Program running process

    • The user starts the App and clicks the start button to start timing
      The runTimer() method starts incrementing seconds and displays it in the text box time_ In view
    • Rotate the mobile phone, Android detects the change of the screen direction, and destroys the original Activity, then calls onSaveInstanceState() to save the instance variables.
    • Android destroys the Activity, creates the Activity again, calls onCreate() method again, and passes in the saved Bundle as a parameter
    • In the onCreate() method, retrieve the value stored in the Bundle and restore it to the state before destruction
    • The runTimer() method continues timing from the seconds before rotation

4.3. When the App is switched to the background, the stopwatch can pause

  • resolvent

    • Override onStop() and stop timing before disappearing

      @Override
      protected void onStop(){
          super.onStop();
          Log.d("life cycle","onStop");
          wasRunning = running; // Whether it is running before recording,
          running = false; //Set running to false to stop timing
      }
      
    • The lifecycle of the parent class must be called before overriding the lifecycle method

      super.onStop();

    • Override onStart() to continue timing until it is visible

      @Override
      //If running==true before, set running to true to continue timing
      protected void onStart(){
          super.onStart();
          Log.d("life cycle","onStop");
          if(wasRunning){ 
              running = true;
          }
      }
      
    • The App is switched to the background, and the Activity object still exists. You can use the instance variable to store the state

      //Seconds timed
      private  int seconds = 0;
      //Status of timing
      private  boolean running = false;
      //A new variable used to save the running state before disappearing in onStop()
      private boolean wasRunning = false;
      
      @Override
      protected void onStop(){
          super.onStop();
          Log.d("life cycle","onStop"); //Log and pause
          wasRunning = running;
          running = false;
      }
      
      @Override
      protected void onStart(){
          super.onStart();
          Log.d("life cycle","onStop"); //Log and recover
          if(wasRunning){
              running = true;
          }
      }
      
    • Save wasRunning in the Bundle before destroying the Activity,
      Recover wasRunning from Bundle after re instantiation of Activity

      @Override
      protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          Log.d("life cycle","onCreate");
          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);
      }
      
  • Operation process

    • The user starts the App, clicks the Start button, and runTimer() starts to increment seconds and update the text box
    • The user clicks the Home key, the Activity disappears, and Android calls onStop()
    • The user returns the stopwatch App, the Activity is visible, and Android calls onStart()

5. MainActivity complete code

public class MainActivity extends AppCompatActivity {

    //Seconds timed
    private  int seconds = 0;
    //Status of timing
    private  boolean running = false;
    //A new variable used to save the running state before disappearing in onStop()
    private boolean wasRunning = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d("life cycle","onCreate");
        setContentView(R.layout.activity_main);
        if(savedInstanceState!=null){
            seconds = savedInstanceState.getInt("seconds");
            running = savedInstanceState.getBoolean("running");
            wasRunning = savedInstanceState.getBoolean("wasRunning");
        }
        //When the Activity starts, runTimer() is started in the onCreate() method
        runTimer();
    }

    @Override
    protected void onStart(){
        super.onStart();
        Log.d("life cycle","onStop");
        if(wasRunning){
            running = true;
        }
    }

    @Override
    protected void onStop(){
        super.onStop();
        Log.d("life cycle","onStop");
        wasRunning = running;
        running = false;
    }


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

    protected void onDestroy(){
        super.onDestroy();
        Log.d("life cycle","onDestroy");
    }

    //Start timing
    public void onClickStart(View view){
        running = true;
    }
    //Stop timing
    public void onClickStop(View view){
        running = false;
    }
    //Reset stopwatch
    public void onClickReset(View view){
        running = false;
        seconds = 0;
        wasTiming = false;
    }
    //Cycle timing method
    private void runTimer(){
        final TextView timeView = findViewById(R.id.time_view);
        //Create handler of UI thread for message processing
        final Handler handler = new Handler();
        handler.post(new Runnable() {//Hand in a Runnable immediately, and the task is in the run() method of Runnable
            @Override
            public void run() {
                int hours = seconds/3600;
                int minutes = (seconds%3600)/60;
                int secs = seconds%60;
                String time = String.format("%d:%02d:%02d", hours, minutes, secs);
                timeView.setText(time);
                if(running){
                    seconds++;
                }
                //Submit the task repeatedly every 1000ms
                handler.postDelayed(this,1000);
            }
        });
    }
}

Keywords: Java Android UI

Added by zed420 on Thu, 11 Nov 2021 09:11:23 +0200