问题
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...
回答1:
Your question seemed interesting to me, 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
in the MyService
's onCreate()
method with mHandler = new ServiceHandler()
, the Handler is associated with the message queue of the currently executed thread. mHandler
process Messages from the queue that, in its turn, is handled by Looper which is looping. As the result you can see "Hello World message received!" in logs.
But in my test
handleMessage()
method is never get called.
When you test the service its methods are called on InstrumentationThread
(see the threads in Debugger
) whose Looper
is prepared but not looping, so the messages can't be handled and aren't shown in logs.
How can I fix my test to make handleMessage() method work?
I'd suggest the following options:
- Tie
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
: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 the
Looper
will show the desiredlogcat
output, but the test won't stop because ofLooper.loop()
running indefinitely until you call Looper.quit().
来源:https://stackoverflow.com/questions/24806453/handler-handlemessage-is-not-called-in-test-but-is-called-in-the-app