如何以同步方式包装黑盒异步调用?

时间:2018-09-14 01:48:44

标签: java android asynchronous concurrency synchronization

我正在我的Android应用程序中使用专有的第三方框架-具体来说是Zebra的EMDK-及其使用的两种公开方法:

.read().cancelRead()是异步的,每个过程花费一秒钟到5整秒的时间。我需要能够向他们发送垃圾邮件而不会崩溃我的应用程序,并确保每个行都不会被两次调用。我该怎么做呢?我本身没有方法的访问权限,反编译器只会给我运行时存根。

编辑:我也不知道这两个调用何时完成。

2 个答案:

答案 0 :(得分:1)

将异步程序更改为阻塞程序是解决此问题的更普遍要求。

在Java中,我们可以使用CountDownLatch(以及Phaser)或LockSupport + Atomic来做到这一点。

例如,如果需要将异步调用asyncDoSomethingAwesome(param, callback)更改为阻塞调用,我们可以编写这样的“包装器”方法:

ResultType doSomethingAwesome(ParamType param) {
    AtomicReference<ResultType> resultContainer = new AtomicReference<>();
    Thread callingThread = Thread.currentThread();
    asyncDoSomethingAwesome(param, result -> {
        resultContainer.set(result);
        LockSupport.unpark(callingThread);
    });
    ResultType result;
    while ((result = resultContainer.get()) == null) {
        LockSupport.park();
    }
    return result;
}

我认为这足以解决您的问题。但是,在编写阻塞程序时,即使底层接口不能正常工作,我们通常也希望“超时”来保持系统稳定,例如:

ResultType doSomethingAwesome(ParamType param, Duration timeout) throws TimeoutException {
    AtomicReference<ResultType> resultContainer = new AtomicReference<>();
    Thread callingThread = Thread.currentThread();
    asyncDoSomethingAwesome(param, result -> {
        resultContainer.set(result);
        LockSupport.unpark(callingThread);
    });
    ResultType result;
    long deadline = Instant.now().plus(timeout).toEpochMilli();
    while ((result = resultContainer.get()) == null) {
        if (System.currentTimeMillis() >= deadline) {
            throw new TimeoutException();
        }
        LockSupport.parkUntil(deadline);
    }
    return result;
}

有时,我们需要对线程之间的信号进行更精细的管理,尤其是在编写并发库时。例如,当我们需要知道阻塞线程是否从另一个调用LockSupport.unpark的线程接收到信号,或者该线程是否成功通知了阻塞线程时,使用Java标准库通常不容易实现。因此,我设计了另一个具有更完善机制的库来解决此问题: https://github.com/wmx16835/experimental_java_common/blob/master/alpha/src/main/java/mingxin/wang/common/concurrent/DisposableBlocker.java

DisposableBlocker的支持下,生活将变得更加轻松:)

ResultType doSomethingAwesome(ParamType param, Duration timeout) throws TimeoutException {
    // We can use org.apache.commons.lang3.mutable.MutableObject instead of AtomicReference,
    // because this object will never be accessed concurrently
    MutableObject<ResultType> resultContainer = new MutableObject<>();
    DisposableBlocker blocker = new DisposableBlocker();
    asyncDoSomethingAwesome(param, result -> {
        resultContainer.setValue(result);
        blocker.unblock();
    });
    if (!blocker.blockFor(timeout)) {
        throw new TimeoutException();
    }
    return resultContainer.getValue();
}

答案 1 :(得分:0)

可能不建议这样做,因为我不能100%地确定您要实现什么/也不需要该结构,但是可以将它们包装在AsyncTask中吗?然后在父级AsyncTask或后台线程中:

AsyncTask1.execute().get(); //get will block until complete
AsyncTask2.execute().get(); //get will block until complete

这是假设您可以通过某种方式知道您正在完成的通话。

相关问题