[Android] Camera callback not be invoked

The Problem

今天用 android.hardware.Camera 写了个简单的拍照函数,代码大致如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
    private void doTakePicture(int cameraId) throws Exception {
        Camera camera = Camera.open(cameraId);
        camera.lock();

        camera.setPreviewDisplay(mActivity.getSurfaceHolder());
        camera.startPreview();

        mMonitor.reset();

        PictureCallback jpegCallback = new PictureCallback() {
            /* callback to save the picture we take */
            @Override
            public void onPictureTaken(byte[] data, Camera camera) {
                try {
                    FileOutputStream fos = new FileOutputStream(mOutFile);
                    fos.write(data);
                    fos.close();
                    Log.i(TAG, "notify take picture done");
                    mMonitor.signal();  // notify done
                } catch (FileNotFoundException e) {
                    Log.e(TAG, "File not found: " + e.getMessage());
                } catch (IOException e) {
                    Log.e(TAG, "IO Exception: " + e.getMessage());
                }
            }
        };

        camera.takePicture(null, null, jpegCallback);

        // wait at most 4s for picture taken done
        boolean success = mMonitor.waitForSignal(4000);
        assertTrue(success);

        camera.stopPreview();
        camera.unlock();
        camera.release();
        camera = null;
    }

大致流程就是对Camera做一些初始化以及preview,然后调用Camera.takePicture 进行拍照,通过jpegCallback保存拍到的图像;调试过程中发现这 jpegCallback 死活都不会被调用到,也就是说照片始终不会被保存下来。

Root Cause

发现 Camera API 里有这样一句话:

This class is not thread-safe, and is meant for use from one event thread. Most long-running operations (preview, focus, photo capture, etc) happen asynchronously and invoke callbacks as necessary. Callbacks will be invoked on the event thread open(int) was called from. This class's methods must never be called from multiple threads at once.

也就是按照我这个写法,Camera.open()所在的Thread和我wait for done的Thread是同一个,假设为Thread A。但是按照文档所说的,event callback也会在Thread A上调用;所以我的wait for done在Thread A上一直等待,并block了这个线程,event callback就没有机会被Thread A调用了

从device log里也可以看到一些错误信息:

1
2
12-18 21:55:16.430 W/Camera_IPU2HwIsp(25314): Event sync poll timeout
12-18 21:55:16.440 E/Camera_IPU2HwIsp(25314):     error -110 in handling message: 1

Solution

解决办法就是将Camera.open() 和 wait for done 分配在两个不同的Thread里,问题就解决了。

因此,在下列代码我将Camera.open()放在一个新的Thread里,并用android.os.Looper来控制这个进程的退出,照片就能顺利保存下来了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
    private void doTakePicture(final int cameraId) throws Exception {
        new Thread() {
            @Override
            public void run() {
                // Set up a looper to be used by camera.
                Looper.prepare();
                Log.v(TAG, "start loopRun");

                // Save the looper so that we can terminate this thread
                // after we are done with it.
                mLooper = Looper.myLooper();

                mCamera = Camera.open(cameraId);
                mMonitor.signal();  // notify we have started

                Looper.loop();  // Blocks forever until Looper.quit() is called.
                Log.v(TAG, "initializeMessageLooper: quit.");
            }
        }.start();

        mMonitor.waitForSignal(10000);  // wait for Loop start

        mCamera.lock();
        /* perform camera operation here */
        ...
        mCamera.unlock();

        mCamera.release();
        mCamera = null;
        /* loop clean up */
        mLooper.quit();
        mLooper.getThread().join();
    }

留言