Andriod - Camera2笔记


前言

自己总结的camera2的简单使用


一、使用步骤及介绍

使用步骤

1.申请相机权限和存储读写权限
2.创建CameraManager对象,获取里面的摄像头信息
2.创建surfaceHolder并为其添加状态监听器,重写里面的surfaceCreated方法
3.在surfaceCreated方法中,创建一个UI线程HandlerThread,也可以不创建
4.调用CameraManager的openCamera()方法打开相机
openCamera()方法有三个参数:
1.相机id
2.摄像头状态监听器:CameraDevice.StateCallback
3.用于相机运行的Ui线程,没有创建UI线程的话也可以为null,为null时默认在主线程运行
5.上一步需要一个摄象头状态监听器,所以这里创建一个CameraDevice.StateCallback
在这个监听器中有一个onOpened方法,这个方法是摄像头打开成功后会调用的

代码步骤

1.创建图像捕获状态监听器:CameraCaptureSession.CaptureCallback
2.创建相机捕获会话状态监听器:CameraCaptureSession.StateCallback,在onConfigured方法里面设置摄像头参数
3.创建相机状态监听器:CameraDevice.StateCallback,在onOpened方法中创建相机捕获会话
4.创建CameraManager对象,获取设备的摄像头信息
5.创建预览组件状态监听器:android.view.SurfaceHolder.Callback,在surfaceCreated方法中创建相机线程并打开相机,线程也可以不创建
6.申请权限,权限申请成功后创建预览组件管理器并添加状态监听器:SurfaceHolder

实际步骤

先申请权限,权限到手后先创建SurfaceHolder,然后在SurfaceHolder中添加SurfaceHolder.Callback,
SurfaceHolder.Callback中需要用到CameraManager和CameraDevice.StateCallback,
CameraDevice.StateCallback中又需要用到CameraCaptureSession.StateCallback,
CameraCaptureSession.StateCallback中又需要用到CameraCaptureSession.CaptureCallback,
所以写代码的时候先创建最后一个,看起来有点绕

二、实例代码

使用SurfaceView实现预览

package com.eyetracking.camera2preview;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CaptureRequest;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.widget.Toast;

import java.util.Arrays;

public class MainActivity extends AppCompatActivity implements SurfaceHolder.Callback {
    private SurfaceView surfaceView;
    private SurfaceHolder holder;
    private CameraDevice cameraDevice;
    private HandlerThread handlerThread;
    private Handler myHandler;
    private CameraManager manager;
    private CameraCaptureSession previeSession;
    private CaptureRequest.Builder previeBuilder;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        surfaceView = (SurfaceView) findViewById(R.id.surfaceView);
        holder = surfaceView.getHolder();
        checkCameraPermission();

    }

