请选择 进入手机版 | 继续访问电脑版

[Android] Android开发 中Google为什么不让用Handler的runWithScissors()

[复制链接]
查看179 | 回复41 | 2021-9-14 05:23:57 | 显示全部楼层 |阅读模式
目次

Android开发

中Google为什么不让用Handler的runWithScissors()

一、序

大家好,这里是承香墨影!

  1. runWithScissors()
复制代码
是 Handler 的一个方法,被标记为 @hide,不答应 寻常 开发 者调用。

这个方法算是比较冷门,假如 口试 中被问及,口试 者不知道时,通常口试 官会换个问法:"怎样 在子线程通过 Handler 向主线程发送一个使命 ,并等主线程处理此使命 后,再继续实行 ?"。

这个场景,就可以借助

  1. runWithScissors()
复制代码
来实现。固然 该方法被标记为 @hide,但是在 Framework 中,也有不少场景利用 到它。不过它也有一些隐患,正是由于 这些隐患,让 Android 工程师将其标为 @hide,不答应 寻常 开发 者利用 。

本日 我们就来聊聊 Handler 的这个冷门的方法

  1. runWithScissors()
复制代码
,以及它大概 出现的一些题目 。

二、Handler.runWithScissors()

2.1 runWithScissors()

先撇开

  1. runWithScissors()
复制代码
方法,既然这里存在 2 个线程的通讯 ,那肯定必要 思量 多线程同步。

起首 想到的就是 Synchronized 锁和它的等待/关照 机制,而通过 Handler 跨线程通讯 时,想要发送一个「使命 」,Runnable 肯定比 Message 更得当 。

接下来,我们看看

  1. runWithScissors()
复制代码
的实现是不是如我们预想一样。

  1. public final boolean runWithScissors(final Runnable r, long timeout) {
  2. if (r == null) {
  3. throw new IllegalArgumentException("runnable must not be null");
  4. }
  5. if (timeout < 0) {
  6. throw new IllegalArgumentException("timeout must be non-negative");
  7. }
  8. if (Looper.myLooper() == mLooper) {
  9. r.run();
  10. return true;
  11. }
  12. BlockingRunnable br = new BlockingRunnable(r);
  13. return br.postAndWait(this, timeout);
  14. }
复制代码

可以看到,

  1. runWithScissors()
复制代码
担当 一个 Runnable,并且可以设置超时时间。

流程也非常简单:

  1. 先简单的对入参举行 校验;
  2. 假如 当火线 程和 Handler 的处理线程划一 ,则直接运行
    1. run()
    复制代码
    方法;
  3. 线程不划一 ,则通过 BlockingRunnable 包装一下,并实行 其
    1. postAndWait()
    复制代码
    方法;

那再继续看看 BlockingRunnable 的源码。

  1. private static final class BlockingRunnable implements Runnable {
  2. private final Runnable mTask;
  3. private boolean mDone;
  4. public BlockingRunnable(Runnable task) {
  5. mTask = task;
  6. }
  7. @Override
  8. public void run() {
  9. try {
  10. // 运行在 Handler 线程
  11. mTask.run();
  12. } finally {
  13. synchronized (this) {
  14. mDone = true;
  15. notifyAll();
  16. }
  17. }
  18. }
  19. public boolean postAndWait(Handler handler, long timeout) {
  20. if (!handler.post(this)) {
  21. return false;
  22. }
  23. synchronized (this) {
  24. if (timeout > 0) {
  25. final long expirationTime = SystemClock.uptimeMillis() + timeout;
  26. while (!mDone) {
  27. long delay = expirationTime - SystemClock.uptimeMillis();
  28. if (delay <= 0) {
  29. return false; // timeout
  30. }
  31. try {
  32. wait(delay);
  33. } catch (InterruptedException ex) {
  34. }
  35. }
  36. } else {
  37. while (!mDone) {
  38. try {
  39. wait();
  40. } catch (InterruptedException ex) {
  41. }
  42. }
  43. }
  44. }
  45. return true;
  46. }
  47. }
复制代码

待实行 的使命 ,会记入 BlockingRunnable 的 mTask,等待后续被调用实行 。

  1. postAndWait()
复制代码
的逻辑也很简单,先通过 handler 尝试将 BlockingRunnable 发出去,之后进入 Synchronized 临界区,尝试
  1. wait()
复制代码
壅闭 。

假如 设置了 timeout,则利用

  1. wait(timeout)
复制代码
进入壅闭 ,若被超时唤醒,则直接返回 false,表示使命 实行 失败。

那么现在 可以看到

  1. postAndWait()
复制代码
返回 false 有 2 个场景:

  1. Handler
    1. post()
    复制代码
    失败,表示 Looper 出题目 了;
  2. 等待超时,使命 还没有实行 竣事 ;

除了超时唤醒外,我们还必要 在使命 实行 完后,唤醒当火线 程。

回看 BlockingRunnable 的

  1. run()
复制代码
方法,
  1. run()
复制代码
被 Handler 调度并在其线程实行 。在此中 调用
  1. mTask.run()
复制代码
,mTask 即我们必要 实行 的 Runnable 使命 。实行 竣事 后,标记 mDone 并通过
  1. notifyAll()
复制代码
唤醒等待。

使命 发起线程,被唤醒后,会判定 mDone,若为 true 则使命 实行 完成,直接返回 true 退出。

2.2 Framework 中的利用

  1. runWithScissors()
复制代码
被标记为 @hide,应用开发 一样寻常 是用不上的,但是在 Framework 中,却有不少利用 场景。

