一图看懂——@hide Handler#runWithScissors (一个执行了就不能后悔的方法)

193 阅读3分钟

一般开发者可能不知道 Handler.runWithScissors() 方法,因为这个api 是 @hide,平时也用不到。看了下注释,WOW!很长,被一个很长又 hide 的 api 勾起了兴趣。决定分析下,结合网上文章的理解做的图片,分享给大家,希望大家能更快的明白,废话就不多说了。

什么场景可以使用呢

简单的说,就是两个线程的同步。

XejhJH.png

XejXFg.png

XevGfH.png

上图就可以了解什么是线程同步了,一般用到 syschronized 以及 wait 、notify。

Android 中 当然就要有 Android 的 特色 ,Handler。

Android 中是怎么实现的

这里贴了注解,但不用着急看,图看完了,再看这个注解就更容易明白了。

android/os/Handler.java
/**
     * Runs the specified task synchronously.
     * <p>
     * If the current thread is the same as the handler thread, then the runnable
     * runs immediately without being enqueued.  Otherwise, posts the runnable
     * to the handler and waits for it to complete before returning.
     * </p><p>
     * This method is dangerous!  Improper use can result in deadlocks.
     * Never call this method while any locks are held or use it in a
     * possibly re-entrant manner.
     * </p><p>
     * This method is occasionally useful in situations where a background thread
     * must synchronously await completion of a task that must run on the
     * handler's thread.  However, this problem is often a symptom of bad design.
     * Consider improving the design (if possible) before resorting to this method.
     * </p><p>
     * One example of where you might want to use this method is when you just
     * set up a Handler thread and need to perform some initialization steps on
     * it before continuing execution.
     * </p><p>
     * If timeout occurs then this method returns <code>false</code> but the runnable
     * will remain posted on the handler and may already be in progress or
     * complete at a later time.
     * </p><p>
     * When using this method, be sure to use {@link Looper#quitSafely} when
     * quitting the looper.  Otherwise {@link #runWithScissors} may hang indefinitely.
     * (TODO: We should fix this by making MessageQueue aware of blocking runnables.)
     * </p>
     *
     * @param r The Runnable that will be executed synchronously.
     * @param timeout The timeout in milliseconds, or 0 to wait indefinitely.
     *
     * @return Returns true if the Runnable was successfully executed.
     *         Returns false on failure, usually because the
     *         looper processing the message queue is exiting.
     *
     * @hide This method is prone to abuse and should probably not be in the API.
     * If we ever do make it part of the API, we might want to rename it to something
     * less funny like runUnsafe().
     */
public final boolean runWithScissors(@NonNull Runnable r, long timeout)

两个参数

Runnable r 同步的

long timeout 超时时间

这里并不讨论它的实现,看图说话

Xez9aT.png

再执行 runWithScissors 之后,当前线程就在 wait,等到notify 才会执行,思路很简单。

为什么说是不安全的呢

这里的不安全主要是指,notify 不会调用的情况,这样current thread 就一直在等待。

XmSzNT.png

notify 只有在 Runnable 执行完毕的才会调用,什么情况runnable 不会调用呢?

  • 死锁:需要用到 syschronized 以及 wait 、notify。就可能出现死锁的问题。这里就不讨论,如果有锁的时候调用需要特别注意使用。
  • message 在没有执行的时候被移除了,如 Handler.removeCallBack ,Looper.quit

XmpW24.png

如何避免

一定要让 Runable 所在的Message 执行。如果Looper 需要退出,用使用Looper.quitSafely()。 Looper.quitSafely() ,该方法只移除排在未来执行的队列。

XmifFx.png

总结

看到网上有人说,不安全,所以不推荐给开发者使用,本人更多的认为:其实 Google 只是为了给自己用而已, framework 用的地方还是挺多的。就放在了Handler 中方便源码使用,标记了 @hide 既然是自己用,那够用就好了啊。使用 runWithScissors 就没考虑还能cancel 的情况,必须要执行。所以源码开发者想使用就要注意了。 timeout 参数 就只是参数而已,并不会因为 time out,而 cancel 。是不是有点反人类呢!我搜了下源码 framework 没有使用返回值,test 代码使用比较多,CTS 测试总不能一直等待吧,所以设置了 timeout,使用了返回值,来判断 CTS 的成功与失败。

参考文章blog.csdn.net/liuwg1226/a…