/*
SurfaceHolder.Callback  ***********************************************************************************************
 */

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        Log.d("SurfaceHolders", "SurfaceHolder创建成功!");
        String cameraId = getCameras();
        //创建一个UI线程
        handlerThread = new HandlerThread("camera");
        handlerThread.start();
        myHandler = new Handler(handlerThread.getLooper());

        try {
            if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
                return;
            }
            manager.openCamera(cameraId, new CameraDevice.StateCallback() {
                @Override
                public void onOpened(@NonNull CameraDevice camera) {
                    Log.d("DeviceCallback", "摄像头打开成功!");
                    cameraDevice = camera;
                    try {
                        cameraDevice.createCaptureSession(Arrays.asList(holder.getSurface()), new CameraCaptureSession.StateCallback() {
                            //摄像机设备完成自身配置后会调用此方法,并且会话可以开始处理捕获请求。
                            @Override
                            public void onConfigured(@NonNull CameraCaptureSession session) {
                                Log.d("CaptureSessionCallback", "摄像机设备已完成自身配置,并且会话可以开始处理捕获请求!");
                                previeSession = session;
                                try {
                                    previeBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
                                    //设置预览画面显示的surface
                                    previeBuilder.addTarget(holder.getSurface());
                                    // previeBuilder.set()方法参数解析:第一个参数表示设置的是什么东西,比如闪光灯,第二个参数表示把要设置的东西设置成什么样,比如关闭闪光灯,打开闪光灯
                                    //可以设置的内容参考https://www.apiref.com/android-zh/android/hardware/camera2/CaptureRequest.html
                                    // 设置对焦模式-自动对焦
                                    previeBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
                                    // 设置闪光灯模式-自动打开闪光灯
                                    previeBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);

                                    //setRepeatingRequest()方法参数说明:
                                    // 第二个参数为CameraCaptureSession.StateCallback(),没什么作用,可以写null
                                    // 第三个参数为预览的hndler线程,为null的时候表示在主线程预览
                                    previeSession.setRepeatingRequest(previeBuilder.build(), null, myHandler);
                                } catch (CameraAccessException e) {
                                    Log.d("CaptureSessionCallback", "cameraCaptureSession访问摄像头失败");
                                }
                            }

                            //如果会话无法按请求进行配置,则调用此方法。
                            @Override
                            public void onConfigureFailed(@NonNull CameraCaptureSession session) {
                                Log.d("CaptureSessionCallback", "会话无法按请求进行配置!");
                            }
                        }, myHandler);
                    } catch (CameraAccessException e) {
                        e.printStackTrace();
                        Log.d("DeviceCallback", "CaptureRequest.Builder访问摄像头失败");
                    }


                }

                @Override
                public void onDisconnected(@NonNull CameraDevice camera) {

                }

                @Override
                public void onError(@NonNull CameraDevice camera, int error) {
                    closeCamera();
                    Toast.makeText(MainActivity.this, "摄像头打开错误", Toast.LENGTH_SHORT).show();
                }
            }, myHandler);
        } catch (CameraAccessException e) {
            Log.d("SurfaceHolders", "cameraManager访问摄像头失败");
        }

    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {

    }

    public String getCameras() {
        String[] cameras = new String[0];
        String cameid = "1";
        try {
            manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
            cameras = manager.getCameraIdList();
            for (int i = 0; i < cameras.length; i++) {
                CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameras[i]);
                Integer facing = characteristics.get(CameraCharacteristics.LENS_FACING);

                //判断是否为后摄,LENS_FACING_BACK为后摄  LENS_FACING_FRONT为前摄 LENS_FACING_EXTERNAL为USB摄像头
                if (facing == CameraCharacteristics.LENS_FACING_BACK) {
                    cameid = cameras[i];
                    Log.d("摄像头ID", cameras[i]);
                }
            }
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
        return cameid;
    }

    //关闭摄像头
    public void closeCamera() {
        if (cameraDevice == null) {
            return;
        }
        cameraDevice.close();
        cameraDevice = null;
    }

    //关闭线程
    private void destoryHandler() {

        if (handlerThread == null) {
            return;
        }
        handlerThread.quitSafely();
        try {
            handlerThread.join(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        closeCamera();
        destoryHandler();
    }

    private void checkCameraPermission() {
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED &&
                ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE},
                    1);
        } else {
            //有权限
            Log.d("MainActivity", "有权限");
            holder.addCallback(this);
        }
    }

    //权限请求回调
    @Override
    public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
        switch (requestCode) {
            case 1:
                if (grantResults != null && grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    holder.addCallback(this);
                } else {
                    Toast.makeText(this, "拒绝权限,请去设置里面手动开启权限", Toast.LENGTH_SHORT).show();
                }
                break;
        }
    }
}

使用TextureView实现预览

这里说明以下TextureView和可以在view层对图片显示优化,镜像翻转等操作,SurfaceView不行

package com.eyetracking.camera2preview2;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.SurfaceTexture;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CaptureRequest;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.util.Log;
import android.view.Surface;
import android.view.TextureView;
import android.widget.Toast;

import java.util.Arrays;

