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");
}

注意事项

  1. 只能从非 Handler 所在线程调用

    • 如果你在 Handler 所在线程(比如主线程)调用此方法,会抛异常或造成死锁:

    if (Looper.myLooper() == mLooper) {
    throw new IllegalStateException(“Cannot call runWithScissors on the same thread”);
    }

  2. 不能在主线程中调用它来等待主线程的结果,否则直接死锁。

  3. 其内部实现使用了 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 是非常方便的工具,但要小心别在主线程中阻塞主线程自身。

当前网速较慢或者你使用的浏览器不支持博客特定功能,请尝试刷新或换用Chrome、Firefox等现代浏览器