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

xdnvmnnf  于 2023-03-21  发布在  Android
关注(0)|答案(1)|浏览(144)

我有以下场景(查看下面的代码):
1.从CameraX的ImageAnalyzer用例循环中获取ImageProxy
1.分析图像(大约需要20毫秒)并返回一些信息,close()ImageProxy
1.通过AsyncTask分析提取的信息(大约需要500到3000毫秒)
1.如果成功,显示结果
我有一些片段依赖于任务1和2的成功程度(特别是一个PreviewView片段和一些结果页面)。目前他们得到ViewModel的通知。当图像被任务1或2分析时。我用viewModel.analyzerState.postValue(<newState>)设置了一个状态。这当然不是理想的,因为我还需要在我的任务中监听该状态,以防前面的图像生成了可以向用户显示的结果。
我现在的问题是:
当结果准备就绪时,任务1或2可能仍在运行,并产生另一个结果,这将触发UI更新两次。我不希望这样,但我仍然希望任务并行运行,以防前一个任务失败(这种情况经常发生)。当我确定有结果时,我希望任务被取消(我已经在AsyncTask上调用了cancel(),但没有起作用)我认为我的问题是将ViewModel用作线程间通信的消息传递工具,因为我在Task 1.和Task 2.中设置了状态,并在循环中侦听它。
我能找到的唯一信息是使用Kotlin协程,这显然是为与ViewModel一起工作而制作的,但我使用的是Java。
解决这类问题的标准方法是什么?我如何在不同的线程和片段之间 * 安全地 * 通信?
代码:

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 -> { /*...*/ } );
eulz3vhy

eulz3vhy1#

这花了我一些时间和很多修改,但现在我有了一个适合我的目的的解决方案。所以不是使用AsyncTaskViewModel进行通信,我实现了我自己的Task类,它基本上只是一个带有一些回调函数的Runnable,这些回调函数允许我与UI通信,然后UI通过ViewModel传播数据更改(在主线程上)。为了确保我在主线程上,我在主线程处理程序中执行回调:

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

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

我的定制Task通过ExecutorService在线程池中执行:

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

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

我认为使用ListenableFuture可能有一个更简单的解决方案(因为我或多或少地用TaskTaskCallback类重新实现了这些解决方案),但我现在不想将Guava添加到我的项目中。
为了确保可以取消任务,我使用了AtomicBoolean,并在得到一些结果后将值设置为false:

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

以下是一些有用的资源:
https://developer.android.com/guide/background/asynchronous/java-threadshttps://developer.android.com/guide/background/asynchronous/listenablefuture

相关问题