API 21中將原來的 camera API 棄用轉而推薦使用新增的 camera2 API,這是一個大的動做,由於新 API 換了架構,讓開發者用起來更難了。session
先來看看 camera2包架構示意圖:架構
這裏引用了管道的概念將安卓設備和攝像頭之間聯通起來,系統向攝像頭髮送 Capture 請求,而攝像頭會返回 CameraMetadata。這一切創建在一個叫做 CameraCaptureSession 的會話中。ide
下面是 camera2包中的主要類:oop
其中 CameraManager 是那個站在高處統管全部攝像投設備(CameraDevice)的管理者,而每一個 CameraDevice 本身會負責創建 CameraCaptureSession 以及創建 CaptureRequest。測試
CameraCharacteristics 是 CameraDevice 的屬性描述類,非要作個對比的話,那麼它與原來的 CameraInfo 有類似性。ui
類圖中有着三個重要的 callback,雖然這增長了閱讀代碼的難度,可是你必需要習慣,由於這是新包的風格。其中 CameraCaptureSession.CaptureCallback 將處理預覽和拍照圖片的工做,須要重點對待。this
這些類是如何相互配合的?下面是簡單的流程圖。code
我是用 SurfaceView 做爲顯示對象(固然還能夠 TextureView 去顯示,詳見參考中的項目)orm
核心代碼以下:對象
mCameraManager = (CameraManager) this.getSystemService(Context.CAMERA_SERVICE); mSurfaceView = (SurfaceView)findViewById(R.id.surfaceview); mSurfaceHolder = mSurfaceView.getHolder(); mSurfaceHolder.addCallback(new SurfaceHolder.Callback() { @Override public void surfaceCreated(SurfaceHolder holder) { initCameraAndPreview(); } });
private void initCameraAndPreview() { Log.d("linc","init camera and preview"); HandlerThread handlerThread = new HandlerThread("Camera2"); handlerThread.start(); mHandler = new Handler(handlerThread.getLooper()); try { mCameraId = ""+CameraCharacteristics.LENS_FACING_FRONT; mImageReader = ImageReader.newInstance(mSurfaceView.getWidth(), mSurfaceView.getHeight(), ImageFormat.JPEG,/*maxImages*/7); mImageReader.setOnImageAvailableListener(mOnImageAvailableListener, mHandler); mCameraManager.openCamera(mCameraId, DeviceStateCallback, mHandler); } catch (CameraAccessException e) { Log.e("linc", "open camera failed." + e.getMessage()); } }
private CameraDevice.StateCallback DeviceStateCallback = new CameraDevice.StateCallback() { @Override public void onOpened(CameraDevice camera) { Log.d("linc","DeviceStateCallback:camera was opend."); mCameraOpenCloseLock.release(); mCameraDevice = camera; try { createCameraCaptureSession(); } catch (CameraAccessException e) { e.printStackTrace(); } } };
private void createCameraCaptureSession() throws CameraAccessException { Log.d("linc","createCameraCaptureSession"); mPreviewBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); mPreviewBuilder.addTarget(mSurfaceHolder.getSurface()); mState = STATE_PREVIEW; mCameraDevice.createCaptureSession( Arrays.asList(mSurfaceHolder.getSurface(), mImageReader.getSurface()), mSessionPreviewStateCallback, mHandler); }
private CameraCaptureSession.StateCallback mSessionPreviewStateCallback = new CameraCaptureSession.StateCallback() { @Override public void onConfigured(CameraCaptureSession session) { Log.d("linc","mSessionPreviewStateCallback onConfigured"); mSession = session; try { mPreviewBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE); mPreviewBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH); session.setRepeatingRequest(mPreviewBuilder.build(), mSessionCaptureCallback, mHandler); } catch (CameraAccessException e) { e.printStackTrace(); Log.e("linc","set preview builder failed."+e.getMessage()); } } };
private CameraCaptureSession.CaptureCallback mSessionCaptureCallback = new CameraCaptureSession.CaptureCallback() { @Override public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request, TotalCaptureResult result) { // Log.d("linc","mSessionCaptureCallback, onCaptureCompleted"); mSession = session; checkState(result); } @Override public void onCaptureProgressed(CameraCaptureSession session, CaptureRequest request, CaptureResult partialResult) { Log.d("linc","mSessionCaptureCallback, onCaptureProgressed"); mSession = session; checkState(partialResult); } private void checkState(CaptureResult result) { switch (mState) { case STATE_PREVIEW: // NOTHING break; case STATE_WAITING_CAPTURE: int afState = result.get(CaptureResult.CONTROL_AF_STATE); if (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) { //do something like save picture } break; } } };
按下 capture 按鈕:
public void onCapture(View view) { try { Log.i("linc", "take picture"); mState = STATE_WAITING_CAPTURE; mSession.setRepeatingRequest(mPreviewBuilder.build(), mSessionCaptureCallback, mHandler); } catch (CameraAccessException e) { e.printStackTrace(); } }
測試用 genemotion 模擬器,直接調用筆記本的攝像頭。
配置圖以下:
demo 界面以下圖:
源碼: