Handler.handleMessage is not called in the test, but is called in the application
I have a service running in a separate process. The service spawns a new thread in the onCreate()
method. This thread sends the message back to the service.
If I start the application manually, everything works fine – messages are received by the handler
in my service. But in my tests, the handleMessage()
method was never called.
How do I fix my test to make the handleMessage()
method work?
Thanks!
Services:
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);
}
}
The 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...
Logcat output of the test (handleMessage()
not called):
MyService.onCreate
MyService.onStartCommand
Thread: run()
sleeping...
sending message...
sleeping...
sending message...
sleeping...
sending message...
sleeping...
Solution
I
thought your question was interesting, so I started digging.
If I start the app manually everything works fine – messages are received by the
Handler
in my service.
When you initialize mHandler with mHandler = new ServiceHandler
() in the onCreate() method of MyService
>,
handler
is associated with the message queue of the currently executing thread. The mHandler
process Messages
are sent from the queue which Looper
handles >is looping Hello World News!”.
But in my test
handleMessage()
method is never get called.
When you test a service, its methods are called on the InstrumentationThread
(see Threads in the Debugger
), whose Looper
is prepared but not cyclical, As a result, messages cannot be processed and do not appear in the log.
How can I fix my test to make handleMessage() method work?
I suggest the following options:
- Bind
MyServiceTest
(bind) to the thread that calls theMyService
method. Write another unit test for the component, and then merge that test withMyServiceTest
into a common.java
test
file. This displays the desiredlogcat
output. Loop
the InstrumentationThread
preparedLooper
inMyServiceTest
. You can do this by addingLooper.loop
() to thetestStartService()
methodof MyServiceTest
:public void testStartService() throws Exception { Intent startServiceIntent = new Intent(getContext(), MyService.class); startService(startServiceIntent); MyService myService = getService(); assertNotNull(myService); Run the Looper (this call is to be added) Looper.loop(); Thread.sleep(10000); }
Note: Running Looper will display the desired
logcat
output, but the test will not stop becauseof Looper. loop(
) runs indefinitely until you callLooper.quit().
.