HandlerThread 阻塞 UI Android

3
我正在修改Google的Camera2 API示例程序,用于Android开发,该示例程序可以在此处找到:https://github.com/googlesamples/android-Camera2Basic 我将拍摄的图像上传到Cloudinary,很明显需要在后台线程中执行以避免阻塞UI。
然而,我遇到的问题是,即使我从理解上认为不应该阻塞UI,但在上传图像时UI确实被阻塞了。这个问题可能是由于Handler是使用后台线程的Looper创建的:
private void startBackgroundThread() {
    mBackgroundThread = new HandlerThread("CameraBackground");
    mBackgroundThread.start();
    mBackgroundHandler = new Handler(mBackgroundThread.getLooper());
}

负责将捕获的图像写入磁盘的ImageSaver类如下:

private static class ImageSaver implements Runnable {
    /**
     * The JPEG image
     */
    private final Image mImage;
    /**
     * The file we save the image into.
     */
    private final File mFile;

    public ImageSaver(Image image, File file ) {
        mImage = image;
        mFile = file;
    }

    @Override
    public void run() {
        ByteBuffer buffer = mImage.getPlanes()[0].getBuffer();
        byte[] bytes = new byte[buffer.remaining()];
        buffer.get(bytes);
        FileOutputStream output = null;
        try {
            output = new FileOutputStream(mFile);
            output.write(bytes);
            InputStream is = new ByteArrayInputStream(bytes);
            Map uploadResult = CloudinaryManager.getInstance().uploader().upload(is, ObjectUtils.asMap(
                    "api_key", CloudinaryManager.CLOUDINARY_API_KEY,
                    "api_secret", CloudinaryManager.CLOUDINARY_SECRET_KEY
            ));
            System.out.println("result");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            mImage.close();
            if (null != output) {
                try {
                    output.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

这里将ImageSaver添加到了处理程序中:
 private final ImageReader.OnImageAvailableListener mOnImageAvailableListener
        = new ImageReader.OnImageAvailableListener() {

    @Override
    public void onImageAvailable(ImageReader reader) {
        mBackgroundHandler.post(new ImageSaver(reader.acquireNextImage(), mFile));
    }
};

我很感激您提供任何帮助或建议,以指引我朝正确的方向前进。

你似乎正确地使用了HandlerThread,错误可能出现在别的地方…? - Omar Aflak
1
请显示堆栈跟踪和错误日志。我们这里不猜测。 - Andrei T
2个回答

1
我认为这是因为相机正在使用后台中的锁...由于你从ImageReader获取图像,我怀疑它会在你使用完资源之前一直保持锁定状态...因此,我的建议是在onImageAvailable方法中填充字节数组,关闭你获取的图像,并将字节数组发送到AsyncTask以执行保存。

0

我曾经遇到过同样的问题,经过一些调查和测试,发现它实际上并没有冻结用户界面,而是冻结了相机预览,在许多相机应用程序中会给人留下同样的印象。

如果您查看unLockFocus()方法,您会发现它将相机设置回正常的预览状态。

从调用此方法的时间点来看,您会发现它直到图像被保存后才会被调用:

           .
           .
           .
           CameraCaptureSession.CaptureCallback CaptureCallback
                    = new CameraCaptureSession.CaptureCallback() {

                @Override
                public void onCaptureCompleted(@NonNull CameraCaptureSession session,
                                               @NonNull CaptureRequest request,
                                               @NonNull TotalCaptureResult result) {
                    showToast("Saved: " + mFile);
                    Log.d(TAG, mFile.toString());
                    unlockFocus();
                }
            };

通过在相机保存序列的早期调用此函数,可以更早地启用预览并解锁UI。

我进行了一些实验,发现如果在获取图像后立即调用此函数并在保存之前调用它,则似乎可以正常工作 - 我还删除了captureCallback中原始的unLockFocus调用。请注意,我没有对竞争条件等进行任何适当的测试,因此我强烈建议您自己进行实验以确保您的情况能够正常工作(如果我进行更多验证,我将更新此内容):

    /**
     * This a callback object for the {@link ImageReader}. "onImageAvailable" will be called when a
     * still image is ready to be saved.
     */
    private final ImageReader.OnImageAvailableListener mOnImageAvailableListener
            = new ImageReader.OnImageAvailableListener() {

        @Override
        public void onImageAvailable(ImageReader reader) {
            Log.d(TAG,"onImageAvailable");

            //Get the image
            Image cameraImage = reader.acquireNextImage();

            //Now unlock the focus so the UI does not look locked - note that this is a much earlier point than in the
            //original Camera2Basic example from google as the original place was causing the preview to lock during any
            //image manipulation and saving.
            unlockFocus();

            //Save the image file in the background - note check you have permissions granted by user or this will cause an exception.
            mBackgroundHandler.post(new ImageSaver(getActivity().getApplicationContext(), cameraImage, outputPicFile);

        }

    };

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接