HandlerThread阻止UI android

时间:2016-05-01 08:18:54

标签: android multithreading android-camera2 android-looper android-handlerthread

我正在修改Google针对Android的Camera2 API示例,可在此处找到:https://github.com/googlesamples/android-Camera2Basic

我正在将捕获的图像上传到Cloudinary,显然需要在后台线程中这样做,因此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));
    }
};

我希望得到任何帮助或建议,指出我正确的方向。

2 个答案:

答案 0 :(得分:1)

我相信它是因为摄像机正在使用背景锁定...因为你从ImageReader获取一个Image,我怀疑它是在你完成资源之前一直锁定...所以作为一个建议,我会填充onImageAvailable中的字节数组,关闭你获得的图像,并将字节数组发送到AsyncTask来执行保存

答案 1 :(得分: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);

        }

    };