如何在 Android 中线程化相机回调?
How to thread camera callbacks in Android?
我正在编写一个 Android 应用程序,它从相机获取持续更新并在调用 onPreviewFrame(byte[] data, Camera camera) 时处理它们。
问题是 onPreviewFrame 每秒仅被调用 8-10 次,而不是 30 次(我预计对于 30 FPS 的相机)。我认为这是因为我的 UI 正在接收所有相机回调,而不是一个单独的线程。
这是我的 SurfaceView,它持有相机并接收相机回调:
public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback, Callback {
private Camera mCamera;
...
mCamera.setPreviewCallback(new Camera.PreviewCallback() {
@Override
public void onPreviewFrame(byte[] data, Camera camera) {
//Frame received (about 8 per second)
这是我打开相机的代码(在我的主程序中 activity):
final Camera camera = getCameraInstance(); //calls camera.open()
mPreview = new CameraPreview(this, camera);
我应该如何使用线程、Asynctask 或 Handler 来让相机回调发生在一旁?我如何在 UI 线程中接收这些更新?
是的,将在主 (UI) 线程上调用回调。根据 Documentation 来自其他方法的所有相机回调都被传递到调用 open() 的线程的事件循环。
这意味着如果线程没有任何事件循环,则回调将传递到主应用程序事件循环。如果没有主应用程序事件循环,则根本不会传递回调。
在那种情况下,由于 onPreviewFrame
在主线程上被调用,因此在执行 UI 操作(例如显示动画)时处理巨大像素矩阵的任务可能会卡住, 打开菜单或即使消息打印在屏幕上。
一个解决方案是创建一个新的 HandlerThread。 HandlerThread 可以方便地启动一个具有内置循环机制的新线程。
此外,请尝试使用 setPreviewCallbackWithBuffer(),而不是 setPreviewCallback()
,因为它会重复使用一个缓冲区,而不必在每一帧上分配新的缓冲区。它通过允许重用预览帧内存来提高预览效率和帧速率。
a good tutorial for understanding Looper, AsyncTask and HandlerThread
请看这里
这是我的答案,这个问题困扰了我几个星期。
在我的例子中,我的相机是从一个用低级 C 代码创建的线程启动的。
但正如@Pavitra Kansara 回答的那样,线程需要一个事件循环,如果不需要,则可能会调用 onPreviewFrame。因此,您需要将相机打开的函数调用返回到主线程(UI),它是这样的:
((SomeActivity)context).runOnUiThread(new Runnable() {
@Override
public void run() {
try {
open camera funcalls ...
}
catch (Exception e) {
e.printStackTrace();
}
}
});
我正在编写一个 Android 应用程序,它从相机获取持续更新并在调用 onPreviewFrame(byte[] data, Camera camera) 时处理它们。
问题是 onPreviewFrame 每秒仅被调用 8-10 次,而不是 30 次(我预计对于 30 FPS 的相机)。我认为这是因为我的 UI 正在接收所有相机回调,而不是一个单独的线程。
这是我的 SurfaceView,它持有相机并接收相机回调:
public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback, Callback {
private Camera mCamera;
...
mCamera.setPreviewCallback(new Camera.PreviewCallback() {
@Override
public void onPreviewFrame(byte[] data, Camera camera) {
//Frame received (about 8 per second)
这是我打开相机的代码(在我的主程序中 activity):
final Camera camera = getCameraInstance(); //calls camera.open()
mPreview = new CameraPreview(this, camera);
我应该如何使用线程、Asynctask 或 Handler 来让相机回调发生在一旁?我如何在 UI 线程中接收这些更新?
是的,将在主 (UI) 线程上调用回调。根据 Documentation 来自其他方法的所有相机回调都被传递到调用 open() 的线程的事件循环。
这意味着如果线程没有任何事件循环,则回调将传递到主应用程序事件循环。如果没有主应用程序事件循环,则根本不会传递回调。
在那种情况下,由于 onPreviewFrame
在主线程上被调用,因此在执行 UI 操作(例如显示动画)时处理巨大像素矩阵的任务可能会卡住, 打开菜单或即使消息打印在屏幕上。
一个解决方案是创建一个新的 HandlerThread。 HandlerThread 可以方便地启动一个具有内置循环机制的新线程。
此外,请尝试使用 setPreviewCallbackWithBuffer(),而不是 setPreviewCallback()
,因为它会重复使用一个缓冲区,而不必在每一帧上分配新的缓冲区。它通过允许重用预览帧内存来提高预览效率和帧速率。
a good tutorial for understanding Looper, AsyncTask and HandlerThread
请看这里这是我的答案,这个问题困扰了我几个星期。
在我的例子中,我的相机是从一个用低级 C 代码创建的线程启动的。
但正如@Pavitra Kansara 回答的那样,线程需要一个事件循环,如果不需要,则可能会调用 onPreviewFrame。因此,您需要将相机打开的函数调用返回到主线程(UI),它是这样的:
((SomeActivity)context).runOnUiThread(new Runnable() {
@Override
public void run() {
try {
open camera funcalls ...
}
catch (Exception e) {
e.printStackTrace();
}
}
});