Android 与 ViewModel 和 CameraX 的线程间通信最佳实践?

Android inter thread communication best practices with ViewModel and CameraX?

提问人:mo5ch 提问时间:3/11/2023 更新时间:3/18/2023 访问量:218

问:

我有以下情况(查看下面的代码):

  1. 从 CameraX 用例的循环中获取ImageProxyImageAnalyzer
  2. 分析图像(大约需要 20 毫秒)并返回一些信息,close()ImageProxy
  3. 通过 AsyncTask 分析提取的信息(大约需要 500 到 3000 毫秒)
  4. 如果成功,则显示结果

我有一些片段,这取决于任务 1 的成功程度。和 2.是(特别是 PreviewView 片段和一些结果页面)。目前,他们收到 .当图像被任一任务 1 分析时。或 2.我用 .这当然不理想,因为我还需要在我的任务中监听该状态,以防上一个图像产生了我可以显示给用户的结果。ViewModelviewModel.analyzerState.postValue(<newState>)

我现在的问题如下:
当结果准备就绪时,任务 1。或 2.可能仍在运行并产生另一个结果,然后触发 UI 更新两次。我不想这样,但我仍然希望任务并行运行,以防前一个任务失败(这种情况经常发生)。当我确定我有结果时,我希望取消任务(我已经调用了 AsyncTask,但这不起作用)。我认为我的问题是使用 作为线程间通信的消息传递工具,因为我在任务 1 中设置了状态。以及任务 2 中。并在循环中聆听它。
cancel()ViewModel

我能找到的唯一信息是使用 Kotlin 协程,这显然是为使用 而制作的,但我使用的是 Java。ViewModel

解决此类问题的标准方法是什么?如何在各种线程和片段之间安全地进行通信?

法典:

void analyzeImage(ImageProxy imageProxy) {    // loop called by CameraX
    if (viewModel.analyzerState.getValue() == 'done') {
        imageProxy.close();
        return;
    }

    viewModel.analyzerState.postValue('extracting');

    Data data = extractData(imageProxy);

    imageProxy.close();

    if (data.isValid()) {
        viewModel.analyzerState.postValue('analyzing');
        new DataAnalyzeTask.execute(data);
    } else {
        viewModel.analyzerState.postValue('idle');
    }
}

// UI listens with
viewModel.analyzerState.observe(getViewLifecycleOwner(), state -> { /*...*/ } );
Android 多线程 android-asynctask android-viewmodel android-camerax

评论


答:

0赞 mo5ch 3/18/2023 #1

我花了一些时间和很多变化,但现在我有一个适合我目的的解决方案。因此,我没有使用 和 for 通信,而是实现了我自己的类,它基本上只是一个带有一些回调的类。这些回调允许我与 UI 进行通信。然后,UI 使用 (在主线程上) 传播数据更改。为了确保我在主线程上,我在主线程处理程序中执行回调:AsyncTaskViewModelTaskRunnableViewModel

Handler mainThreadHandler = HandlerCompat.createAsync(Looper.getMainLooper());

// in Task class:
mainThreadHandler.post(new Runnable() {
    @Override
    public void run() {
        callback.onSomethingHappend(result);
    }
});

我的自定义 s 通过以下命令在线程池中执行:TaskExecutorService

ExecutorService executor = Executors.newFixedThreadPool(4);   // should be called only once when the application launches

executor.execute(new Task(data, callback));

我认为可能有一个更简单的解决方案(因为我或多或少地用我的和类重新实现了这些),但我现在不想将 Guava 添加到我的项目中。ListenableFutureTaskTaskCallback

为了确保我可以取消我的任务,一旦我得到一些结果,我就使用并将该值设置为 false:AtomicBoolean

atomicBoolean.compareAndSet(/*expectedValue=*/false, /*newValue=*/true);

以下是一些有用的资源:

https://developer.android.com/guide/background/asynchronous/java-threads https://developer.android.com/guide/background/asynchronous/listenablefuture