如何使用多个流程包装单个回调

时间:2019-10-25 08:49:27

标签: kotlin-coroutines

我有一个第三方图书馆,您可以在其中订阅一些 topic 来接收有关该 topic 的更新(带有回调)。但是问题是,当您再次使用相同的 topic 调用subscribe方法时,以前的订阅将被新的静默替换。

现在,我想用Flow代替回调。所以我写了以下代码:

val flow: Flow<Message> = callbackFlow {
    lib.subscribe(topic) { message -> offer(message) }
    awaitClose { lib.unsubscribe(topic) }
}

但是当我多次致电collect时,只有最后一个收到消息。

那么我该如何实现呢?

  • 已订阅同一主题
  • 最后一个订阅者关闭/取消时,退订给定主题。

我已经看过BroadcastChannel,但是找不到任何解决第二个要求的东西。而且我刚刚开始探索Flow,所以也许有一些我简单地错过了的简单包装器。

1 个答案:

答案 0 :(得分:0)

如果您将 docker pull busybox:latest 转换为 public void doOcr(String tessDataPath, String imagePath) throws IOException { int set_only_init_params = FALSE; int oem = ITessAPI.TessOcrEngineMode.OEM_DEFAULT; PointerByReference configs = null; int configs_size = 0; String[] params = {"load_system_dawg", "tessedit_char_whitelist"}; String vals[] = {"F", ""}; //0123456789-.IThisalotfpnex //PointerByReference vars_vec = new PointerByReference(); //vars_vec.setPointer(new Pointer(params)); //PointerByReference vars_values = new PointerByReference(); //vars_values.setPointer(new StringArray(vals)); NativeSize vars_vec_size = new NativeSize(params.length); TessAPI1 api = new TessAPI1(); ITessAPI.TessBaseAPI handle = api.TessBaseAPICreate(); int rc = api.TessBaseAPIInit4(handle, tessDataPath, "eng", oem, configs, configs_size, null, null, vars_vec_size, set_only_init_params); api.TessBaseAPISetVariable(handle, "user_defined_dpi", "270"); if (rc != 0) { api.TessBaseAPIDelete(handle); //logger.error("Could not initialize tesseract."); return; } String outputbase = "C:\\Application\\ResultRenderer"; ITessAPI.TessResultRenderer renderer = api.TessHOcrRendererCreate(outputbase); api.TessResultRendererInsert(renderer, api.TessBoxTextRendererCreate(outputbase)); api.TessResultRendererInsert(renderer, api.TessTextRendererCreate(outputbase)); String dataPath = api.TessBaseAPIGetDatapath(handle); api.TessResultRendererInsert(renderer, api.TessPDFRendererCreate(outputbase, dataPath, FALSE)); int result = api.TessBaseAPIProcessPages(handle, imagePath, null, 0, renderer); if (result == FALSE) { //logger.error("Error during processing."); return; } while ((renderer = api.TessResultRendererNext(renderer)) != null) { String ext = api.TessResultRendererExtention(renderer).getString(0); String log = String.format("TessResultRendererExtention: %s\nTessResultRendererTitle: %s\nTessResultRendererImageNum: %d", ext, api.TessResultRendererTitle(renderer).getString(0), api.TessResultRendererImageNum(renderer)); } api.TessDeleteResultRenderer(renderer); api.TessBaseAPIEnd(handle); File ocrPdf = new File(outputbase + ".pdf"); if (!ocrPdf.exists()) { // TODO } } (当然,您仍然可以将其公开为更抽象的 Flow 类型),那么后续订阅者将被添加而无需重新执行传递给 SharedFlow 的块,这意味着不会替换库的原始订阅者。

Kotlin Flow 库提供了一个 Flow 扩展,这使得转换为 callbackFlow() 变得非常容易。

这个答案还有一个重要的部分。当您调用 Flow<T>.shareIn(): SharedFlow<T> 时,您需要将 SharedFlow 作为参数之一传递。这在您的用例中至关重要,因为没有它,可能不会调用 shareIn(),这意味着您不会从库中取消订阅。使用 SharingStarted.WhileSubscribed,完全退订 awaitClose() 也会导致退订原始 WhileSubscribed,这将触发对 SharedFlow 的调用。

您的代码可能如下所示:

Flow

来自 shareIn() 的文档:

<块引用>

WhileSubscribed() — 当第一个订阅者出现时开始上游流,当最后一个订阅者消失时立即停止......