如何解决Camera2 API访问同步
Camera2 API允许我们指定线程(通过Handler
实例),在该线程上我们收到包含CameraDevice
,CameraCaptureSession
,CaptureResult
等的回调。这些回调中的信息可配置捕获会话,创建捕获请求并获取捕获结果。但是,当用户通过UI控制摄像头配置(例如对焦,测光)时,他是从主线程进行配置的。在这里,我们开发人员有两种选择:
- 从任何线程(包括
CameraCaptureSession.capture
)使用Camera2 API调用(例如main thread
)“ 直接 ”。在这里,我们需要管理会话状态并同步对Camera2 API的访问。 - 将所有Camera2 API调用移至“ CameraThread”。每当我们需要访问Camera2 API时,使用
Handler
将消息发送到“ CameraThread”。因此,我们实际上只会在单线程(“ CameraThread”)中使用它。
请让我澄清一下我的意思。假设我们为Camera2 API回调创建了HandlerThread
。
mCameraThread = new HandlerThread("CameraThread");
mCameraThread.start();
mHandler = new Handler(mCameraThread.getLooper());
第一种方法:
CameraCaptureSession.StateCallback
在“ CameraThread”上运行。
public void onConfigured(@NonNull CameraCaptureSession session) {
synchronized (STATE_MONITOR){
mState = State.Configured;
mSession = session;
}
}
用户可以在任何线程(包括“ MainThread”)上调用以下方法,因此我们需要同步对mSession
的访问。
public void takePicture() {
synchronized (STATE_MONITOR){
if(mState == State.Configured){
CaptureRequest request = ...;
mSession.capture(request,callback,mHandler)
}
}
}
在Camera2Basic示例中使用了这种方法。到目前为止,我仍在使用第一种方法。
第二种方法:
CameraCaptureSession.StateCallback
和前面的情况一样在“ CameraThread”上运行。
public void onConfigured(@NonNull CameraCaptureSession session) {
mState = State.Configured;
mSession = session;
}
但是,我们不直接访问mSession
,而是将消息发布到“ CameraThread”。因此,我们这里不需要同步,因为mSession
仅从单个线程访问。
public void takePicture() {
assertThatThreadIsRunning();
mHandler.post(() -> {
if(mState == State.Configured){
CaptureRequest request = ...;
mSession.capture(request,mHandler)
}
});
}
第二种方法的好处是我们可以避免任何多线程处理。
作为示例,我们可以考虑将CameraCaptureSession.CaptureCallback
用于预览捕获请求。来自此类重复请求的回调非常频繁地触发,应进行AF和AE控制。第二种方法使我们可以避免这种情况下的同步开销,我认为 可能会稍微提高性能并降低能耗。
据我从sources获得的信息,CameraDeviceImpl
和CameraCaptureSessionImpl
是线程安全的,因此两种方法都是可以接受的。
但是,我想知道第二种方法是否可行,哪种方法更好?
解决方法
它们都是可行的。 “更好”取决于一系列因素,例如代码库的大小以及代码中希望使用会话和设备的不同位置。
将回调发送到相机处理程序线程会产生一些小开销,还有更多的样板要编写,因此对于较小的应用程序,只需从正在使用的任何线程进行调用并进行适当的同步即可。
但是,随着应用程序复杂性的提高,将与相机API的所有交互保持在一个线程中变得越来越有吸引力。不仅因为您不必显式同步,还因为如果与相机对象的每次交互都发生在同一线程上,则更容易推断所有权,系统状态等。另外,由于某些相机API方法可能会长时间阻塞,因此您实际上不想长时间冻结UI。因此,将调用发送到另一个线程很有价值。
因此,这是一些额外的样板+较小的开销与无法将摄像机代码集中在一个地方以简化和平滑的折衷。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。