面试官:“ Handler的runWithScissors()了解吗?”
The following article is from TechMerger Author Jingle Zhang
一般开发者可能不知道 Handler.runWithScissors() 方法,因为这个api 是 @hide,平时也用不到。
看了下注释,WOW!很长,被一个 很长又 hide 的 api 勾起了兴趣。决定分析下,结合网上文章的理解做的图片,分享给大家,希望大家能更快的明白,废话就不多说了。
什么场景可以使用呢
简单的说,就是两个线程的同步。
上图就可以了解什么是线程同步了,一般用到 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 超时时间
这里并不讨论它的实现,看图说话
再执行 runWithScissors 之后,当前线程就在 wait,等到notify 才会执行,思路很简单。
为什么说是不安全的呢
这里的不安全主要是指,notify 不会调用的情况,这样current thread 就一直在等待。
notify 只有在 Runnable 执行完毕的才会调用,什么情况runnable 不会调用呢?
死锁:需要用到 syschronized 以及 wait 、notify。就可能出现死锁的问题。这里就不讨论,如果有锁的时候调用需要特别注意使用。 message 在没有执行的时候被移除了,如 Handler.removeCallBack ,Looper.quit
如何避免
一定要让 Runable 所在的Message 执行。如果Looper 需要退出,用使用Looper.quitSafely()。Looper.quitSafely() ,该方法只移除排在未来执行的队列。
总结
看到网上有人说,不安全,所以不推荐给开发者使用,本人更多的认为:其实 Google 只是为了给自己用而已, framework 用的地方还是挺多的。就放在了Handler 中方便源码使用,标记了 @hide 既然是自己用,那够用就好了啊。
使用 runWithScissors 就没考虑还能cancel 的情况,必须要执行。所以源码开发者想使用就要注意了。timeout 参数 就只是参数而已,并不会因为 time out,而 cancel 。
是不是有点反人类呢!
我搜了下源码 framework 没有使用返回值,test 代码使用比较多,CTS 测试总不能一直等待吧,所以设置了 timeout,使用了返回值,来判断 CTS 的成功与失败。