Why does one background thread have the same thread id as the main service thread?
Trying to create 2 background threads and post a message from thread0 to thread1 where it is handled by a MessageHandler using the android Looper and the message queue associated with thread1.
I was expecting to see separate thread IDs for threads 0,1 and the main thread. However, I was surprised to see something unexpected.
09-08 14:23:21.089: D/MQ(4514): Hi, the system is up! Today is: Sep 8, 2014 2:23:21 PM
09-08 14:23:21.099: V/MQ(4514): onStartCommand(). I am INSIDE THE main sERVICE Thread id = 1
09-08 14:23:21.099: V/MQ(4514): BackgroundThread0. Thread id = 280 sent message 0
09-08 14:23:21.099: V/MQ(4514): MessageHandler on thread Thread id = 1 received message 0
Why does BackgroundThread1 have the same thread id as the main service thread id = 1?
I install it from Eclipse using Run As and run it in a windows console window:
D:\>adb shell am startservice --user 0 -a android.intent.action.MAIN -n "com.sandbox.mq/.MainService"
Starting service: Intent { act=android.intent.action.MAIN cmp=com.sandbox.mq/.MainService }
- package com.sandbox.mq;
public class StartMainService extends Application {
final static String TAG = "MQ";
public void onCreate() {
super.onCreate();
Context context = getApplicationContext();
Log.d(TAG, "Hi, the system is up! Today is: " + DateFormat.getDateTimeInstance().format(new Date()));
}
}
-
public class MainService extends Service {
final static String TAG = "MQ";
BackgroundThread0 bthread0;
BackgroundThread1 bthread1;
public class MqBinder extends Binder {
public MqBinder(Context ctxt) {
Log.v(TAG, "MqBinder() " + "Thread id = "
+ Thread.currentThread().getId());
}
}
@Override
public IBinder onBind(Intent arg0) {
return new MqBinder(this);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.v(TAG, "onStartCommand(). I am INSIDE THE main sERVICE "
+ "Thread id = " + Thread.currentThread().getId());
bthread1 = new BackgroundThread1();
if (!bthread1.isAlive()) {
bthread1.start();
} else {
Log.v(TAG, "onStartCommand(). bthread1 was already started");
}
bthread0 = new BackgroundThread0();
if (!bthread0.isAlive()) {
bthread0.start();
} else {
Log.v(TAG, "onStartCommand(). bthread0 was already started");
}
return START_STICKY;
}
private class BackgroundThread0 extends Thread {
Handler b1Handler;
@Override
public void run() {
super.run();
b1Handler = bthread1.b1Handler;
Message msg = b1Handler.obtainMessage(MessageHandler.TYPE0);
b1Handler.sendMessage(msg);
Log.v(TAG, "BackgroundThread0. " + "Thread id = "
+ Thread.currentThread().getId() + " sent message "
+ msg.what);
}
}
private class BackgroundThread1 extends Thread {
public BackgroundThread1() {
super();
b1Handler = new MessageHandler();
}
Handler b1Handler;
@Override
public void run() {
super.run();
Looper.prepare();
Looper.loop();
}
}
private static class MessageHandler extends Handler {
static final int TYPE0 = 0;
static final int TYPE1 = 1;
static final int TYPE2 = 2;
public MessageHandler() {
}
@Override
public void handleMessage(Message msg) {
Log.v(TAG, "MessageHandler on thread " + "Thread id = "
+ Thread.currentThread().getId() + " received message "
+ msg.what);
switch (msg.what) {
case TYPE0:
break;
case TYPE1:
break;
case TYPE2:
break;
}
super.handleMessage(msg);
}
}
}
-
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.sandbox.mq" >
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-sdk
android:minSdkVersion="19"
android:targetSdkVersion="19" />
<application
android:name="com.sandbox.mq.StartMainService"
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:persistent="true"
android:theme="@style/AppTheme" >
<service android:name="com.sandbox.mq.MainService" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</service>
</application>
</manifest>
Out of eclipse, DDMS window
1 4514 Native 0 2 main
*2 4518 VmWait 0 0 GC
*3 4519 VmWait 0 0 Signal Catcher
*4 4520 Runnable 0 2 JDWP
*5 4521 VmWait 0 0 Compiler
*6 4522 Wait 0 0 ReferenceQueueDaemon
*7 4523 Wait 0 0 FinalizerDaemon
*8 4524 Wait 0 0 FinalizerWatchdogDaemon
9 4525 Native 0 0 Binder_1
10 4526 Native 0 0 Binder_2
11 4527 Native 0 0 Thread-279
After executing the below TacBoss suggestion, I get thread id = 450 which is expected. thread id 451 is probably finished.
1 11756 Native 1 1 main
*2 11760 VmWait 0 0 GC
*3 11761 VmWait 0 0 Signal Catcher
*4 11762 Runnable 0 0 JDWP
*5 11763 VmWait 0 0 Compiler
*6 11764 Wait 0 0 ReferenceQueueDaemon
*7 11765 Wait 0 0 FinalizerDaemon
*8 11766 Wait 0 0 FinalizerWatchdogDaemon
9 11767 Native 0 0 Binder_1
10 11768 Native 0 0 Binder_2
11 11770 Native 0 0 Thread-450
09-08 15:06:05.089: D/MQ(7931): Hi, the system is up! Today is: Sep 8, 2014 3:06:05 PM
09-08 15:06:05.089: V/MQ(7931): onStartCommand(). I am INSIDE THE main sERVICE Thread id = 1
09-08 15:06:06.149: D/MQ(7946): Hi, the system is up! Today is: Sep 8, 2014 3:06:06 PM
09-08 15:06:06.149: V/MQ(7946): onStartCommand(). I am INSIDE THE main sERVICE Thread id = 1
09-08 15:06:10.269: D/MQ(7964): Hi, the system is up! Today is: Sep 8, 2014 3:06:10 PM
09-08 15:06:10.269: V/MQ(7964): onStartCommand(). I am INSIDE THE main sERVICE Thread id = 1
09-08 15:06:26.339: D/MQ(8110): Hi, the system is up! Today is: Sep 8, 2014 3:06:26 PM
09-08 15:06:26.339: V/MQ(8110): onStartCommand(). I am INSIDE THE main sERVICE Thread id = 1
09-08 15:06:36.319: D/MQ(8166): Hi, the system is up! Today is: Sep 8, 2014 3:06:36 PM
09-08 15:06:36.319: V/MQ(8166): onStartCommand(). I am INSIDE THE main sERVICE Thread id = 1
09-08 15:06:37.389: D/MQ(8182): Hi, the system is up! Today is: Sep 8, 2014 3:06:37 PM
09-08 15:06:37.389: V/MQ(8182): onStartCommand(). I am INSIDE THE main sERVICE Thread id = 1
09-08 15:06:41.469: D/MQ(8200): Hi, the system is up! Today is: Sep 8, 2014 3:06:41 PM
09-08 15:06:41.469: V/MQ(8200): onStartCommand(). I am INSIDE THE main sERVICE Thread id = 1
09-08 15:08:27.439: D/MQ(8577): Hi, the system is up! Today is: Sep 8, 2014 3:08:27 PM
09-08 15:08:27.439: V/MQ(8577): onStartCommand(). I am INSIDE THE main sERVICE Thread id = 1
09-08 15:08:28.479: D/MQ(8593): Hi, the system is up! Today is: Sep 8, 2014 3:08:28 PM
09-08 15:08:28.489: V/MQ(8593): onStartCommand(). I am INSIDE THE main sERVICE Thread id = 1
09-08 15:08:32.579: D/MQ(8612): Hi, the system is up! Today is: Sep 8, 2014 3:08:32 PM
09-08 15:08:32.579: V/MQ(8612): onStartCommand(). I am INSIDE THE main sERVICE Thread id = 1
09-08 15:08:48.679: D/MQ(8630): Hi, the system is up! Today is: Sep 8, 2014 3:08:48 PM
09-08 15:08:48.679: V/MQ(8630): onStartCommand(). I am INSIDE THE main sERVICE Thread id = 1
09-08 15:29:00.949: D/MQ(11756): Hi, the system is up! Today is: Sep 8, 2014 3:29:00 PM
09-08 15:29:00.949: V/MQ(11756): onStartCommand(). I am INSIDE THE main sERVICE Thread id = 1
09-08 15:29:00.959: V/MQ(11756): BackgroundThread0. Thread id = 451 sent message 0
09-08 15:29:00.959: V/MQ(11756): MessageHandler on thread Thread id = 450 received message 0
- code changes
private class BackgroundThread1 extends Thread {
public BackgroundThread1() {
super();
start();
}
Handler b1Handler;
@Override
public void run() {
super.run();
Looper.prepare();
Looper looper = Looper.myLooper();
b1Handler = new MessageHandler(looper);
Looper.loop();
}
}
public int onStartCommand(Intent intent, int flags, int startId) {
Log.v(TAG, "onStartCommand(). I am INSIDE THE main sERVICE "
+ "Thread id = " + Thread.currentThread().getId());
bthread1 = new BackgroundThread1();
// if (!bthread1.isAlive()) {
// bthread1.start();
// } else {
// Log.v(TAG, "onStartCommand(). bthread1 was already started");
// }
bthread0 = new BackgroundThread0();
if (!bthread0.isAlive()) {
bthread0.start();
} else {
Log.v(TAG, "onStartCommand(). bthread0 was already started");
}
return START_STICKY;
}
source to share
OK it didn't show up ...
the problem is that you are creating a handler inside BGThread1's constructor and this code is called from the main thread! and therefore the handler queue is also bound to the main thread.
public BackgroundThread1() {
super();
b1Handler = new MessageHandler(); // this is the main thread here...
}
If you want this handler to run on a different thread, you first need to do something like this:
private class BackgroundThread1 extends HandlerThread {
public BackgroundThread1() {
super();
...
Some code
...
start();
b1Handler = new MessageHandler(getLooper());
// this is the main thread here... but you create the handler with the looper of the new thread!
}
...
}
This will create a thread in the context you expect from it.
=============================================== === =
An example of extending a "regular" stream:
class MyThread
extends Thread {
private Handler myHandler;
public MyThread() {
start();
}
@Override
public void run() {
Looper.prepare();
Looper looper = Looper.myLooper();
myHandler = new Handler(looper);
Looper.loop();
}
}
The handler now runs on a new thread.
source to share
This is because the lifecycle is Service
handled in the main thread, that is: Service
methods such as onCreate
, onStartCommand
are onDestruction
called on the main thread by default, as explained in the second paragraph android.app.Service Class Overview
Note that services, like other application objects, run on the main hosting thread.
source to share