首页  编辑  

安卓后台静默拍照,Take photo in background without preview in Android

Tags: /Android/   Date Created:
安卓后台静默拍照,适用于5.0以下版本,高版本未测试。
https://blog.csdn.net/feiyu1947/article/details/84840126

实际上,安卓拍照需要预览没错,但预览并非需要一个SurfaceView,如果用SurfaceView来作为预览输出,那么程序后台之后,没有界面,就无法拍照了。
mTexture = new SurfaceTexture(0);
try {
 mCamera.setPreviewTexture(mTexture);
 mCamera.startPreview();
} catch (IOException e) {
 Log.e(TAG, "initiate camera failed, e: " + e.getMessage());
}

package company.camera;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.SurfaceTexture;
import android.graphics.Typeface;
import android.hardware.Camera;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import java.io.File;
import java.io.FileOutputStream;
import java.nio.ByteBuffer;
import java.util.Date;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class CameraManager implements Camera.PictureCallback, Camera.PreviewCallback {
    final String TAG = "静默拍照";
    private Camera mCamera;
    private SurfaceTexture mTexture;
    private String fileName;
    private SurfaceHolder mHolder;
    private Paint paint = new Paint();
    private String osd = "牛逼";
    private int fontHeight;
    private int fontWidth;
    private SurfaceView surfaceView;
    static private Lock lock = new ReentrantLock();  // 多线程同步锁,防止多个线程同时操作摄像头导致崩溃

    /**
     * 新建一个摄像头对象
     *
     * @param surfaceView
     */
    public CameraManager(SurfaceView surfaceView) {
        this.surfaceView = surfaceView;
        mTexture = new SurfaceTexture(0);
        mHolder = surfaceView.getHolder();
        paint.setColor(Color.WHITE);
        paint.setTypeface(Typeface.DEFAULT_BOLD);
        paint.setTextSize(80);
        paint.setAntiAlias(false);
        Paint.FontMetricsInt fm = paint.getFontMetricsInt();

        // 计算单个字符的高度和宽度,以便后面添加水印的时候计算字符的位置
        this.fontHeight = fm.bottom - fm.top;
        Rect bounds = new Rect();
        paint.getTextBounds("牛", 0, 1, bounds);
        this.fontWidth = bounds.right - bounds.left;
    }

    /**
     * 自动拍照
     *
     * @param id       摄像头ID,前置或者后置
     * @param fileName 保存的文件名
     * @return 成功返回true,失败返回false
     */
    public boolean takePhoto(int id, final String fileName, int width, int height, String osd) {
        lock.lock();
        try {
            closeCamera();  // 防止各种意外情况下,没有关闭摄像头

            this.fileName = fileName;
            this.osd = osd;
            openCamera(id, width, height);
            mCamera.takePicture(null, null, this);
            return true;
        } catch (Exception e) {
            Log.e(TAG, "拍摄失败: " + e.getMessage());
            return false;
        } finally {
            lock.unlock();
        }
    }

    /**
     * 关闭摄像头
     */
    private void closeCamera() {
        lock.lock();
        try {
            if (mCamera != null) {
                mCamera.setPreviewCallback(null);
                mCamera.stopPreview();
                mCamera.release();
            }
            mCamera = null;
        } finally {
            lock.unlock();
        }
    }

    /**
     * 打开相机
     *
     * @param id 摄像头信息,分为前置/后置摄像头, 0 前置, 1 后置
     * @return 是否成功打开某个摄像头
     */
    private boolean openCamera(int id, int width, int height) {
        // 尝试开启摄像头
        try {
            mCamera = Camera.open(id);
            mCamera.setPreviewTexture(mTexture);
            Camera.Parameters parameters = mCamera.getParameters();
            parameters.setPreviewSize(width, height);
            parameters.setPictureSize(width, height);
            mCamera.setParameters(parameters);

            mCamera.startPreview();
            return mCamera != null;
        } catch (Exception e) {
            Log.e(TAG, "打开摄像头失败: " + e.getMessage());
            return false;
        }
    }

    /*
     * 将拍照得到的字节转为bitmap,然后旋转,接着写入SD卡
     * @param data
     * @param camera
     */
    @Override
    public void onPictureTaken(byte[] data, Camera camera) {
        closeCamera();

        Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length).copy(Bitmap.Config.ARGB_8888, true);
        Canvas canvas = new Canvas(bitmap);

        String time = lyh.Utils.formatDateTime("yyyy-MM-dd HH:mm:ss EEE", new Date());
        int w = bitmap.getWidth();
        int h = bitmap.getHeight();
        int x, y;

        // 顶部时间戳
        x = 50;
        y = fontHeight;
        canvas.drawText(time, x, y, paint);

        // 右下角OSD文本
        x = w - 50 - osd.length() * fontWidth;
        y = h - fontHeight;
        canvas.drawText(osd, x, y, paint);

        // 创建并保存图片文件
        lyh.Utils.mkdirs(fileName);
        File pictureFile = new File(fileName);
        try {
            FileOutputStream fos = new FileOutputStream(pictureFile);
            bitmap.compress(Bitmap.CompressFormat.JPEG, 90, fos);
            bitmap.recycle();
            fos.close();
            Log.i(TAG, "拍摄成功并保存文件: " + pictureFile);
        } catch (Exception error) {
            Log.e(TAG, "拍摄失败: " + error.getMessage());
        }

        Canvas canvasSV = surfaceView.getHolder().lockCanvas();
        if (canvasSV != null) {
            Rect src = new Rect(0, 0, w, h);
            Rect dst = new Rect(0, 0, canvasSV.getWidth(), canvasSV.getHeight());
            canvasSV.drawBitmap(bitmap, src, dst, null);
            surfaceView.getHolder().unlockCanvasAndPost(canvasSV);
        } else
            Log.e(TAG, "屏幕锁定,无法显示输出图片");
    }

}