Merge "Remove voodoo magic."

main
Jean Chalard 2013-04-12 10:59:47 +00:00 committed by Android (Google) Code Review
commit 676c97eb2d
1 changed files with 9 additions and 32 deletions

View File

@ -162,45 +162,22 @@ public class InputTestsBase extends ServiceTestCase<LatinIME> {
// on the same thread that the tests are running on to mimic the actual environment as // on the same thread that the tests are running on to mimic the actual environment as
// closely as possible. // closely as possible.
// Now, Looper#loop() never exits in normal operation unless the Looper#quit() method // Now, Looper#loop() never exits in normal operation unless the Looper#quit() method
// is called, so we need to do that at the right time so that #loop() returns at some // is called, which has a lot of bad side effects. We can however just throw an exception
// point and we don't end up in an infinite loop. // in the runnable which will unwind the stack and allow us to exit.
// After we quit, the looper is still technically ready to process more messages but private final class InterruptRunMessagesException extends RuntimeException {
// the handler will refuse to enqueue any because #quit() has been called and it // Empty class
// explicitly tests for it on message enqueuing, so we'll have to reset it so that }
// it lets us continue normal operation.
protected void runMessages() { protected void runMessages() {
// Here begins deep magic.
final Looper looper = mLatinIME.mHandler.getLooper();
mLatinIME.mHandler.post(new Runnable() { mLatinIME.mHandler.post(new Runnable() {
@Override @Override
public void run() { public void run() {
looper.quit(); throw new InterruptRunMessagesException();
} }
}); });
// The only way to get out of Looper#loop() is to call #quit() on it (or on its queue).
// Once #quit() is called remaining messages are not processed, which is why we post
// a message that calls it instead of calling it directly.
Looper.loop();
// Once #quit() has been called, the looper is not functional any more (it used to be,
// but now it SIGSEGV's if it's used again).
// It won't accept creating a new looper for this thread and switching to it...
// ...unless we can trick it into throwing out the old looper and believing it hasn't
// been initialized before.
MessageQueue queue = Looper.myQueue();
try { try {
// However there is no way of doing it externally, and the static ThreadLocal Looper.loop();
// field into which it's stored is private. } catch (InterruptRunMessagesException e) {
// So... get out the big guns. // Resume normal operation
java.lang.reflect.Field f = Looper.class.getDeclaredField("sThreadLocal");
f.setAccessible(true); // private lolwut
final ThreadLocal<Looper> a = (ThreadLocal<Looper>) f.get(looper);
a.set(null);
looper.prepare();
} catch (NoSuchFieldException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} }
} }