最近在逛GooglePlay应用市场的时候发现很多的手电筒功能居然都带有广告感觉非常的不可思议的。而且这些应用不仅仅有广告而且安装包还特别的大,我本来还想下载一个来玩玩的,后来看了他们的东西感觉也不过如此的。主要功能还是打开手电筒的功能,有没有其他的新颖的地方了,为了能让大家更好的有一个属于的自己的手电筒功能,这里我将整理一个比较完美适配各个机型的手电筒出来。
    在着手准备开发的时候我们更多的时候可能会想着去百度或者google上寻找相应的代码,但是后来我发现市面上的这些代码更多的时候只是一个Demo,如果要完美的适配更多的记性问题还是挺多的,而且很多的代码还是之前的了,比如说5.0,6.0的代码就没有去适配了。因为之前一直都会研究下Android的源代码,而系统也是有自带的手电筒的功能的,于是我在思考是不是可以将系统中手电筒的源代码拿过来然后进行整合就可以了

因为之前已经下载了Android的源代码了,所以这里我就直接使用Everything(该软件是一款在Window上全局搜索文件,而且速度还非常的快) 软件通过搜索Flashlight关键字就可以找到所有该名的开头的.java文件的。通过搜索我们找到了FlashlightController.java 在 /android-25/com/android/systemui/statusbar/policy,其实我们在下载Android SDK的时候就已经会把该源代码下载下来了,并不需要下载所有的Android代码的。

打开手电筒在Android5.0以上跟Android5.0以下的实现还有点不一样的。而且在Android5.0以下打开手电筒也还是有点不一样的。下面我们分别来介绍各个版本下的具体实现:

Android5.0以下的实现

//开启手电筒
private Camera start() throws Throwable {mCamera = Camera.open();if(mCamera == null) {//表示当前手机没有前置摄像头return null;}mParameters = mCamera.getParameters();mParameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);mCamera.setParameters(mParameters);mCamera.startPreview();return mCamera;
}//关闭手电筒
private void close() throws Throwable {mParameters.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);mCamera.setParameters(mParameters);mCamera.stopPreview();mCamera.release();
}

从上面的代码中我们可以看出Android5.0以下的实现方式特别的简单,只是通过Camera.open()来获取一个Camera就可以打开手电筒了,这个大家都不必多说的。网上的大部分的实现也都是这样子的了。

