Android 系统手电筒最强适配版

时间:2021-01-09 20:39:10
    在做项目的时候,遇到了打开手电筒的功能。本来在网上相应的代码已经很多,便拿过用了。但是在测试阶段发现各种各样的问题。无论是国产的手机,还是三星的手机。并且随着系统的不同,也有着这种各样的差异。

今天就对这个问题进行统一的整理。
一、4.4及其以下:
1、 打开手电筒:

private static Camera camera;

public static void flashLight(final Context context) {
        try {
                if (camera != null) {
                    camera.release();
                    camera = null;
                }
                camera = Camera.open();
                Camera.Parameters param = camera.getParameters();
      param.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);
                camera.setParameters(param);
                camera.setPreviewTexture(new SurfaceTexture(0));
                camera.startPreview();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

2、关闭手电筒:

public static void closeFlashLight(final Context context) {
        try {
                if (camera == null) {
                    camera = Camera.open();
                }
                Camera.Parameters parameters = camera.getParameters();
                parameters.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);
                camera.setParameters(parameters);
                camera.stopPreview();
                camera.release();
                camera = null;
            }
        } catch (Exception e) {
        }
    }

3、其中:
(1)camera.setPreviewTexture(new SurfaceTexture(0)); 这行代码是为了适配三星的手机。
(2)每一次关闭手电筒后,要对camera 进行释放。

二、Android 5.0 系统在Camera 方面做了很大的改动,废弃掉了Camera类。引入了Camera2 、CameraManager,用于管理摄像头,以便于更好的处理多个摄像头的问题。 具体的细节问题,可以去看其源码。


import android.content.Context;
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.CameraDevice.StateCallback;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CameraMetadata;
import android.hardware.camera2.CaptureRequest;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Process;
import android.util.Log;
import android.util.Size;
import android.view.Surface;