public class MainActivity extends AppCompatActivity implements TextureView.SurfaceTextureListener {
    private TextureView textureView;
    private CameraDevice cameraDevice;
    private HandlerThread handlerThread;
    private Handler myHandler;
    private CameraManager manager;
    private CameraCaptureSession previeSession;
    private CaptureRequest.Builder previeBuilder;
    private Surface mSurface;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textureView = (TextureView) findViewById(R.id.textureView);
        checkCameraPermission();
    }

    @Override
    public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
        Log.d("SurfaceHolders", "SurfaceHolder创建成功!");
        //设置预览分辨率
        surface.setDefaultBufferSize(1920, 1080);
        mSurface = new Surface(surface);

        String cameraId = getCameras();
        //创建一个UI线程
        handlerThread = new HandlerThread("camera");
        handlerThread.start();
        myHandler = new Handler(handlerThread.getLooper());
        try {
            if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
                return;
            }
            manager.openCamera(cameraId, new CameraDevice.StateCallback() {
                @Override
                public void onOpened(@NonNull CameraDevice camera) {
                    Log.d("DeviceCallback", "摄像头打开成功!");
                    cameraDevice = camera;
                    try {
                        cameraDevice.createCaptureSession(Arrays.asList(mSurface), new CameraCaptureSession.StateCallback() {
                            //摄像机设备完成自身配置后会调用此方法,并且会话可以开始处理捕获请求。
                            @Override
                            public void onConfigured(@NonNull CameraCaptureSession session) {
                                Log.d("CaptureSessionCallback", "摄像机设备已完成自身配置,并且会话可以开始处理捕获请求!");
                                previeSession = session;
                                try {
                                    previeBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
                                    //设置预览画面显示的surface
                                    previeBuilder.addTarget(mSurface);

                                    // previeBuilder.set()方法参数解析:第一个参数表示设置的是什么东西,比如闪光灯,第二个参数表示把要设置的东西设置成什么样,比如关闭闪光灯,打开闪光灯
                                    //可以设置的内容参考https://www.apiref.com/android-zh/android/hardware/camera2/CaptureRequest.html
                                    // 设置对焦模式-自动对焦
                                    previeBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
                                    // 设置闪光灯模式-自动打开闪光灯
                                    previeBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);

                                    //setRepeatingRequest()方法参数说明:
                                    // 第二个参数为CameraCaptureSession.StateCallback(),没什么作用,可以写null
                                    // 第三个参数为预览的hndler线程,为null的时候表示在主线程预览
                                    previeSession.setRepeatingRequest(previeBuilder.build(), null, myHandler);
                                } catch (CameraAccessException e) {
                                    Log.d("CaptureSessionCallback", "cameraCaptureSession访问摄像头失败");
                                }
                            }

                            //如果会话无法按请求进行配置,则调用此方法。
                            @Override
                            public void onConfigureFailed(@NonNull CameraCaptureSession session) {
                                Log.d("CaptureSessionCallback", "会话无法按请求进行配置!");
                            }
                        }, myHandler);
                    } catch (CameraAccessException e) {
                        e.printStackTrace();
                        Log.d("DeviceCallback", "CaptureRequest.Builder访问摄像头失败");
                    }


                }

                @Override
                public void onDisconnected(@NonNull CameraDevice camera) {

                }

                @Override
                public void onError(@NonNull CameraDevice camera, int error) {
                    closeCamera();
                    Toast.makeText(MainActivity.this, "摄像头打开错误", Toast.LENGTH_SHORT).show();
                }
            }, myHandler);
        } catch (CameraAccessException e) {
            Log.d("SurfaceHolders", "cameraManager访问摄像头失败");
        }

    }

    @Override
    public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {

    }

    @Override
    public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
        return false;
    }

    @Override
    public void onSurfaceTextureUpdated(SurfaceTexture surface) {

    }

    public String getCameras() {
        String[] cameras = new String[0];
        String cameid = "1";
        try {
            manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
            cameras = manager.getCameraIdList();
            for (int i = 0; i < cameras.length; i++) {
                CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameras[i]);
                Integer facing = characteristics.get(CameraCharacteristics.LENS_FACING);

                //判断是否为后摄,LENS_FACING_BACK为后摄  LENS_FACING_FRONT为前摄 LENS_FACING_EXTERNAL为USB摄像头
                if (facing == CameraCharacteristics.LENS_FACING_BACK) {
                    cameid = cameras[i];
                    Log.d("摄像头ID", cameras[i]);
                }
            }
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
        return cameid;
    }

    //关闭摄像头
    public void closeCamera() {
        if (cameraDevice == null) {
            return;
        }
        cameraDevice.close();
        cameraDevice = null;
    }

    //关闭线程
    private void destoryHandler() {

        if (handlerThread == null) {
            return;
        }
        handlerThread.quitSafely();
        try {
            handlerThread.join(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        closeCamera();
        destoryHandler();
    }

    private void checkCameraPermission() {
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED &&
                ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE},
                    1);
        } else {
            //有权限
            Log.d("MainActivity", "有权限");
            textureView.setSurfaceTextureListener(this);
        }
    }

    //权限请求回调
    @Override
    public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
        switch (requestCode) {
            case 1:
                if (grantResults != null && grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    textureView.setSurfaceTextureListener(this);
                } else {
                    Toast.makeText(this, "拒绝权限,请去设置里面手动开启权限", Toast.LENGTH_SHORT).show();
                }
                break;
        }
    }
}