Android6.0以下的实现
    Android5.0到Android6.0之间的实现跟其他的版本也还有点不同的,由于google废弃了Camera而采用了CameraService来实现打开相机,同时打开手电筒通过开启相机的闪光灯的方式来实现:

  1. 首先获取cameraId。

    /**
    * 获取相机的cameraId,如果cameraId为空表示该手机不支持闪光灯
    * 还有一种可能就是该Camera的权限被拒绝了,所以一直拿不到CameraService的
    * @return
    * @throws CameraAccessException
    */
    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public 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;
    }
  2. 接着在CameraDevice.StateCallback接口的onOpened()回调方法中获取CameraDevice

    private final CameraDevice.StateCallback mCameraListener = new CameraDevice.StateCallback() {@Overridepublic void onOpened(CameraDevice camera) {//这里表示获取CameraDevice成功了,然后再执行下一步mCameraDevice = camera;postUpdateFlashlight();}@Overridepublic void onDisconnected(CameraDevice camera) {//该回调接口表示连接底层的CameraService失败,然后重新释放资源if (mCameraDevice == camera) {dispatchError(ERROR_CODE_CAMERA_IN_USE);teardown();}}@Overridepublic void onError(CameraDevice camera, int error) {Log.e(TAG, "Camera error: camera = " + camera + " error=" + error);if (camera == mCameraDevice || mCameraDevice == null) {// 我们在这里处理获取CameraDevice的一些错误码// 因为在获取CameraDevice的时候会有些出错误的handleError(errorCode);}}
    };private void startDevice() {//表示用户未授权给手电筒if (ContextCompat.checkSelfPermission(mContext, "android.permission.CAMERA") != 0) {dispatchError(ERROR_CODE_NOT_PERMISSION);return;}try {mCameraManager.openCamera(mCameraId, mCameraListener, mHandler);} catch (CameraAccessException e) {e.printStackTrace();} catch (SecurityException e1) {Log.e(TAG, "The System will reject the camera permission and not open camera");} catch (Throwable throwable) {Log.e(TAG, "Couldn't open the camera use the cameraId by the CameraManager");}
    }
    
  3. 接着通过CameraCaptureSession.StateCallback的onConfigured()回调方法获取CameraCaptureSession

    private final CameraCaptureSession.StateCallback mSessionListener =new CameraCaptureSession.StateCallback() {@Overridepublic void onConfigured(CameraCaptureSession session) {mSession = session;postUpdateFlashlight();}@Overridepublic void onConfigureFailed(CameraCaptureSession session) {Log.e(TAG, "Configure failed.");if (mSession == null || mSession == session) {handleError(ERROR_CODE_UNKNOW);}}
    };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<>(1);outputs.add(mSurface);mCameraDevice.createCaptureSession(outputs, mSessionListener, 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;
    }
  4. 当我们CameraDevice和CameraCaptureSession,这个时候我们就可以控制手电筒了

    //开启手电筒功能
    if (mFlashlightRequest == null || (mFlashlightRequest.get(CaptureRequest.FLASH_MODE)).intValue() != 2) {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;mFlashlightEnabled = true;
    }//关闭手电筒功能
    if (mFlashlightRequest != null) {CaptureRequest.Builder builder =     mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);//在这里我们只要设置一下FLASH_MODE就可以关闭手电筒,这样子我们不去释放资源的话,再下次打开手电筒的时候就可以非常快速的打开了,但是这里也有一个特别需要注意的是这样子虽然可以加快打开手电筒的速度,但是如果我们不释放资源的话,则其他的应用是开不起手电筒功能的,这样子也是有利也有弊的。在开发的时候我们要特别的注意这个问题。builder.set(CaptureRequest.FLASH_MODE, CameraMetadata.FLASH_MODE_OFF);builder.addTarget(mSurface);CaptureRequest request = builder.build();mSession.capture(request, null, mHandler);mFlashlightRequest = request;mFlashlightEnabled = false;
    }
  5. 当我们不需要使用手电筒功能的时候需要对该资源进行释放,不然别的应用将不能开启手电筒功能

    private void teardown() {if (mCameraDevice != null) {mCameraDevice.close();}mCameraDevice = null;mSession = null;mFlashlightRequest = null;if (mSurface != null) {mSurface.release();mSurfaceTexture.release();}mSurface = null;mSurfaceTexture = null;
    }

    通过上面打开手电筒的代码我们发现android5.0上的实现是比较复杂了,因为google对camera进行了大规模的改动和重新架构了,废弃了之前的Camera而采用了CameraManager去实现。但是在有些变态的手机上则会遇到获取cameraId出现SecurityException的异常,比如说在有些魅蓝手机装有阿里的云Os系统上使用这种方式去打开手电筒则不行的,还是需要使用第一种方式去打开手电筒的。

Android6.0及以上打开方式

Google的开发人员觉得在5.0上打开手电筒的方式太过于复杂了,于是就将更多没必要的细节封装起来不需要让调用知道更多的。因此在Android6.0上就做了一些一定的规模的精简性,实现上没有这么负责了。

  1. 首先还是需要获取cameraId,这一步更上面的是一样的
  2. 其次注册CameraManager.TorchCallback

    
    private final CameraManager.TorchCallback mTorchCallback =new CameraManager.TorchCallback() {//该方法表示手电筒是否支持@Overridepublic void onTorchModeUnavailable(String cameraId) {if (TextUtils.equals(cameraId, mCameraId)) {setCameraAvailable(false);}}//该方法表示手电筒状态发生了变化,比如说手电筒从开变成了关@Overridepublic void onTorchModeChanged(String cameraId, boolean enabled) {if (TextUtils.equals(cameraId, mCameraId)) {setCameraAvailable(true);setTorchMode(enabled);}}private void setCameraAvailable(boolean available) {boolean changed;synchronized (HighVersionController.this) {changed = mCameraAvailable != available;mCameraAvailable = available;}if (changed) {Log.d(TAG, "dispatchAvailabilityChanged(" + available + ")");}}private void setTorchMode(boolean enabled) {boolean changed;synchronized (HighVersionController.this) {changed = mFlashlightEnabled != enabled;mFlashlightEnabled = enabled;}if (changed) {Log.d(TAG, "dispatchModeChanged(" + enabled + ")");dispatchModeChanged(enabled);}}
    };public synchronized void ensureHandler() {if (mHandler == null) {HandlerThread thread = new HandlerThread(TAG, 8);thread.start();mHandler = new Handler(thread.getLooper());}
    }if (mCameraId != null) {mCameraManager.registerTorchCallback(mTorchCallback, mHandler);
    }

    从上面的代码中我们可以很快的知道Android6.0上的实现方式跟5.0上进行了大大的精简了,我们不需要关心内部的一些东西,我们只要关心的是手电筒的状态是否发生了变化以及手电筒是否能用之类的就可以了,至于一些其他的细节的话我们可以封装起来通过回调的方式暴漏出来就可以了。下面我们就将几个平台不同差异化的实现然后抽象出来,然后统一根据不同的版本然后通过不同的方式来实现出来。