import java.util.ArrayList;
/** * Manages the flashlight. */
public class FlashlightController {
    private static final String TAG = "FlashlightController";
    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
    private static final int DISPATCH_ERROR = 0;
    private static final int DISPATCH_OFF = 1;
    private static final int DISPATCH_AVAILABILITY_CHANGED = 2;
    private final CameraManager mCameraManager;
    private Handler mHandler;
    private final ArrayList<FlashlightListener> mListeners = new ArrayList<FlashlightListener>(1);
    private boolean mFlashlightEnabled;
    private String mCameraId;
    private boolean mCameraAvailable;
    private CameraDevice mCameraDevice;
    private CaptureRequest mFlashlightRequest;
    private CameraCaptureSession mSession;
    private SurfaceTexture mSurfaceTexture;
    private Surface mSurface;
    public FlashlightController(Context mContext) {
        mCameraManager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
        initialize();
    }
    public void initialize() {
        try {
            mCameraId = getCameraId();
        } catch (Throwable e) {
            LogUtil.e(TAG, "Couldn't initialize.", e);
            return;
        }
        if (mCameraId != null) {
            ensureHandler();
            mCameraManager.registerAvailabilityCallback(mAvailabilityCallback, mHandler);
        }
    }
    public synchronized void setFlashlight(boolean enabled) {
        if (mFlashlightEnabled != enabled) {
            mFlashlightEnabled = enabled;
            postUpdateFlashlight();
        }
    }
    public void killFlashlight() {
        boolean enabled;
        synchronized (this) {
            enabled = mFlashlightEnabled;
        }
        if (enabled) {
            mHandler.post(mKillFlashlightRunnable);
        }
    }
    public synchronized boolean isAvailable() {
        return mCameraAvailable;
    }
    public void addListener(FlashlightListener l) {
        synchronized (mListeners) {
            cleanUpListenersLocked(l);
            mListeners.add(l);
        }
    }
    public void removeListener(FlashlightListener l) {
        synchronized (mListeners) {
            cleanUpListenersLocked(l);
        }
    }
    private synchronized void ensureHandler() {
        if (mHandler == null) {
            HandlerThread thread = new HandlerThread(TAG, Process.THREAD_PRIORITY_BACKGROUND);
            thread.start();
            mHandler = new Handler(thread.getLooper());
        }
    }
    private void startDevice() throws CameraAccessException {
        mCameraManager.openCamera(getCameraId(), new  StateCallback() {
            @Override
            public void onOpened(CameraDevice camera) {
                mCameraDevice = camera;
                postUpdateFlashlight();
            }
            @Override
            public void onDisconnected(CameraDevice camera) {
                if (mCameraDevice == camera) {
                    dispatchOff();
                    teardown();
                }
            }
            @Override
            public void onError(CameraDevice camera, int error) {
                LogUtil.e(TAG, "Camera error: camera=" + camera + " error=" + error);
                if (camera == mCameraDevice || mCameraDevice == null) {
                    handleError();
                }
            }
        }, mHandler);
    }
    private void startSession() throws CameraAccessException {
        mSurfaceTexture = new SurfaceTexture(0,false);
        Size size = getSmallestSize(mCameraDevice.getId());
        mSurfaceTexture.setDefaultBufferSize(size.getWidth(), size.getHeight());
        mSurface = new Surface(mSurfaceTexture);
        ArrayList<Surface> outputs = new ArrayList<Surface>(1);
        outputs.add(mSurface);
        mCameraDevice.createCaptureSession(outputs, new android.hardware.camera2.CameraCaptureSession.StateCallback() {
            @Override
            public void onConfigured(CameraCaptureSession session) {
                mSession = session;
                postUpdateFlashlight();
            }
            @Override
            public void onConfigureFailed(CameraCaptureSession session) {
                LogUtil.e(TAG, "Configure failed.");
                if (mSession == null || mSession == session) {
                    handleError();
                }
            }
        }, mHandler);
    }
    private Size getSmallestSize(String cameraId) throws CameraAccessException {
        Size[] outputSizes = mCameraManager.getCameraCharacteristics(cameraId)
                .get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)
                .getOutputSizes(SurfaceTexture.class);
        if (outputSizes == null || outputSizes.length == 0) {
            throw new IllegalStateException(
                    "Camera " + cameraId + "doesn't support any outputSize.");
        }
        Size chosen = outputSizes[0];
        for (Size s : outputSizes) {
            if (chosen.getWidth() >= s.getWidth() && chosen.getHeight() >= s.getHeight()) {
                chosen = s;
            }
        }
        return chosen;
    }
    private void postUpdateFlashlight() {
        ensureHandler();
        mHandler.post(mUpdateFlashlightRunnable);
    }
    private String getCameraId() throws CameraAccessException {
        String[] ids = mCameraManager.getCameraIdList();
        for (String id : ids) {
            CameraCharacteristics c = mCameraManager.getCameraCharacteristics(id);
            Boolean flashAvailable = c.get(CameraCharacteristics.FLASH_INFO_AVAILABLE);
            Integer lensFacing = c.get(CameraCharacteristics.LENS_FACING);
            if (flashAvailable != null && flashAvailable
                    && lensFacing != null && lensFacing == CameraCharacteristics.LENS_FACING_BACK) {
                return id;
            }
        }
        return null;
    }
    private void updateFlashlight(boolean forceDisable) {
        try {
            boolean enabled;
            synchronized (this) {
                enabled = mFlashlightEnabled && !forceDisable;
            }
            if (enabled) {
                if (mCameraDevice == null) {
                    startDevice();
                    return;
                }
                if (mSession == null) {
                    startSession();
                    return;
                }
                if (mFlashlightRequest == null) {
                    CaptureRequest.Builder builder = mCameraDevice.createCaptureRequest(
                            CameraDevice.TEMPLATE_PREVIEW);
                    builder.set(CaptureRequest.FLASH_MODE, CameraMetadata.FLASH_MODE_TORCH);
                    builder.addTarget(mSurface);
                    CaptureRequest request = builder.build();
                    mSession.capture(request, null, mHandler);
                    mFlashlightRequest = request;
                }
            } else {
                if (mCameraDevice != null) {
                    mCameraDevice.close();
                    teardown();
                }
            }
        } catch (Exception e) {
            LogUtil.e(TAG, "Error in updateFlashlight", e);
            handleError();
        }
    }
    private void teardown() {
        mCameraDevice = null;
        mSession = null;
        mFlashlightRequest = null;
        if (mSurface != null) {
            mSurface.release();
            mSurfaceTexture.release();
        }
        mSurface = null;
        mSurfaceTexture = null;
    }
    private void handleError() {
        synchronized (this) {
            mFlashlightEnabled = false;
        }
        dispatchError();
        dispatchOff();
        updateFlashlight(true);
    }
    private void dispatchOff() {
        dispatchListeners(DISPATCH_OFF, false);
    }
    private void dispatchError() {
        dispatchListeners(DISPATCH_ERROR, false);
    }
    private void dispatchAvailabilityChanged(boolean available) {
        dispatchListeners(DISPATCH_AVAILABILITY_CHANGED, available);
    }
    private void dispatchListeners(int message, boolean argument) {
        synchronized (mListeners) {
            final int N = mListeners.size();
            boolean cleanup = false;
            for (int i = 0; i < N; i++) {
                FlashlightListener l = mListeners.get(i);
                if (l != null) {
                    if (message == DISPATCH_ERROR) {
                        l.onFlashlightError();
                    } else if (message == DISPATCH_OFF) {
                        l.onFlashlightOff();
                    } else if (message == DISPATCH_AVAILABILITY_CHANGED) {
                        l.onFlashlightAvailabilityChanged(argument);
                    }
                } else {
                    cleanup = true;
                }
            }
            if (cleanup) {
                cleanUpListenersLocked(null);
            }
        }
    }
    private void cleanUpListenersLocked(FlashlightListener listener) {
        for (int i = mListeners.size() - 1; i >= 0; i--) {
            FlashlightListener found = mListeners.get(i);
            if (found == null || found == listener) {
                mListeners.remove(i);
            }
        }
    }

    private final Runnable mUpdateFlashlightRunnable = new Runnable() {
        @Override
        public void run() {
            updateFlashlight(false);
        }
    };
    private final Runnable mKillFlashlightRunnable = new Runnable() {
        @Override
        public void run() {
            synchronized (this) {
                mFlashlightEnabled = false;
            }
            updateFlashlight(true);
            dispatchOff();
        }
    };
    private final CameraManager.AvailabilityCallback mAvailabilityCallback =
            new CameraManager.AvailabilityCallback() {
        @Override
        public void onCameraAvailable(String cameraId) {
            if (DEBUG) LogUtil.d(TAG, "onCameraAvailable(" + cameraId + ")");
            if (cameraId.equals(mCameraId)) {
                setCameraAvailable(true);
            }
        }
        @Override
        public void onCameraUnavailable(String cameraId) {
            if (DEBUG) LogUtil.d(TAG, "onCameraUnavailable(" + cameraId + ")");
            if (cameraId.equals(mCameraId)) {
                setCameraAvailable(false);
            }
        }
        private void setCameraAvailable(boolean available) {
            boolean changed;
            synchronized (FlashlightController.this) {
                changed = mCameraAvailable != available;
                mCameraAvailable = available;
            }
            if (changed) {
                if (DEBUG) LogUtil.d(TAG, "dispatchAvailabilityChanged(" + available + ")");
                dispatchAvailabilityChanged(available);
            }
        }
    };
    public interface FlashlightListener {

        void onFlashlightOff();

        void onFlashlightError();

        void onFlashlightAvailabilityChanged(boolean available);
    }
}

以上是FlashlightController 类。
打开手电筒:
FlashlightController .setFlashlight(true);
关闭手电筒:
FlashlightController .killFlashlight();

仅仅是这个两个方法就可以解决问题。由于5.0系统自己实现的手电筒的功能。所以FlashlightController 这个类便是来自于5.0系统的源码。改动的地方很小。

三、
以上便可以解决手电筒的问题了。但是在实际测试过程中还是有一些问题:
1、在三星拥有5.0系统的手机上,需要用一中的方法,才可以。
2、一些手机在长时间打开手电筒后,闪光灯会自动关闭。例如魅族,Nexus。除非重启手机,否则摄像头便不好用了。