runWithScissors
建议阅读:
Android | Handler.runWithScissors 解析
面试官:”Handler的runWithScissors()了解吗?为什么Google不让开发者用?”
Handler.runWithScissors(Runnable r, long timeout) 是 Android 中 Handler 提供的一个少见但非常有用的方法,常用于在特定线程(通常是主线程或某个 Handler 所在线程)上同步执行一个任务,并阻塞当前线程直到执行完成或超时。
方法签名
public final boolean runWithScissors(@NonNull Runnable r, long timeout)
参数说明
- r: 要执行的 Runnable,它会在该 Handler 所在的线程中运行。
- timeout: 最长等待时间(毫秒)。若超时则当前线程停止等待,不管 Runnable 是否已完成。
返回值
- true:Runnable 成功执行完成。
- false:Runnable 未能在 timeout 毫秒内完成,或者调用失败。
使用场景
runWithScissors() 适用于如下场景:
- 你当前不在主线程(比如子线程),但你希望同步地在主线程上执行一段代码,并等其执行完再继续。
- 不想自己实现复杂的 CountDownLatch 等同步工具时,用它可以一行代码完成“切换线程 + 等待结果”。
示例
Handler handler = new Handler(Looper.getMainLooper());
boolean success = handler.runWithScissors(() -> {
// 在主线程中执行的任务
Log.d("TAG", "This runs on main thread");
}, 2000); // 最多等待 2 秒
if (success) {
Log.d("TAG", "Runnable executed successfully");
} else {
Log.d("TAG", "Runnable execution timed out or failed");
}
注意事项
只能从非 Handler 所在线程调用:
- 如果你在 Handler 所在线程(比如主线程)调用此方法,会抛异常或造成死锁:
if (Looper.myLooper() == mLooper) {
throw new IllegalStateException(“Cannot call runWithScissors on the same thread”);
}不能在主线程中调用它来等待主线程的结果,否则直接死锁。
其内部实现使用了 BlockingRunnable,调用 post() 向目标线程发消息,然后 wait() 等待 Runnable 执行完成。
源码简析(Android 10+)
public final boolean runWithScissors(final Runnable r, long timeout) {
if (r == null) {
throw new IllegalArgumentException("runnable must not be null");
}
if (Looper.myLooper() == mLooper) {
// 同一线程,直接执行(避免死锁)
r.run();
return true;
}
BlockingRunnable br = new BlockingRunnable(r);
return br.postAndWait(this, timeout);
}
其中 BlockingRunnable.postAndWait() 实现了同步等待逻辑。
总结
特性 | 说明 |
---|---|
执行线程 | Handler 所在线程 |
当前线程是否阻塞 | 是(直到任务完成或超时) |
同线程是否允许调用 | 否,会抛异常或死锁 |
是否适合主线程中调用 | 不适合,除非你要同步执行后台线程的 Handler |
如果你想实现一个轻量级线程切换 + 同步等待效果,runWithScissors 是非常方便的工具,但要小心别在主线程中阻塞主线程自身。