AndroidHandlerrunWithScissors梳理流程解析

2022-10-21 18:31:54
目录
前言runWithScissors梳理流程存在的问题总结

前言

看>

public static WindowManagerService main(final Context context, final InputManagerService im,final boolean showBootMsgs, final boolean onlyCore, WindowManagerPolicy policy,ActivityTaskManagerService atm, Supplier<SurfaceControl.Transaction> transactionFactory,Supplier<Surface> surfaceFactory,
            Function<SurfaceSession, SurfaceControl.Builder> surfaceControlFactory) {
        DisplayThread.getHandler().runWithScissors(() ->
                sInstance = new WindowManagerService(context, im, showBootMsgs, onlyCore, policy,
                        atm, transactionFactory, surfaceFactory, surfaceControlFactory), 0);
        return sInstance;
}

通过 DisplayThread.getHandler() 调用了 runWithScissors 方法。

该方法的设计初衷就是:在一个线程中通过 Handler 向另外一个线程发送消息,并等待另一个线程处理完成后再继续执行。

runWithScissors

首先来看一下官方文档的描述:

同步运行指定的任务。如何当前线程和处理线程相同,则立即执行不用排队,否则就发送到别的线程进行处理,并等待他完成后再返回。另外,这种方法很危险,使用不当可能会造成死锁,毕竟是两个线程间的通信。

还有该方法被标记为>

下面我们来分析一下代码:

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

首先获取当前线程的 looper,在拿到 Handler 所属的looper,如果是同一个,就直接执行并返回 true,否则就继续往下走。

如果所属的 looper 不相同,则使用 BlockingRunnable 进行包装,并调用 postAndWait 方法:

private static final class BlockingRunnable implements Runnable {
    private final Runnable mTask;
    private boolean mDone;
    public BlockingRunnable(Runnable task) {
        mTask = task;
    }
    @Override
    public void run() {
        try {
            mTask.run();//运行在 handler 线程
        } finally {
            synchronized (this) {
                mDone = true; //标记完成
                notifyAll(); //唤醒线程
            }
        }
    }
    public boolean postAndWait(Handler handler, long timeout) {
        //使用 post 进行发送
        if (!handler.post(this)) {
            return false;
        }
        synchronized (this) {
            if (timeout > 0) {
                final long expirationTime = SystemClock.uptimeMillis() + timeout;
                while (!mDone) {
                    long delay = expirationTime - SystemClock.uptimeMillis();
                    if (delay <= 0) {
                        return false; // timeout
                    }
                    try {
                        wait(delay);
                    } catch (InterruptedException ex) {
                    }
                }
            } else {
                while (!mDone) {
                    try {
                        wait();
                    } catch (InterruptedException ex) {
                    }
                }
            }
        }
        return true;
    }
}

在 postAndWait 方法中,首先调用 post 添加到 queue 队列中,如果成功返回 true,如果发送失败,postAndWait 方法直接退出。

发送成功后,就会添加的队里中,等到合适的时候 run 方法就会执行,然后就会执行 finally 块,将 mDone 置为 true。

post() 方法执行成功后,就会进入 synchronized 代码块,需要注意的是 run 方法中也有一个 synchronized,这两个锁对象都是 this,所以说,同一时刻只能有一个代码块被执行,另一个只能进行等待。

接着就是 timeout 大于 0 并且 mDone 标志一直处于 false,则进行 wait 等待,等待结束后如果任务还没有完成,直接 return false,表示任务失败。

如果 timeout 小于0,则不需要延时,直接进行阻塞,没有超时时间,只能等待被唤醒。

最后 return true 表示任务成功。

梳理流程

1,首先判断目标线程和当前线程是否相同,相同则立即执行任务,return>

2,接着就使用 BlockingRunnable 进行包装,然后使用 post 发送。发送失败表示目标线程的 Looper 有问题,直接 return false, 表示任务失败。

3,发送成功以后,会有两个分支,一个是 run 方法中的 synchronized,还有一个是 postAndWait 中的synchronized 。这两个在同一时刻只能有一个执行。run 方法中执行任务,postAndWait 中进行延时或者直接等待。

4,最后就是延时等待结束后任务没完成则表示任务失败,如果没有延时就直接进行 wait 进行阻塞,直到被唤醒。这里没有超时逻辑,会存在一定的问题。

存在的问题

通过上面的分析,我们大底可以分析出问题的关键了,具体如下所示:

    没有超时取消逻辑延时完成后,任务如果没有完成,直接回>

    死锁

    1,如果 Runable 在没有执行的时候被移除了,例如 Handler.removeCallBack,Looper.quit,这个任务就永远得不到执行,就会导致 wait 一直等待。

    2,如果 wait 一直无法被唤醒, 并且这个时候还持有者别的锁,就会导致死锁。

    那么要如何解决呢,上面第一种也无需解决,如果它不符合你的业务,你也就不需要使用它了,第二种只需要保证当前线程没有别的锁,而且 looper 不能直接退出,需要退出的时候也需要安全退出(quitSafely方法)。

    总结

    通过分析我们也可以看出来>

    以上就是Android Handler runWithScissors 梳理流程解析的详细内容,更多关于Android Handler runWithScissors 的资料请关注易采站长站其它相关文章!