前言
感觉时间真的过的飞快,转眼我都工作大半年了,仿佛学生时代的我就在昨日,而今日却要为生计而奔波。记得小时候很爱看书,那时候最想买书店那种上面写着名著,包装又很漂亮的,而且读完书还能和小伙伴们分享自己获得的新知,而如今都化为泡影,只留下为着不知怎样的未来之景而不得不看源码的我。
每次阅读源码,我都想着许多过往云烟,可以说,一切不在一样,不过,想再多又如何,倒不如按下心思,在手中记下这一切更为妥当。还是看看这些充满智慧结晶的源码吧!
正篇
本想着先深入一下post方法的,但转念还是按部就班吧,让我们接着往下看:
/** * 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) { if (r == null) { throw new IllegalArgumentException("runnable must not be null"); } if (timeout < 0) { throw new IllegalArgumentException("timeout must be non-negative"); } if (Looper.myLooper() == mLooper) { r.run(); return true; } BlockingRunnable br = new BlockingRunnable(r); return br.postAndWait(this, timeout); }
好家伙,这个注释都快看晕了,一眼下去连代码都没找到,而且又是一个没接触过的方法, 方法名:runWithScissors(),老规矩,先解释一下注释,其实注释也挺有趣的!
首先方法名头上顶了一个@hide的注解,表明这个方法不希望普通开发者使用,然后还打趣道:"此方法容易被滥用,可能不应该出现在 API 中。如果我们真的让它成为 API 的一部分,我们可能得把它重命名为像 runUnsafe() 这样不那么有趣的东西比较好。"这段话形象的告诉了我们这个方法应该成为一个冷门方法,但经过我查询资料和源码发现,这个方法用到的地方其实不少,在 Framework 中,更是很多场景都用到了,一些博客也举例了,例如比较熟悉的 WMS 启动流程中,分别在 main()
和 initPolicy()
中,通过 runWithScissors()
切换到 "android.display" 和 "android.ui" 线程去做一些初始工作:
private void initPolicy() { UiThread.getHandler().runWithScissors(new Runnable() { public void run() { // 运行在"android.ui"线程 WindowManagerPolicyThread.set(Thread.currentThread(), Looper.myLooper()); mPolicy.init(mContext, WindowManagerService.this, WindowManagerService.this); } }, 0); }
全局搜索也能看到众多应用场景:
之所以在Framework
中有这么多应用场景而不建议我们用是因为该方法存在隐患,这点在注释第二段重点说明:这种方法很危险!使用不当会导致死锁。切勿在持有任何锁时调用此方法,或以可能可重入的方式使用它。这段话表明该方法有死锁风险以及超时后,没有取消的逻辑(即通过 runWithScissors() 发送 Runnable 时可设置超时时间,但当超时唤醒时,会直接 false 退出。所以当超时退出时,这个 Runnable 依然还会在目标线程的 MessageQueue 中,并没有被移除掉,这也就导致它最终还是会被 Handler 线程调度并执行。)
接着我们再去看看它的作用:runWithScissors()
接受一个 Runnable
,并且可以设置超时时间,先简单的对入参进行校验;如果当前线程和 Handler
的处理线程一致,则直接运行 run()
方法;线程不一致,则通过 BlockingRunnable
包装一下,并执行其 postAndWait()
方法,也就是说我们可以通过阻塞的方式,向目标线程发送任务,并等待任务执行结束。
而注释又说明:在后台线程必须同步等待必须在处理程序线程上运行的任务完成的情况下,此方法有时很有用。然而,这个问题通常是糟糕设计的征兆。在采用这种方法之前,请考虑改进设计(如果可能),哈哈哈,结果仍是不希望我们直接使用该方法,如果想使用,可以考虑我们自己重新实现一个 BlockingRunnable 去使用。而且得注意安全使用 runWithScissors()
还需要满足 2 个条件:
- Handler 的 Looper 不允许退出,比如说, Android 主线程 Looper 就不允许退出;
- Looper 退出时,要使用安全退出 即
quitSafely()
方式退出;
总结
总之,这个注释特长的方法不推荐使用,仅为我们阅读源码所需要,毕竟方法存在缺陷,在阅读源码又可能会遇到,所以还是得看看的,而且这个方法还涉及Synchronized 锁和它的等待/通知机制,可以复习或学习一些临界问题,线程的同步、死锁,以及 Handler 的退出方式对消息的影响这方面的知识集。
所以,后续我也要复习一下有关这些锁啊,同步啊之类的知识了。