Reputation: 3736
Trying to create 2 background threads and sending a message from thread0 to thread1 where it is handled by MessageHandler using the android Looper and message queue associated with thread1.
I expected 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 am starting 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>
From 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 following TacBoss suggestion below, I get thread id=450 which is expected. thread id 451 likely completed.
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;
}
Upvotes: 0
Views: 1261
Reputation: 10266
OK, this didn't pop straight up...
the problem is you create the handler within the constructor of the BGThread1 and this code is called from the main thread! and that is why the handler queue is also attached to the main thread.
public BackgroundThread1() {
super();
b1Handler = new MessageHandler(); // this is the main thread here...
}
if you want this handler to run on another thread you first have 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 would create the thread in the context you expect it to.
===================================================
An example of extending a "regular" thread:
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();
}
}
Now the handler runs on the new thread.
Upvotes: 1
Reputation: 1389
This is because the Service
lifecycle is handled on the main thread, that is : Service
methods like onCreate
, onStartCommand
, onDestruction
are called on the main thread by default, as explained in the second paragraph of android.app.Service's Class Overview
Note that services, like other application objects, run in the main thread of their hosting process.
Upvotes: 1