实现预览和拍照功能

package com.eyetracking.camera2picture;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.ImageFormat;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.TotalCaptureResult;
import android.media.Image;
import android.media.ImageReader;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.HandlerThread;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.widget.ImageButton;
import android.widget.Toast;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Arrays;

public class MainActivity extends AppCompatActivity implements SurfaceHolder.Callback {
    private ImageButton btPicture;
    private SurfaceView surfaceView;
    private SurfaceHolder holder;
    private CameraDevice cameraDevice;
    private HandlerThread handlerThread;
    private Handler myHandler;
    private CameraManager manager;
    private CameraCaptureSession previeSession;
    private CaptureRequest.Builder previeBuilder;
    private CaptureRequest.Builder captureBuilder;
    private ImageReader imageReader;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        surfaceView = (SurfaceView) findViewById(R.id.surfaceView);
        btPicture = (ImageButton) findViewById(R.id.picture);
        holder = surfaceView.getHolder();
        checkCameraPermission();
    }

    public void btPicture(View v) {
        Toast.makeText(getApplicationContext(), "点击拍照", Toast.LENGTH_SHORT).show();
        openCapture();
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        setImageReader();
        Log.d("SurfaceHolders", "SurfaceHolder创建成功!");
        String cameraId = getCameras();
        //创建一个UI线程
        handlerThread = new HandlerThread("camera");
        handlerThread.start();
        myHandler = new Handler(handlerThread.getLooper());
        try {
            if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
                return;
            }
            manager.openCamera(cameraId, new CameraDevice.StateCallback() {
                @Override
                public void onOpened(@NonNull CameraDevice camera) {
                    Log.d("DeviceCallback", "摄像头打开成功!");
                    cameraDevice = camera;
                    try {
                        cameraDevice.createCaptureSession(Arrays.asList(holder.getSurface(), imageReader.getSurface()), new CameraCaptureSession.StateCallback() {
                            @Override
                            public void onConfigured(@NonNull CameraCaptureSession session) {
                                Log.d("CaptureSessionCallback", "摄像机设备已完成自身配置,并且会话可以开始处理捕获请求!");
                                previeSession = session;
                                openPreview();
                            }
                            //如果会话无法按请求进行配置,则调用此方法。
                            @Override
                            public void onConfigureFailed(@NonNull CameraCaptureSession session) {
                                Log.d("CaptureSessionCallback", "会话无法按请求进行配置!");
                            }
                        }, myHandler);
                    } catch (CameraAccessException e) {
                        e.printStackTrace();
                        Log.d("DeviceCallback", "CaptureRequest.Builder访问摄像头失败");
                    }
                }

                @Override
                public void onDisconnected(@NonNull CameraDevice camera) {
                    previeSession.close();
                    closeCamera();
                }

                @Override
                public void onError(@NonNull CameraDevice camera, int error) {
                    previeSession.close();
                    closeCamera();
                    Toast.makeText(MainActivity.this, "摄像头打开错误", Toast.LENGTH_SHORT).show();
                }
            }, myHandler);
        } catch (CameraAccessException e) {
            Log.d("SurfaceHolders", "cameraManager访问摄像头失败");
        }

    }

    public void openCapture() {
        if (previeSession != null) {
            try {
                //设置拍照参数
                captureBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
                captureBuilder.addTarget(imageReader.getSurface());
                captureBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
                captureBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
                //停止预览状态
                previeSession.stopRepeating();
                previeSession.abortCaptures();
                //执行拍照
                previeSession.capture(captureBuilder.build(), new CameraCaptureSession.CaptureCallback() {
                    //当图像捕捉已完全完成并且所有结果元数据可用时调用此方法。
                    @Override
                    public void onCaptureCompleted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) {
                        super.onCaptureCompleted(session, request, result);
                        Log.d("CaptureCallbacks", "拍摄完成,重新打开预览");
                        openPreview();
                    }
                }, myHandler);
            } catch (CameraAccessException e) {
                Log.d("CaptureSessionCallback", "cameraCaptureSession访问摄像头失败");
            }
        }
    }

    public void openPreview() {
        try {
            previeBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
            previeBuilder.addTarget(holder.getSurface());
            previeBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
            previeBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
            previeSession.setRepeatingRequest(previeBuilder.build(), null, myHandler);
        } catch (CameraAccessException e) {
            Log.d("CaptureSessionCallback", "cameraCaptureSession访问摄像头失败");
        }
    }


    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        closeCamera();
        destoryHandler();
    }

    private void setImageReader() {
        imageReader = ImageReader.newInstance(1920, 1080, ImageFormat.JPEG, 1);
        imageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() {
            @Override
            public void onImageAvailable(ImageReader reader) {
                Log.d("pictureListener", "取出图片数据");
                Image image = reader.acquireNextImage();
                ByteBuffer buffer = image.getPlanes()[0].getBuffer();
                byte[] bytes = new byte[buffer.remaining()];
                buffer.get(bytes);//由缓冲区存入字节数组
                Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
                try {
                    String filePath = null;
                    if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
                        if (Build.VERSION.SDK_INT < 29) {
                            filePath = Environment.getExternalStorageDirectory().getCanonicalPath() + "/" + System.currentTimeMillis() + ".jpg";
                        } else {
                            filePath = getExternalFilesDir(null).getAbsolutePath() + "/" + System.currentTimeMillis() + ".jpg";
                        }
                    }
                    //将图片数据写入图片文件
                    FileOutputStream outStream = new FileOutputStream(filePath);
                    bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outStream);
                    outStream.flush();
                    outStream.close();
                    image.close();
                    Log.d("pictureListener", "图片保存成功");
                } catch (IOException e) {
                    e.printStackTrace();
                    Log.d("pictureListener", "图片保存失败");
                }
            }
        }, myHandler);
    }

    public String getCameras() {
        String[] cameras = new String[0];
        String cameid = "1";
        try {
            manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
            cameras = manager.getCameraIdList();
            for (int i = 0; i < cameras.length; i++) {
                CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameras[i]);
                Integer facing = characteristics.get(CameraCharacteristics.LENS_FACING);

                //判断是否为后摄,LENS_FACING_BACK为后摄  LENS_FACING_FRONT为前摄 LENS_FACING_EXTERNAL为USB摄像头
                if (facing == CameraCharacteristics.LENS_FACING_BACK) {
                    cameid = cameras[i];
                    Log.d("摄像头ID", cameras[i]);
                }
            }
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
        return cameid;
    }

    //关闭摄像头
    public void closeCamera() {
        if (previeSession != null) {
            previeSession.close();
            previeSession = null;
        }

        if (cameraDevice != null) {
            cameraDevice.close();
            cameraDevice = null;
        }
    }

    //关闭线程
    private void destoryHandler() {
        if (handlerThread != null) {
            handlerThread.quitSafely();
            try {
                handlerThread.join(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        closeCamera();
        destoryHandler();
    }

    private void checkCameraPermission() {
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED &&
                ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE},
                    1);
        } else {
            //有权限
            Log.d("MainActivity", "有权限");
            holder.addCallback(this);
        }
    }

    //权限请求回调
    @Override
    public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
        switch (requestCode) {
            case 1:
                if (grantResults != null && grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    holder.addCallback(this);
                } else {
                    Toast.makeText(this, "拒绝权限,请去设置里面手动开启权限", Toast.LENGTH_SHORT).show();
                }
                break;
        }
    }

}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">
    <RelativeLayout
        android:id="@+id/ls"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_alignParentRight="true"
        android:background="#070707">

        <ImageButton
            android:id="@+id/picture"
            android:layout_width="96px"
            android:layout_height="96px"
            android:layout_centerInParent="true"
            android:layout_marginLeft="20dp"
            android:layout_marginRight="20dp"
            android:onClick="btPicture"
            tools:src="@android:drawable/ic_menu_camera" />
    </RelativeLayout>
    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_toLeftOf="@+id/ls">

        <SurfaceView
            android:id="@+id/surfaceView"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    </FrameLayout>