类图的设计

首先我们抽象出一个FlashLightFactory的接口,该接口主要定义错误码,开启手电筒方法,添加手电筒状态监听,删除状态监听,以及获取手电筒开关状态等等一系列的东西。下面分别介绍不同的类的作用

  • FlashLightFactory:该接口主要是定义了一些对手电筒操作的基本方法,比如说开启手电筒,获取手电筒的开关状态,以及判断手机是否支持手电筒功能等等。
  • BaseController:该类继承FlashLightFactory接口,然后实现一些简单的常用的功能,比如说错误的分发,手电筒状态的分发之类的
  • CompatController:该类继承BaseController,主要是用在Android5.0以上的一些公共的方法上的,比如说获取CameraId,因为5.0之后废除了Camera而直接使用CameraManager了,如果我们将CameraManager之类的东西写在BaseController方法内的话在一些低版本的手机则会出现奔溃的。
  • LowVersionController:该类直接由于是用了低版本的方法去开启手电筒的,而且还是使用了Camera去实现的,所以我们需要在这个里面单独进行处理开启手电筒的功能的,主要是处理android5.0以下的手机上的。
  • HighVersionController:该类继承CompatController,用于处理Android6.0上开启手电筒的方式。
  • MiddleVersionController:该类主要是用于实现Android5.0-6.0之间的打开手电筒的,通过使用CameraManager以及CameraDevice等等去实现的。
  • FlashLightListener:该接口的主要是对外提供使用的,主要是用户手电筒打开错误的回调,以及手电筒开关状态的回调,供调用手电筒功能的人使用

当我们有了一个清晰的逻辑之后,我们就根据这个逻辑分别对应不同的版本去实现的。其实上面的实现也是根据Android各个版本对手电筒不同的实现进行不同的处理的,这样子我们是为了更好的适配不同的手机类型,使我们的手电筒更加兼容不同的品牌。下面我们就可以弄一个专门搞一个控制类用于控制各个版本的处理的方式。

核心代码
    为了让大家更好的学习代码以及能更快的掌握知识,同时也尽可能的去踩坑,下面我会将代码放到github上,同时也希望大家更加踊跃的反馈问题,或者是大家有更好的实现方法也可以将你们的代码提交上去,有利于大家的一起学习和进步。本次的打开手电筒其实说难不难,但是要做起来而且要做好的话确实也不简单,特别是大家都对那些系统的API都不是很熟悉的情况下要写出一个比较好的兼容各个平台的手电筒功能还真的是不简单,本人本着不重复造轮子,合理的利用Android开源的代码所以就将各个平台的手电筒功能给搬过来,因为这样子的代码才是最稳定的,坑也是最少的。我们平时在学些的时候更多的还是要多利用别人的代码,别老是自己去重复的造轮子,你造的轮子肯定比不过别人经过千万测试的代码要好的。

github地址https://github.com/ACMNexus/flashlight

