受@Josh Guilfoyle 的回答启发,我决定尝试使用反射来访问我需要的东西,以使我自己的 non-blocking 和 non-quitting Looper.loop()
。
/**
* Using reflection, steal non-visible "message.next"
* @param message
* @return
* @throws Exception
*/
private Message _next(Message message) throws Exception {
Field f = Message.class.getDeclaredField("next");
f.setAccessible(true);
return (Message)f.get(message);
}
/**
* Get and remove next message in local thread-pool. Thread must be associated with a Looper.
* @return next Message, or 'null' if no messages available in queue.
* @throws Exception
*/
private Message _pullNextMessage() throws Exception {
final Field _messages = MessageQueue.class.getDeclaredField("mMessages");
final Method _next = MessageQueue.class.getDeclaredMethod("next");
_messages.setAccessible(true);
_next.setAccessible(true);
final Message root = (Message)_messages.get(Looper.myQueue());
final boolean wouldBlock = (_next(root) == null);
if(wouldBlock)
return null;
else
return (Message)_next.invoke(Looper.myQueue());
}
/**
* Process all pending Messages (Handler.post (...)).
*
* A very simplified version of Looper.loop() except it won't
* block (returns if no messages available).
* @throws Exception
*/
private void _doMessageQueue() throws Exception {
Message msg;
while((msg = _pullNextMessage()) != null) {
msg.getTarget().dispatchMessage(msg);
}
}
现在在我的测试中(需要在 UI 线程上运行),我现在可以执行以下操作:
@UiThreadTest
public void testCallbacks() throws Throwable {
adapter = new UpnpDeviceArrayAdapter(getInstrumentation().getContext(), upnpService);
assertEquals(0, adapter.getCount());
upnpService.getRegistry().addDevice(createRemoteDevice());
// the adapter posts a Runnable which adds the new device.
// it has to because it must be run on the UI thread. So we
// so we need to process this (and all other) handlers before
// checking up on the adapter again.
_doMessageQueue();
assertEquals(2, adapter.getCount());
// remove device, _doMessageQueue()
}
我并不是说这是一个好主意,但到目前为止它一直对我有用。可能值得一试!我喜欢这一点的是Exceptions
,被扔进一些hander.post(...)
会破坏测试,否则情况并非如此。