Create a second Thread

This app executes an infinite for loop. But the UI (main) thread is not allowed to go into an infinite loop. For example, if the onCreate method went into an infinite loop, we would never finish executing onCreate and we would never execute the other lifecycle methods.

We therefore create a second thread to execute the infinite loop. The second thread is not allowed to write on the screen (only the UI thread can do that), so it performs its output with Log.d.

The code to be executed by a second thread is packaged in a method named run. The run method is packaged in a subclass of class Runnable. See Specifying the Code to Run on a Thread.

Source code in Thread.zip

  1. MainActivity.java. The runnable variable refers to an object that belongs to an anonymous subclass of class Runnable.
  2. activity_main.xml
  3. strings.xml
  4. AndroidManifest.xml
  5. build.gradle (Module: app)

Output from Log.d.

A few seconds after launching the app, press the Android back button to destroy the Activity. Observe that the second thread keeps on running. One way to kill the thread is to go to the Settings app and Force Stop or Uninstall the app. Another way is with the kill command below.

08-25 18:41:37.590    6291-6291/? D/myTag: App Process ID: 6291
08-25 18:41:37.590    6291-6291/? D/myTag: Name of UI thread: main
08-25 18:41:37.590    6291-6291/? D/myTag: ID of UI thread: 1
08-25 18:41:37.590    6291-6304/? D/myTag: Name of second thread: Thread-348
08-25 18:41:37.590    6291-6304/? D/myTag: ID of second thread: 348
08-25 18:41:37.590    6291-6304/? D/myTag: 0
08-25 18:41:38.590    6291-6304/? D/myTag: 1
08-25 18:41:39.594    6291-6304/? D/myTag: 2
08-25 18:41:40.594    6291-6304/? D/myTag: 3
08-25 18:41:41.594    6291-6304/? D/myTag: 4
08-25 18:41:42.594    6291-6304/? D/myTag: 5
08-25 18:41:43.594    6291-6304/? D/myTag: 6
08-25 18:41:44.594    6291-6304/? D/myTag: 7
08-25 18:41:45.594    6291-6304/? D/myTag: 8
08-25 18:41:46.594    6291-6304/? D/myTag: 9
08-25 18:41:47.594    6291-6304/? D/myTag: 10
08-25 18:41:47.981    6291-6291/? D/myTag: onDestroy
08-25 18:41:48.598    6291-6304/? D/myTag: 11
etc.

Output from adb and ps (process status)

adb devices

adb -s 192.168.57.101:5555 shell

ps
USER     PID   PPID  VSIZE  RSS     WCHAN    PC         NAME
u0_a56    6291  149   587548 39004 ffffffff b76a207b S edu.nyu.scps.thread

ps -t 6291
USER     PID   PPID  VSIZE  RSS     WCHAN    PC         NAME
u0_a56    6291  149   587548 39004 ffffffff b76a207b S edu.nyu.scps.thread
u0_a56    6294  6291  587548 39004 c0183a45 b76a2399 S GC
u0_a56    6296  6291  587548 39004 c014bfe7 b76a17cb S Signal Catcher
u0_a56    6297  6291  587548 39004 c021a64f b76a0770 S JDWP
u0_a56    6298  6291  587548 39004 c0183a45 b76a2399 S Compiler
u0_a56    6299  6291  587548 39004 c0183a45 b76a2399 S ReferenceQueueD
u0_a56    6300  6291  587548 39004 c0183a45 b76a2399 S FinalizerDaemon
u0_a56    6301  6291  587548 39004 c0183a45 b76a2399 S FinalizerWatchd
u0_a56    6302  6291  587548 39004 c05963ba b76a0586 S Binder_1
u0_a56    6303  6291  587548 39004 c05963ba b76a0586 S Binder_2
u0_a56    6304  6291  587548 39004 c0183a45 b76a2399 S Thread-348

kill -9 6291
ps -t 6291
USER     PID   PPID  VSIZE  RSS     WCHAN    PC         NAME

Output from the Android Device Monitor

In Android Studio, pull down
Tools → Android → Android Device Monitor
On the Devices tab, select your device and your app. On the toolbar, press the Update Threads icon (three parael arrows).

Things to try

  1. Let the Runnable object be an anonymous temporary.
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    Thread currentThread = Thread.currentThread();
                    Log.d("myTag", "Name of second thread: " + currentThread.getName());
                    Log.d("myTag", "ID of second thread: " + currentThread.getId());
    
                    for (int i = 0;; ++i) {
                        Log.d("myTag", String.valueOf(i));
                        try {
                            Thread.sleep(1000L);   //milliseconds
                        } catch (InterruptedException interruptedException) {
                        }
                    }
                }
            });
    
            thread.start();
    

  2. Let the Thread be an anonymous temporary too.
            new Thread(new Runnable() {
                @Override
                public void run() {
                    Thread currentThread = Thread.currentThread();
                    Log.d("myTag", "Name of second thread: " + currentThread.getName());
                    Log.d("myTag", "ID of second thread: " + currentThread.getId());
    
                    for (int i = 0;; ++i) {
                        Log.d("myTag", String.valueOf(i));
                        try {
                            Thread.sleep(1000L);   //milliseconds
                        } catch (InterruptedException interruptedException) {
                        }
                    }
                }
            }).start();
    

  3. Implement the run() Method suggests we lower the second thread’s priority using android.os.Process.setThreadPriority rather than Thread.setPriority, but it doesn’t say why. android.os.Process.setPriority goes from –20 (highest scheduling priority) to 19 (lowest scheduling priority) inclusive. Thread.setPriority goes from 10 (highest scheduling priority) to 1 (lowest scheduling priority) inclusive. Incidentally, android.os.Process.myTid returns a different number than Thread.getId.
            //In the onCreate method.
            int myTid = android.os.Process.myTid();
            Log.d("myTag", "priority of UI thread: " + android.os.Process.getThreadPriority(myTid));
    
                //In the run method, before the infinite loop.
                android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_BACKGROUND);
                int myTid = android.os.Process.myTid();
                Log.d("myTag", "priority of second thread: " + android.os.Process.getThreadPriority(myTid));
    
    priority of UI thread: 0        (this is equal to android.os.Process.THREAD_PRIORITY_DEFAULT)
    priority of second thread: 10   (this is equal to android.os.Process.THREAD_PRIORITY_BACKGROUND)
    

  4. Print the state of each thread.
            //at the start of onCreate
            Thread.State state = currentThread.getState();
            Log.d("myTag", "state of UI thread = " + state);
    
            //at the end of onCreate
    
            Log.d("myTag", "state of second thread = " + thread.getState());
            thread.start();
            Log.d("myTag", "state of second thread = " + thread.getState());
    
    state of UI thread = RUNNABLE
    
    state of second thread = NEW
    state of second thread = WAITING
    

  5. Launch the app. Change the device’s orientation to destroy and re-create the Activity. Observe that you now have two threads running simultaneously. Things get messy if an Activity creates a thread that can run forever and does not destroy it.

    Let’s keep the thread alive only while the Activity is in the resumed state. Add three fields to MainActivity.

        Thread thread;
        int i = 0;
        boolean shouldContinue = true;
    
    Create the thread object in the onCreate method of the MainActivity, as we are currently doing, but do not call its start method there. Add the following methods to class MainActivity.
        @Override
        protected void onResume() {
            super.onResume();
            shouldContinue = true;
            thread.start();   //Create the thread.
        }
    
        @Override
        protected void onPause() {
            shouldContinue = false;   //Destroy the thread.
            super.onPause();
        }
    
    Change the for loop to
                    for (; shouldContinue; ++i) {