Android开发

中Google为什么不让用Handler的runWithScissors()

比方 比较认识 的 WMS 启动流程中,分别在

  1. main()
复制代码
  1. initPolicy()
复制代码
中,通过
  1. runWithScissors()
复制代码
切换到 "android.display" 和 "android.ui" 线程去做一些初始工作。

  1. private void initPolicy() {
  2. UiThread.getHandler().runWithScissors(new Runnable() {
  3. public void run() {
  4. // 运行在"android.ui"线程
  5. WindowManagerPolicyThread.set(Thread.currentThread(), Looper.myLooper());
  6. mPolicy.init(mContext, WindowManagerService.this, WindowManagerService.this);
  7. }
  8. }, 0);
  9. }
复制代码

比方 上面代码,就是从 "android.display" 线程,通过切换到 "android.ui" 线程去执利用 命 。

三、runWithScissors() 的题目

看似

  1. runWithScissors()
复制代码
通过 Synchronized 的等待关照 机制,共同 Handler 发送 Runnable 实行 壅闭 使命 ,看似没有题目 ,但依然被 Android 工程师设为 @hide。

我们继续看看它的题目 。

3.1 假如 超时了,没有取消的逻辑

通过

  1. runWithScissors()
复制代码
发送 Runnable 时,可以指定超时时间。当超时唤醒时,是直接 false 退出。

当超时退出时,这个 Runnable 依然还在目标 线程的 MessageQueue 中,没有被移除掉,它终极 还是会被 Handler 线程调度并实行 。

此时的实行 ,显然并不符合我们的业务预期。

3.2 大概 造成死锁

而更严厉 的是,利用

  1. runWithScissors()
复制代码
大概 造成调用线程进入壅闭 ,而得不到唤醒,假如 当前持有别的锁,还会造成死锁。

我们通过 Handler 发送的 MessageQueue 的消息,一样寻常 都会得到实行 ,而当线程 Looper 通过

  1. quit()
复制代码
退出时,会清算 掉还未实行 的使命 ,此时发送线程,则永久 得不到唤醒。

那么在利用

  1. runWithScissors()
复制代码
时,就要求 Handler 地点 的线程 Looper,不答应 退出,或者利用
  1. quitSafely()
复制代码
方式退出。

  1. quit()
复制代码
  1. quitSafely()
复制代码
都表示退出,会去清算 对应的 MessageQueue,区别在于,
  1. qiut()
复制代码
会清算 MessageQueue 中全部 的消息,而
  1. quitSafely()
复制代码
只会清算 掉当前时间点之后(when > now)的消息,当前时间之前的消息,依然会得到实行 。

那么只要利用

  1. quitSafely()
复制代码
退出,通过
  1. runWithScissors()
复制代码
发送的使命 ,依然会被实行 。

也就是说,安全利用

  1. runWithScissors()
复制代码
要满足 2 个条件:

  1. Handler 的 Looper 不答应 退出,比方 Android 主线程 Looper 就不答应 退出;
  2. Looper 退出时,利用 安全退出
    1. quitSafely()
    复制代码
    方式退出;

四、总结时间

本日 我们先容 了一个冷门的方法

  1. runWithScissors()
复制代码
以及其原理,可以通过壅闭 的方式,向目标 线程发送使命 ,并等待使命 实行 竣事 。

固然 被它标记为 @hide,无法直接利用 ,但这都是纯软件实现,我们实在 可以本身 实现一个 BlockingRunnable 去利用 。当然本来 存在的题目 ,在利用 时也必要 注意 。

我知道就算这个方法不被标记为 @hide,利用 的场景也非常的少,但是它依然可以帮助我们思索 一些临界题目 ,线程的同步、死锁,以及 Handler 的退出方式对消息的影响。

到此这篇关于Android开发 中Google为什么不让用Handler的runWithScissors()的文章就先容 到这了,更多干系 Android runWithScissors()内容请搜刮 脚本之家从前 的文章或继续欣赏 下面的干系 文章渴望 大家以后多多支持脚本之家!


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
回复

使用道具 举报

avatar 咑啲呿迗嘡迗qc | 2021-9-15 07:54:58 | 显示全部楼层
被admin楼主的逻辑打败了!
回复

使用道具 举报

avatar 小鑫鑫鑫h | 2021-9-16 09:43:26 | 显示全部楼层
admin楼主说的我也略懂!
回复

使用道具 举报

avatar earth20011 | 2021-9-18 08:17:23 | 显示全部楼层
求加金币!
回复

使用道具 举报

avatar 123457732 | 2021-9-20 04:51:11 | 显示全部楼层
每次看到admin楼主的帖子都有惊吓!
回复

使用道具 举报

admin楼主病的不轻啊!
回复

使用道具 举报

avatar 喵呜_520 | 2021-10-1 22:12:49 | 显示全部楼层
收藏了,改天让朋友看看!
回复

使用道具 举报

avatar 天褐女孩涛 | 2021-10-4 20:30:01 | 显示全部楼层
小弟默默的路过贵宝地~~~
回复

使用道具 举报

avatar 冰下的火圆 | 2021-10-5 11:14:06 | 显示全部楼层
admin楼主,我告诉你一个你不知道的的秘密,有一个牛逼的网站,他卖的服务器是永久的,我们的网站用 服务器都是在这家买的,你可以去试试。访问地址:http://fwq.mxswl.com
回复

使用道具 举报

avatar 大圆镜2015 | 2021-10-5 22:15:50 | 显示全部楼层
关注一下!
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则