21.XXPermissions请求相机权限实现手电筒
今天实现一个小功能,调用相机权限实现手电筒,顺带学习一下CameraManager
系统服务和两个好用的权限请求框架,主要推荐使用XXPermissions
1.权限设置
在manifest.xml
中引入相机权限
java
<!--引入相机权限-->
<uses-permission android:name="android.permission.CAMERA"/>
2.xml布局文件
xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<ImageView
android:id="@+id/flashBtn"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="@drawable/off"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</RelativeLayout>
3.前备知识
CameraManager
是系统服务之一,专门用于 检测 和 打开相机,以及 获取相机设备特性
String[] getCameraIdList()
获取当前连接的相机设备列表,这个 id 通常都是从 0 开始并依次递增的。
对于一般的手机而言:
后置摄像头一般为 CameraCharacteristics.LENS_FACING_FRONT
,常量值为 “0”; 前置摄像头一般为 CameraCharacteristics.LENS_FACING_BACK
,常量值为 “1”
2. `void setTorchMode(String cameraId, boolean enabled)`
打开和关闭指定相机设备的闪光灯功能。
4.XXPermissions框架(推荐)
GitHub - getActivity/XXPermissions: Android 权限请求框架,已适配 Android 13
4.1框架亮点
- 首款适配 Android 13 的权限请求框架
- 首款也是唯一一款适配所有 Android 版本的权限请求框架
- 简洁易用:采用链式调用的方式,使用只需一句代码
- 体积感人:功能在同类框架中是最全的,但是框架体积是垫底的
- 适配极端情况:无论在多么极端恶劣的环境下申请权限,框架依然坚挺
- 向下兼容属性:新权限在旧系统可以正常申请,框架会做自动适配,无需调用者适配
- 自动检测错误:如果出现错误框架会主动抛出异常给调用者(仅在 Debug 下判断,把 Bug 扼杀在摇篮中)
4.2集成步骤
- 如果你的项目 Gradle 配置是在
7.0 以下
,需要在build.gradle
文件中加入
java
allprojects {
repositories {
// JitPack 远程仓库:https://jitpack.io
maven { url 'https://jitpack.io' }
}
}
- 如果你的 Gradle 配置是
7.0 及以上
,则需要在settings.gradle
文件中加入
java
dependencyResolutionManagement {
repositories {
// JitPack 远程仓库:https://jitpack.io
maven { url 'https://jitpack.io' }
}
}
- 配置完远程仓库后,在项目 app 模块下的
build.gradle
文件中加入远程依赖
java
android {
// 支持 JDK 1.8
compileOptions {
targetCompatibility JavaVersion.VERSION_1_8
sourceCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
// 权限请求框架:https://github.com/getActivity/XXPermissions
implementation 'com.github.getActivity:XXPermissions:16.0'
}
- 如果项目是基于 AndroidX 包,请在项目
gradle.properties
文件中加入
java
# 表示将第三方库迁移到 AndroidX
android.enableJetifier = true
- 如果项目是基于 Support 包则不需要加入此配置
4.3请求权限
java
XXPermissions.with(this)
// 申请单个权限
.permission(Permission.RECORD_AUDIO)
// 申请多个权限
.permission(Permission.Group.CALENDAR)
// 设置权限请求拦截器(局部设置)
//.interceptor(new PermissionInterceptor())
// 设置不触发错误检测机制(局部设置)
//.unchecked()
.request(new OnPermissionCallback() {
@Override
public void onGranted(List<String> permissions, boolean all) {
if (!all) {
toast("获取部分权限成功,但部分权限未正常授予");
return;
}
toast("获取录音和日历权限成功");
}
@Override
public void onDenied(List<String> permissions, boolean never) {
if (never) {
toast("被永久拒绝授权,请手动授予录音和日历权限");
// 如果是被永久拒绝就跳转到应用权限系统设置页面
XXPermissions.startPermissionActivity(context, permissions);
} else {
toast("获取录音和日历权限失败");
}
}
});
4.4框架其他API
java
// 判断一个或多个权限是否全部授予了
XXPermissions.isGranted(Context context, String... permissions);
// 获取没有授予的权限
XXPermissions.getDenied(Context context, String... permissions);
// 判断某个权限是否为特殊权限
XXPermissions.isSpecial(String permission);
// 判断一个或多个权限是否被永久拒绝了
XXPermissions.isPermanentDenied(Activity activity, String... permissions);
// 跳转到应用权限设置页
XXPermissions.startPermissionActivity(Context context, String... permissions);
XXPermissions.startPermissionActivity(Activity activity, String... permissions);
XXPermissions.startPermissionActivity(Activity activity, String... permission, OnPermissionPageCallback callback);
XXPermissions.startPermissionActivity(Fragment fragment, String... permissions);
XXPermissions.startPermissionActivity(Fragment fragment, String... permissions, OnPermissionPageCallback callback);
// 设置不触发错误检测机制(全局设置)
XXPermissions.setCheckMode(false);
// 设置权限申请拦截器(全局设置)
XXPermissions.setInterceptor(new IPermissionInterceptor() {});
5.Dexter框架
这个框架不再处于积极开发中,最新的一次的更新是在14个月前。
Dexter
是一个 Android 库,它简化了在运行时请求权限的过程。 Android Marshmallow
包含一项新功能,允许用户在运行应用程序时授予或拒绝权限,而不是在安装时授予所有权限。 这种方法让用户可以更好地控制应用程序,但需要开发人员添加大量代码来支持它。 Dexter 将您的权限代码从您的活动中释放出来,让您可以在任何您想要的地方编写该逻辑。
5.1引入依赖
将库包含在您的build.gradle
java
dependencies{
implementation 'com.karumi:dexter:6.2.3'
}
5.2单个权限
对于每个权限,注册一个PermissionListener
实现以接收请求的状态:
java
Dexter.withContext(this)
.withPermission(Manifest.permission.CAMERA)
.withListener(new PermissionListener() {
@Override public void onPermissionGranted(PermissionGrantedResponse response) {/* ... */}
@Override public void onPermissionDenied(PermissionDeniedResponse response) {/* ... */}
@Override public void onPermissionRationaleShouldBeShown(PermissionRequest permission, PermissionToken token) {/* ... */}
}).check();
5.3多个权限
java
Dexter.withContext(this)
.withPermissions(
Manifest.permission.CAMERA,
Manifest.permission.READ_CONTACTS,
Manifest.permission.RECORD_AUDIO
).withListener(new MultiplePermissionsListener() {
@Override public void onPermissionsChecked(MultiplePermissionsReport report) {/* ... */}
@Override public void onPermissionRationaleShouldBeShown(List<PermissionRequest> permissions, PermissionToken token) {/* ... */}
}).check();
6.同类权限请求框架之间的对比
适配细节 | XXPermissions | AndPermission | PermissionX | AndroidUtilCode | PermissionsDispatcher | RxPermissions | EasyPermissions |
---|---|---|---|---|---|---|---|
对应版本 | 16.0 | 2.0.3 | 1.6.4 | 1.31.0 | 4.9.2 | 0.12 | 3.0.0 |
issues 数 | |||||||
框架体积 | 51 KB | 127 KB | 90 KB | 500 KB | 99 KB | 28 KB | 48 KB |
框架维护状态 | 维护中 | 停止维护 | 维护中 | 停止维护 | 维护中 | 停止维护 | 停止维护 |
闹钟提醒权限 | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
所有文件管理权限 | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ |
安装包权限 | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ |
悬浮窗权限 | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ |
系统设置权限 | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ |
通知栏权限 | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
通知栏监听权限 | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
勿扰权限 | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
忽略电池优化权限 | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
查看应用使用情况权限 | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
VPN 权限 | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
Android 13 危险权限 | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
Android 12 危险权限 | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ |
Android 11 危险权限 | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ |
Android 10 危险权限 | ✅ | ✅ | ✅ | ❌ | ✅ | ❌ | ❌ |
Android 9.0 危险权限 | ✅ | ❌ | ✅ | ❌ | ✅ | ❌ | ❌ |
Android 8.0 危险权限 | ✅ | ✅ | ✅ | ❌ | ✅ | ❌ | ❌ |
新权限自动兼容旧设备 | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
屏幕方向旋转场景适配 | ✅ | ✅ | ✅ | ❌ | ✅ | ❌ | ❌ |
后台申请权限场景适配 | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
Android 12 内存泄漏问题修复 | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
错误检测机制 | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
7.Dexter请求权限实现手电筒
使用这个框架的前提只需要在build.gradle
中引入依赖即可
java
public class MainActivity extends AppCompatActivity {
ImageView flashBtn;
boolean state;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
flashBtn=findViewById(R.id.flashBtn);
Dexter.withContext(this)//指定页面
.withPermission(Manifest.permission.CAMERA)//指定权限
.withListener(new PermissionListener() {//创建监听
@Override
public void onPermissionGranted(PermissionGrantedResponse permissionGrantedResponse) {
//运行手电筒
runFlashlight();
}
//用户提示
@Override
public void onPermissionDenied(PermissionDeniedResponse permissionDeniedResponse) {
Toast.makeText(MainActivity.this,"Camera permission request",Toast.LENGTH_SHORT).show();
}
@Override
public void onPermissionRationaleShouldBeShown(PermissionRequest permissionRequest, PermissionToken permissionToken) {
}
}).check();
}
private void runFlashlight() {
flashBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if(!state){
//设置相机功能管理
CameraManager cameraManager=(CameraManager) getSystemService(Context.CAMERA_SERVICE);
try {
//getCameraIdList()按标识符返回当前连接的相机设备列表,包括可能被其他相机 API 客户端使用的相机。
String cameraId=cameraManager.getCameraIdList()[0];
//打开和关闭指定相机设备的闪光灯功能。
cameraManager.setTorchMode(cameraId,true);
//设置状态为true,打开
state=true;
//更改图片资源
flashBtn.setImageResource(R.drawable.on);
}catch (CameraAccessException e){
}
}else{
//设置相机功能管理
CameraManager cameraManager=(CameraManager) getSystemService(Context.CAMERA_SERVICE);
try {
//getCameraIdList()按标识符返回当前连接的相机设备列表,包括可能被其他相机 API 客户端使用的相机。
String cameraId=cameraManager.getCameraIdList()[0];
//打开和关闭指定相机设备的闪光灯功能。
cameraManager.setTorchMode(cameraId,false);
state=false;
flashBtn.setImageResource(R.drawable.off);
}catch (CameraAccessException e){
}
}
}
});
}
}
8.XXPermissions请求权限实现手电筒
- 我的项目的 Gradle 配置是
7.0 及以上
,所以我需要在settings.gradle
文件中加入maven { url 'https://jitpack.io' }
, - 并且我的项目是基于 AndroidX 包,需要在项目
gradle.properties
文件中加入android.enableJetifier = true
- 最后在项目 app 模块下的
build.gradle
文件中加入远程依赖
java
public class MainActivity extends AppCompatActivity {
ImageView flashBtn;
boolean state;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
flashBtn=findViewById(R.id.flashBtn);
XXPermissions.with(this)
// 申请单个权限(相机)
.permission(Permission.CAMERA)
// 申请多个权限(日历),要申请多个权限只需要把要申请的权限列出来即可
//.permission(Permission.Group.CALENDAR)
.request(new OnPermissionCallback() {
@Override
public void onGranted(List<String> permissions, boolean all) {
if (!all) {
Toast.makeText(MainActivity.this,"获取部分权限成功,但部分权限未正常授予",Toast.LENGTH_SHORT).show();
return;
}
runFlashlight();
Toast.makeText(MainActivity.this,"获取相机权限成功",Toast.LENGTH_SHORT).show();
}
@Override
public void onDenied(List<String> permissions, boolean never) {
if (never) {
Toast.makeText(MainActivity.this,"被永久拒绝授权,请手动授予相机权限",Toast.LENGTH_SHORT).show();
// 如果是被永久拒绝就跳转到应用权限系统设置页面
XXPermissions.startPermissionActivity(MainActivity.this, permissions);
} else {
Toast.makeText(MainActivity.this,"获取录音和日历权限失败",Toast.LENGTH_SHORT).show();
}
}
});
}
private void runFlashlight() {
flashBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if(!state){
//设置相机功能管理
CameraManager cameraManager=(CameraManager) getSystemService(Context.CAMERA_SERVICE);
try {
//getCameraIdList()按标识符返回当前连接的相机设备列表,包括可能被其他相机 API 客户端使用的相机。
String cameraId=cameraManager.getCameraIdList()[0];
//打开和关闭指定相机设备的闪光灯功能。
cameraManager.setTorchMode(cameraId,true);
//设置状态为true,打开
state=true;
//更改图片资源
flashBtn.setImageResource(R.drawable.on);
}catch (CameraAccessException e){
}
}else{
//设置相机功能管理
CameraManager cameraManager=(CameraManager) getSystemService(Context.CAMERA_SERVICE);
try {
//getCameraIdList()按标识符返回当前连接的相机设备列表,包括可能被其他相机 API 客户端使用的相机。
String cameraId=cameraManager.getCameraIdList()[0];
//打开和关闭指定相机设备的闪光灯功能。
cameraManager.setTorchMode(cameraId,false);
state=false;
flashBtn.setImageResource(R.drawable.off);
}catch (CameraAccessException e){
}
}
}
});
}
}
9.效果展示
10.参考资料
Teacher.Hu(胡老师博客)Android 动态权限最全解析
[Teacher.Hu(胡老师博客)【Android】相对布局(RelativeLayout)最全解析