CameraManager是通过获取系统服务方式获取CameraManager对象
- // 获取CameraManager 相机设备管理器
- mCameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
activity被显示出来(执行到Activity的onResume方法)时,判断显示相机预览的控件TextTure是否可用,可用的话则打开相机;否则注册SurfaceTextureListener监听器对象,当可用是会回调onSurfaceTextureAvailable()函数,在此函数中进行打开相机:
1、创建activity时第一次走到OnResume()时,会先走else逻辑,因为此时TextTure还不可用,需要注册SurfaceTextureListener监听器,监听器中监听到TextTure可用时(即onSurfaceTextureAvailable)这时会首次打开摄像头;
2、后面再进入到activity的OnResume()时,则直接打开摄像头。
- @Override
- protected void onResume() {
- super.onResume();
- if (texture.isAvailable()) {
- openCamera(texture.getWidth(), texture.getHeight(), mCameraId);
- } else {
- texture.setSurfaceTextureListener(textureListener);
- }
- startBackgroundThread();
- }
-
-
- /**
- * TextureView 生命周期响应
- */
- private final TextureView.SurfaceTextureListener textureListener = new TextureView.SurfaceTextureListener() {
- @Override //创建
- public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
- //当TextureView创建完成,打开指定摄像头相机
- openCamera(width, height, mCameraId);
- }
-
- @Override //尺寸改变
- public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
-
- }
-
- @Override //销毁
- public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
- return false;
- }
-
- @Override //更新
- public void onSurfaceTextureUpdated(SurfaceTexture surface) {
-
- }
- };
- /**
- * 打开指定摄像头ID的相机
- *
- * @param width
- * @param height
- * @param cameraId
- */
- private void openCamera(int width, int height, int cameraId) {
- if (ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
- // TODO: Consider calling
- //return;
- requestPermission();
- }
- try {
- mSurfaceWidth = width;
- mSurfaceHeight = height;
- getCameraId(cameraId);
- CameraCharacteristics characteristics = mCameraManager.getCameraCharacteristics(mCameraId + "");
- StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
- // 获取设备方向
- int rotation = getWindowManager().getDefaultDisplay().getRotation();
- int totalRotation = sensorToDeviceRotation(characteristics, rotation);
- boolean swapRotation = totalRotation == 90 || totalRotation == 270;
- int rotatedWidth = mSurfaceWidth;
- int rotatedHeight = mSurfaceHeight;
- if (swapRotation) {
- rotatedWidth = mSurfaceHeight;
- rotatedHeight = mSurfaceWidth;
- }
- // 获取最佳的预览尺寸
- mPreviewSize = getPreferredPreviewSize(map.getOutputSizes(SurfaceTexture.class), rotatedWidth, rotatedHeight);
- if (swapRotation) {
- texture.setAspectRatio(mPreviewSize.getWidth(), mPreviewSize.getHeight());
- } else {
- texture.setAspectRatio(mPreviewSize.getHeight(), mPreviewSize.getWidth());
- }
- if (mImageReader == null) {
- // 创建一个ImageReader对象,用于获取摄像头的图像数据,maxImages是ImageReader一次可以访问的最大图片数量
- mImageReader = ImageReader.newInstance(mPreviewSize.getWidth(), mPreviewSize.getHeight(),
- ImageFormat.JPEG, 2);
- mImageReader.setOnImageAvailableListener(mOnImageAvailableListener, mBackgroundHandler);
- }
- //检查是否支持闪光灯
- Boolean available = characteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE);
- mFlashSupported = available == null ? false : available;
- mCameraManager.openCamera(mCameraId + "", mStateCallback, null);
- } catch (CameraAccessException e) {
- e.printStackTrace();
- }
- }
1、要打开摄像头,需要先检测app的相机权限;
2、记录TextTure的宽高值;
3、通过cameramanager对象获取指定摄像头的特性信息CameraCharacteristics(下文详解);
4、通过characteristics对象获取相机支持的可用流配置;
5、获取android手机当前摆放的方向;
6、根据设备方向和摄像头方向,经过处理后得出最终的预览页控件的宽高大小(可能会发生旋转)。
7、从CameraCharacteristics中选择一个最匹配TextTure大小的宽高值;
8、然后重新设定TextTure的大小;
9、创建ImageReader图像读取流对象,ImageReader可以让应用直接访问到相机的image 数据,并渲染到Surface上,也就是TextTure控件上;创建ImageReader需要指定预览页的宽、高和图像的格式,和最大image数。
10、给ImageReader对象设置监听器OnImageAvailableListener; (ImageReader对象在打开摄像头显示预览这里其实不是必须的,可以在实际拍照的时候创建也可以)
11、检查摄像头是否支持开启闪光灯;
12、cameramanager对象调用openCamera()打开指定摄像头,并注册了状态回调对象StateCallback,因为openCamera()函数内部其实做了很多事情,是一个耗时动作,所以这里使用了回调函数异步执行。
13、执行打开摄像头动作后,当检测到摄像头已成功打开,则回调到onOpened(),可以获取到摄像头对象CameraDevice,这个类很重要。接下来就可以在该函数中创建预览了;
14、配置相机预览页大小,创建一个显示预览图像的缓冲区Surface。
15、通过CameraDevice构建一个预览请求构建器对象CaptureRequest.Builder,通过这个构建器设置预览请求对象的相关信息,比如指定预览数据显示位置,即Surface;
16、通过CameraDevice建立一个捕获会话CameraCaptureSession,有了这个会话对象后,后面的所有动作都是基于这个会话对象来操作了;创建会话时,也是注册了一个会话状态回调对象异步地执行接下来的动作;
17、当会话成功建立后,会回调onConfigured()函数;可以在该函数中继续设置预览请求对象的属性,比如设置自动对焦,设置闪光灯模式;
18、最后,预览请求构建器对象调用build()构建了一个捕获请求对象,并通过captureSession会话设置一个无休止的预览请求(即预览),即mCaptureSession.setRepeatingRequest(mPreviewRequest, null, mBackgroundHandler);
通过这种无休止地发生预览请求实现了相机的预览。
19、到这里TextTure对象中的Surface上就成功展示了相机预览画面数据了。