I have a service that runs in a separate process. The service spawns a new thread in onCreate()
method. This thread sends messages back to the service.
If I start the app manually everything works fine - messages are received by the Handler
in my service. But in my test handleMessage()
method is never get called.
How can I fix my test to make handleMessage()
method work?
Thanks!
Service:
public class MyService extends Service {
private ServiceHandler mHandler;
private final int MSG_HELLO_WORLD = 1;
private final String TAG = getClass().getSimpleName();
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
return null;
}
@Override
public void onCreate() {
Log.d(TAG, "MyService.onCreate");
super.onCreate();
mHandler = new ServiceHandler();
new Thread(new Runnable() {
@Override
public void run() {
Log.d(TAG, "Thread: run()");
while (true) {
try {
Log.d(TAG, "sleeping...");
Thread.sleep(3000);
} catch (InterruptedException e) {
Log.d(TAG, "Thread was interrupted.");
break;
}
Log.d(TAG, "sending message...");
Message msg = Message.obtain(mHandler, MSG_HELLO_WORLD);
msg.sendToTarget();
}
}
}).start();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "MyService.onStartCommand");
return START_REDELIVER_INTENT;
}
private class ServiceHandler extends Handler {
@Override
public void handleMessage(Message msg) {
Log.d(TAG, "ServiceHandler.handleMessage");
if (msg.what == MSG_HELLO_WORLD) {
Log.d(TAG, "Hello World message received!");
}
}
}
}
Test:
public class MyServiceTest extends ServiceTestCase<MyService> {
private static final String TAG = MyService.class.getSimpleName();
public MyServiceTest() {
super(MyService.class);
}
public void setUp() throws Exception {
super.setUp();
}
public void testStartService() throws Exception {
Intent startServiceIntent = new Intent(getContext(), MyService.class);
startService(startServiceIntent);
MyService myService = getService();
assertNotNull(myService);
// give the service some time to send messages
Thread.sleep(10000);
}
}
App's logcat output (handleMessage()
is called):
MyService.onCreate
MyService.onStartCommand
Thread: run()
sleeping...
sending message...
sleeping...
ServiceHandler.handleMessage
Hello World message received!
sending message...
sleeping...
Test's logcat output (handleMessage()
is not called):
MyService.onCreate
MyService.onStartCommand
Thread: run()
sleeping...
sending message...
sleeping...
sending message...
sleeping...
sending message...
sleeping...
Your question seemed interesting to me, so I started digging.
When you initialize
mHandler
in theMyService
'sonCreate()
method withmHandler = new ServiceHandler()
, theHandler
is associated with the message queue of the currently executed thread.mHandler
processMessages
from the queue that, in its turn, is handled byLooper
which is looping. As the result you can see "Hello World message received!" in logs.When you test the service its methods are called on
InstrumentationThread
(see the threads inDebugger
) whoseLooper
is prepared but not looping, so the messages can't be handled and aren't shown in logs.I'd suggest the following options:
MyServiceTest
to the thread on whichMyService
's methods are called. Write one moreunit test
for the component and then combine the test withMyServiceTest
in one common.java
test file. This will show the desiredlogcat
output.Loop the prepared
Looper
ofInstrumentationThread
inMyServiceTest
. You can do it by addingLooper.loop()
in thetestStartService()
method ofMyServiceTest
:Note: running the
Looper
will show the desiredlogcat
output, but the test won't stop because ofLooper.loop()
running indefinitely until you callLooper.quit()
.