Android 开启手电筒功能(完美适配4.x, 5.x, 6.x )相关推荐

  1. Android开启手电筒功能(完美适配Android4x,5x,6x)

    原文http://blog.csdn.net/mynameishuangshuai Android4x.5x.6x的手电筒开启方法,写成公共方法,分享给大家. 添加摄像机和闪光灯权限 <uses ...

  2. 8.【小萌伴Android】手电筒功能及其实现

    前面三篇介绍了[小萌伴]百宝箱中的三款原生小游戏,这里说说百宝箱中另一功能模块--实用小应用... [小萌伴]手电筒.gif 小应用主要包含了四个功能:今日历史.新闻.手电筒和找手机:今日历史和新闻模 ...

  3. android搜狗互联网链接,完美适配Android5.0搜狗手机浏览器上线

    被网友誉为"移动阅读神器"的搜狗手机浏览器Android版近日获得更新.全新V3.7版本不仅完美适配Android5.0系统,还对小说阅读功能进行再次改进,令网友在阅读.更新小说时 ...

  4. android开启gps功能,android 打开GPS的几种方式

    1.在讨论打开gps的之前先看下如何检测gps的开关情况: 方式一: boolean gpsEnabled = locationManager.isProviderEnabled(LocationMa ...

  5. Android实现计步器功能,适配Android10,隔天步数清零,查看历史运动纪录_附源码

    最近需要用到计步功能,这可难坏我了,IOS端倒好,有自带的计步功能,让我惊讶的是连已爬楼层都给做好了,只需要调接口便可获得数据,我有一句MMP,我很想讲. 但是抱怨归抱怨,功能还是得尝试的去实现,微信 ...

  6. android开启照相功能,Android--启动拍照功能并返回结果

    简单的调用了一下系统的拍照功能,下面代码: //拍照的方法 private void openTakePhoto(){ /** *在启动拍照之前最好先判断一下sdcard是否可用 */ String ...

  7. android开启加速功能,Android硬件加速开发简介

    从 Android 3.0(API Level 11)开始,Android 2D 渲染管线被设计为能更好地支持硬件加速功能. 硬件加速功能将所有在 View 组件的 Canvas 上执行的绘制操作都交 ...

  8. android 开启手电筒,android 9.0 获取手电筒状态以及打开/关闭手电筒

    查了很多资料,其他博客大多都是介绍以前的的方案,基于9.0系统的基本没有找到,要么就不全,特别是获取手电筒状态的资料,下面就大致介绍一下: 打开和关闭的方法是:toggleLight 获取手电筒状态的 ...

  9. android开启照相功能,Android打开系统相机并拍照的2种显示方法

    本文实例为大家分享了Android打开系统相机并拍照的具体实现代码,供大家参考,具体内容如下 目标效果: 第二张为点击第一个按钮拍照后显示的,比较模糊,第三章为点击第二个按钮拍照后显示的,比较清楚. ...

最新文章

  1. HarmonyOS UI开发 DirectionalLayout(定向布局) 的使用
  2. 记-python中socket服务器设置中的setsockopt
  3. EasyUI实现两个列表联动
  4. 使用jquery获取url及url参数的方法及定义JQuery扩展方法
  5. 【转】VS2013动态库文件的创建及其使用详解
  6. react native 问题点
  7. 哔哩哔哩视频下载神器
  8. Python图形界面实现咖啡店点单系统
  9. Unity内置Shader解读2——Bumped Specular
  10. 计算机领域影响因子3.5什么水平,计算机学科SCI(包括SCI-E)影响因子排名
  11. MFC 控件类型和状态
  12. 中美自动驾驶最新融资情况:千万级与十亿级美元的距离
  13. Android Studio下Terminal窗口Window下cmd黑窗口中使用adb命令调试精炼详解
  14. iOS 逆向编程(三)实操越狱详细流程
  15. qq邮箱服务器在哪里设置密码,手把手教程 邮箱这样设置,就对了
  16. 金蝶计算机快捷键,(完整版)金蝶软件快捷键
  17. 基于SSH的百货中心供应链管理系统(附论文)
  18. Thinkphp+vue女生穿衣服装搭配系统毕业设计
  19. 简单理解 柯理化函数
  20. [:断开的管道]异常信息原因探究

热门文章

  1. 2022-2027年中国移动医疗器械行业发展监测及投资战略研究报告
  2. 网站关键词排名怎么去优化到百度首页(思维与技巧结合)
  3. Flutter文本输入框TextField属性(InputDecoration、textInputAction、inputFormatters等等)详解
  4. BZOJ3098. Hash Killer II(生日攻击)
  5. 获取微信版本号 user agent
  6. sqlserver对cpu主频要求_记一次SQLServer服务器CPU飙升100%的处理
  7. Linux 基础命令 -- su
  8. java交换两个变量值
  9. PLSQL配置连接oracle
  10. 在微信小游戏中使用three.js显示3D图形