API 21中将原来的camera API弃用转而推荐使用新增的camera2 API,这是一个大的动作,因为新API换了架构,让开发者用起来更难了。
先来看看camera2包架构示意图:
这里引用了管道的概念将安卓设备和摄像头之间联通起来,系统向摄像头发送Capture请求,而摄像头会返回CameraMetadata。这一切建立在一个叫作CameraCaptureSession的会话中。
下面是camera2包中的主要类:
其中CameraManager是那个站在高处统管所有摄像投设备(CameraDevice)的管理者,而每个CameraDevice自己会负责建立CameraCaptureSession以及建立CaptureRequest。CameraCharacteristics是CameraDevice的属性描述类,非要做个对比的话,那么它与原来的CameraInfo有相似性。
类图中有着三个重要的callback,虽然这增加了阅读代码的难度,但是你必须要习惯,因为这是新包的风格。其中CameraCaptureSession.CaptureCallback将处理预览和拍照图片的工作,需要重点对待。
这些类是如何相互配合的?下面是简单的流程图。
我是用SurfaceView作为显示对象(当然还可以TextureView去显示,详见参考中的项目)
核心代码如下:
<code class="hljs java has-numbering"> mCameraManager = (CameraManager) <span class="hljs-keyword">this</span>.getSystemService(Context.CAMERA_SERVICE); mSurfaceView = (SurfaceView)findViewById(R.id.surfaceview); mSurfaceHolder = mSurfaceView.getHolder(); mSurfaceHolder.addCallback(<span class="hljs-keyword">new</span> SurfaceHolder.Callback() { <span class="hljs-annotation">@Override</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">surfaceCreated</span>(SurfaceHolder holder) { initCameraAndPreview(); } });</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li></ul>
<code class="hljs avrasm has-numbering"> private void initCameraAndPreview() { Log<span class="hljs-preprocessor">.d</span>(<span class="hljs-string">"linc"</span>,<span class="hljs-string">"init camera and preview"</span>)<span class="hljs-comment">;</span> HandlerThread handlerThread = new HandlerThread(<span class="hljs-string">"Camera2"</span>)<span class="hljs-comment">;</span> handlerThread<span class="hljs-preprocessor">.start</span>()<span class="hljs-comment">;</span> mHandler = new Handler(handlerThread<span class="hljs-preprocessor">.getLooper</span>())<span class="hljs-comment">;</span> try { mCameraId = <span class="hljs-string">""</span>+CameraCharacteristics<span class="hljs-preprocessor">.LENS</span>_FACING_FRONT<span class="hljs-comment">;</span> mImageReader = ImageReader<span class="hljs-preprocessor">.newInstance</span>(mSurfaceView<span class="hljs-preprocessor">.getWidth</span>(), mSurfaceView<span class="hljs-preprocessor">.getHeight</span>(), ImageFormat<span class="hljs-preprocessor">.JPEG</span>,<span class="hljs-comment">/*maxImages*/</span><span class="hljs-number">7</span>)<span class="hljs-comment">;</span> mImageReader<span class="hljs-preprocessor">.setOnImageAvailableListener</span>(mOnImageAvailableListener, mHandler)<span class="hljs-comment">;</span> mCameraManager<span class="hljs-preprocessor">.openCamera</span>(mCameraId, DeviceStateCallback, mHandler)<span class="hljs-comment">;</span> } catch (CameraAccessException e) { Log<span class="hljs-preprocessor">.e</span>(<span class="hljs-string">"linc"</span>, <span class="hljs-string">"open camera failed."</span> + e<span class="hljs-preprocessor">.getMessage</span>())<span class="hljs-comment">;</span> } }</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li></ul>
<code class="hljs java has-numbering"><span class="hljs-keyword">private</span> CameraDevice.StateCallback DeviceStateCallback = <span class="hljs-keyword">new</span> CameraDevice.StateCallback() { <span class="hljs-annotation">@Override</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onOpened</span>(CameraDevice camera) { Log.d(<span class="hljs-string">"linc"</span>,<span class="hljs-string">"DeviceStateCallback:camera was opend."</span>); mCameraOpenCloseLock.release(); mCameraDevice = camera; <span class="hljs-keyword">try</span> { createCameraCaptureSession(); } <span class="hljs-keyword">catch</span> (CameraAccessException e) { e.printStackTrace(); } } };</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li></ul>
<code class="hljs avrasm has-numbering"> private void createCameraCaptureSession() throws CameraAccessException { Log<span class="hljs-preprocessor">.d</span>(<span class="hljs-string">"linc"</span>,<span class="hljs-string">"createCameraCaptureSession"</span>)<span class="hljs-comment">;</span> mPreviewBuilder = mCameraDevice<span class="hljs-preprocessor">.createCaptureRequest</span>(CameraDevice<span class="hljs-preprocessor">.TEMPLATE</span>_PREVIEW)<span class="hljs-comment">;</span> mPreviewBuilder<span class="hljs-preprocessor">.addTarget</span>(mSurfaceHolder<span class="hljs-preprocessor">.getSurface</span>())<span class="hljs-comment">;</span> mState = STATE_PREVIEW<span class="hljs-comment">;</span> mCameraDevice<span class="hljs-preprocessor">.createCaptureSession</span>( Arrays<span class="hljs-preprocessor">.asList</span>(mSurfaceHolder<span class="hljs-preprocessor">.getSurface</span>(), mImageReader<span class="hljs-preprocessor">.getSurface</span>()), mSessionPreviewStateCallback, mHandler)<span class="hljs-comment">;</span> }</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li></ul>
<code class="hljs avrasm has-numbering">private CameraCaptureSession<span class="hljs-preprocessor">.StateCallback</span> mSessionPreviewStateCallback = new CameraCaptureSession<span class="hljs-preprocessor">.StateCallback</span>() { @Override public void onConfigured(CameraCaptureSession session) { Log<span class="hljs-preprocessor">.d</span>(<span class="hljs-string">"linc"</span>,<span class="hljs-string">"mSessionPreviewStateCallback onConfigured"</span>)<span class="hljs-comment">;</span> mSession = session<span class="hljs-comment">;</span> try { mPreviewBuilder<span class="hljs-preprocessor">.set</span>(CaptureRequest<span class="hljs-preprocessor">.CONTROL</span>_AF_MODE, CaptureRequest<span class="hljs-preprocessor">.CONTROL</span>_AF_MODE_CONTINUOUS_PICTURE)<span class="hljs-comment">;</span> mPreviewBuilder<span class="hljs-preprocessor">.set</span>(CaptureRequest<span class="hljs-preprocessor">.CONTROL</span>_AE_MODE, CaptureRequest<span class="hljs-preprocessor">.CONTROL</span>_AE_MODE_ON_AUTO_FLASH)<span class="hljs-comment">;</span> session<span class="hljs-preprocessor">.setRepeatingRequest</span>(mPreviewBuilder<span class="hljs-preprocessor">.build</span>(), mSessionCaptureCallback, mHandler)<span class="hljs-comment">;</span> } catch (CameraAccessException e) { e<span class="hljs-preprocessor">.printStackTrace</span>()<span class="hljs-comment">;</span> Log<span class="hljs-preprocessor">.e</span>(<span class="hljs-string">"linc"</span>,<span class="hljs-string">"set preview builder failed."</span>+e<span class="hljs-preprocessor">.getMessage</span>())<span class="hljs-comment">;</span> } } }<span class="hljs-comment">;</span></code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li></ul>
<code class="hljs java has-numbering"><span class="hljs-keyword">private</span> CameraCaptureSession.CaptureCallback mSessionCaptureCallback = <span class="hljs-keyword">new</span> CameraCaptureSession.CaptureCallback() { <span class="hljs-annotation">@Override</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onCaptureCompleted</span>(CameraCaptureSession session, CaptureRequest request, TotalCaptureResult result) { <span class="hljs-comment">// Log.d("linc","mSessionCaptureCallback, onCaptureCompleted");</span> mSession = session; checkState(result); } <span class="hljs-annotation">@Override</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onCaptureProgressed</span>(CameraCaptureSession session, CaptureRequest request, CaptureResult partialResult) { Log.d(<span class="hljs-string">"linc"</span>,<span class="hljs-string">"mSessionCaptureCallback, onCaptureProgressed"</span>); mSession = session; checkState(partialResult); } <span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title">checkState</span>(CaptureResult result) { <span class="hljs-keyword">switch</span> (mState) { <span class="hljs-keyword">case</span> STATE_PREVIEW: <span class="hljs-comment">// NOTHING</span> <span class="hljs-keyword">break</span>; <span class="hljs-keyword">case</span> STATE_WAITING_CAPTURE: <span class="hljs-keyword">int</span> afState = result.get(CaptureResult.CONTROL_AF_STATE); <span class="hljs-keyword">if</span> (CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED == afState || CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED == afState || CaptureResult.CONTROL_AF_STATE_PASSIVE_FOCUSED == afState || CaptureResult.CONTROL_AF_STATE_PASSIVE_UNFOCUSED == afState) { <span class="hljs-comment">//do something like save picture</span> } <span class="hljs-keyword">break</span>; } } };</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li><li>37</li><li>38</li></ul>
按下capture按钮:
<code class="hljs cs has-numbering"> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onCapture</span>(View view) { <span class="hljs-keyword">try</span> { Log.i(<span class="hljs-string">"linc"</span>, <span class="hljs-string">"take picture"</span>); mState = STATE_WAITING_CAPTURE; mSession.setRepeatingRequest(mPreviewBuilder.build(), mSessionCaptureCallback, mHandler); } <span class="hljs-keyword">catch</span> (CameraAccessException e) { e.printStackTrace(); } }</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li></ul>
测试用genemotion模拟器,直接调用笔记本的摄像头。
配置图如下:
demo界面如下图:
源码:
请参考github中的两个demo项目:
https://github.com/pinguo-yuyidong/Camera2
https://github.com/googlesamples/android-Camera2Basic