</RelativeLayout>

使用ImageReader实现不显示预览画面,仅获取预览数据(AI人脸识别可用)

package com.eyetracking.camera2preview3;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.ImageFormat;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CaptureRequest;
import android.media.Image;
import android.media.ImageReader;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.HandlerThread;
import android.util.Log;
import android.widget.Toast;

import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Arrays;

public class MainActivity extends AppCompatActivity {
    private CameraDevice cameraDevice;
    private HandlerThread handlerThread;
    private Handler myHandler;
    private CameraManager manager;
    private CameraCaptureSession previeSession;
    private CaptureRequest.Builder previeBuilder;
    private ImageReader imageReader;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        checkCameraPermission();
    }

    private void setImageReader() {
        imageReader = ImageReader.newInstance(1920, 1080, ImageFormat.JPEG, 2);
        imageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() {
            @Override
            public void onImageAvailable(ImageReader reader) {
                Log.d("pictureListener", "取出图片数据");
                Image image = reader.acquireNextImage();
                ByteBuffer buffer = image.getPlanes()[0].getBuffer();
                byte[] bytes = new byte[buffer.remaining()];
                buffer.get(bytes);//由缓冲区存入字节数组
                Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
                try {
                    String filePath = null;
                    if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
                        if (Build.VERSION.SDK_INT < 29) {
                            filePath = Environment.getExternalStorageDirectory().getCanonicalPath() + "/" + System.currentTimeMillis() + ".jpg";
                        } else {
                            filePath = getExternalFilesDir(null).getAbsolutePath() + "/" + System.currentTimeMillis() + ".jpg";
                        }
                    }
                    //将图片数据写入图片文件
                    FileOutputStream outStream = new FileOutputStream(filePath);

                    bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outStream);
                    outStream.flush();
                    outStream.close();
                    image.close();
                    Log.d("pictureListener", "图片保存成功");
                } catch (IOException e) {
                    e.printStackTrace();
                    Log.d("pictureListener", "图片保存失败");
                }
            }
        }, myHandler);
        previewData();
    }

    public void previewData() {
        Log.d("SurfaceHolders", "SurfaceHolder创建成功!");
        String cameraId = getCameras();
        //创建一个UI线程
        handlerThread = new HandlerThread("camera");
        handlerThread.start();
        myHandler = new Handler(handlerThread.getLooper());

        try {
            if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
                return;
            }
            manager.openCamera(cameraId, new CameraDevice.StateCallback() {
                @Override
                public void onOpened(@NonNull CameraDevice camera) {
                    Log.d("DeviceCallback", "摄像头打开成功!");
                    cameraDevice = camera;
                    try {
                        cameraDevice.createCaptureSession(Arrays.asList(imageReader.getSurface()), new CameraCaptureSession.StateCallback() {
                            //摄像机设备完成自身配置后会调用此方法,并且会话可以开始处理捕获请求。
                            @Override
                            public void onConfigured(@NonNull CameraCaptureSession session) {
                                Log.d("CaptureSessionCallback", "摄像机设备已完成自身配置,并且会话可以开始处理捕获请求!");
                                previeSession = session;
                                try {
                                    previeBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
                                    //设置预览画面显示的surface
                                    previeBuilder.addTarget(imageReader.getSurface());
                                    // previeBuilder.set()方法参数解析:第一个参数表示设置的是什么东西,比如闪光灯,第二个参数表示把要设置的东西设置成什么样,比如关闭闪光灯,打开闪光灯
                                    //可以设置的内容参考https://www.apiref.com/android-zh/android/hardware/camera2/CaptureRequest.html
                                    // 设置对焦模式-自动对焦
                                    previeBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
                                    // 设置闪光灯模式-自动打开闪光灯
                                    previeBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);

                                    //setRepeatingRequest()方法参数说明:
                                    // 第二个参数为CameraCaptureSession.StateCallback(),没什么作用,可以写null
                                    // 第三个参数为预览的hndler线程,为null的时候表示在主线程预览
                                    previeSession.setRepeatingRequest(previeBuilder.build(), null, myHandler);
                                } catch (CameraAccessException e) {
                                    Log.d("CaptureSessionCallback", "cameraCaptureSession访问摄像头失败");
                                }
                            }

                            //如果会话无法按请求进行配置,则调用此方法。
                            @Override
                            public void onConfigureFailed(@NonNull CameraCaptureSession session) {
                                Log.d("CaptureSessionCallback", "会话无法按请求进行配置!");
                            }
                        }, myHandler);
                    } catch (CameraAccessException e) {
                        e.printStackTrace();
                        Log.d("DeviceCallback", "CaptureRequest.Builder访问摄像头失败");
                    }


                }

                @Override
                public void onDisconnected(@NonNull CameraDevice camera) {

                }

                @Override
                public void onError(@NonNull CameraDevice camera, int error) {
                    closeCamera();
                    Toast.makeText(MainActivity.this, "摄像头打开错误", Toast.LENGTH_SHORT).show();
                }
            }, myHandler);
        } catch (CameraAccessException e) {
            Log.d("SurfaceHolders", "cameraManager访问摄像头失败");
        }

    }

    public String getCameras() {
        String[] cameras = new String[0];
        String cameid = "1";
        try {
            manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
            cameras = manager.getCameraIdList();
            for (int i = 0; i < cameras.length; i++) {
                CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameras[i]);
                Integer facing = characteristics.get(CameraCharacteristics.LENS_FACING);

                //判断是否为后摄,LENS_FACING_BACK为后摄  LENS_FACING_FRONT为前摄 LENS_FACING_EXTERNAL为USB摄像头
                if (facing == CameraCharacteristics.LENS_FACING_BACK) {
                    cameid = cameras[i];
                    Log.d("摄像头ID", cameras[i]);
                }
            }
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
        return cameid;
    }

    //关闭摄像头
    public void closeCamera() {
        if (cameraDevice == null) {
            return;
        }
        cameraDevice.close();
        cameraDevice = null;
    }

    //关闭线程
    private void destoryHandler() {

        if (handlerThread == null) {
            return;
        }
        handlerThread.quitSafely();
        try {
            handlerThread.join(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        closeCamera();
        destoryHandler();
    }

    private void checkCameraPermission() {
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED &&
                ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE},
                    1);
        } else {
            //有权限
            Log.d("MainActivity", "有权限");
            setImageReader();
        }
    }

    //权限请求回调
    @Override
    public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
        switch (requestCode) {
            case 1:
                if (grantResults != null && grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    setImageReader();
                } else {
                    Toast.makeText(this, "拒绝权限,请去设置里面手动开启权限", Toast.LENGTH_SHORT).show();
                }
                break;
        }
    }

}

总结

camera代码相对简单,更容易理解,camera2代码更复杂,流程更精细,同样对数据的可操作性更高,以上代码均可直接复制粘贴使用

  • 20
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值