(一)、Android录音权限被禁解决方案
大家在做Android录音的时候会碰到权限被禁止的情况出现,当用户禁止了录音权限的时候我们为了界面友好会提示用户,但是安卓机型很多,有些机型在录音start的时候如果被禁会报异常,当然这种情况很好解决,但是有些机型会正常执行,在此我们分析一下我所遇到的情况。
被禁情况分析
1,第一种,就是start的时候会报异常,这种我们把它包在try catch中即可捕获到异常。在此不多累述。
2,第二种,就是不报异常,正常执行,这种情况我们没办法去判断系统是否禁止了我们的app的录音权限。
所以我在此分析的是部分机型在被禁止后不报异常,我们可以去检测音频振幅大小,部分机型的音频振幅值在用MediaRecorder时是0,在用AudioRecord时值小于0,所以这种情况我们可以通过其振幅值判断:
(1)AudioRecord判断方法:
int readSize = 0;
/*--实时录音写数据--*/
readSize = audioRecord.read(buffer,0,minBufferSize);
if (readSize < 0) { //录音出现异常 }
该方法能检测到大部分机型录音被禁止,或其他异常状况。
检测录音程序是否被禁用了,可以使用下面的代码判断
int read = audioRecord.read(data, 0, recBufSize);
if(AudioRecord.ERROR_INVALID_OPERATION != read){
// 做正常的录音处理
} else {
//录音可能被禁用了,做出适当的提示
}
(2)MediaRecorder判断方法:
MediaRecorder就不怎么好检测了,因为MediaRecorder在用方法getMaxAmplitude()时取得振幅值是0-32767,也就是即使不禁止录音权限,振幅值依然会有0值出现,所以不能简单地判断振幅值是否为0,我在此的检测方法是根据前1s的录音,取10次振幅值进行判断:
先声明三个变量:
private int vocAuthority[] = new int[10];
private int vocNum = 0;
private boolean check = true;
然后写个方法:
int vocLevel = mRecorder.getMaxAmplitude();
if (check) {
if (vocNum >= 10) {
Set set = new HashSet(); for (int i = 0; i < vocNum; i++) { set.add(vocAuthority[i]); } if (set.size() == 1) { if (handler != null) handler.sendEmptyMessage(MSG_ERROR_AUDIO_RECORD); }else{ check = false; } } else { vocAuthority[vocNum] = vocLevel; vocNum++; } }
该方法每100ms执行一次,1s十次后判断vocAuthority中的值是否全部一样,也就是说是否全部为零,如果全部一样那么录音肯定有问题。(我试了如果在非常安静的情况下前十次的取值也不是都为零,大家可以试试)
3,第三种,还有部分机型不仅不报异常,而且在录音时会制造音频振幅的假数据,也就是虽然录音被禁止,系统依然会把音频振幅反馈给你,像华为p7就是这样,这种情况肯定不能再去检测音频振幅了,只能另辟蹊径,通过我的观察发现在用MediaRecorder录音时,如果被禁止那么本地不会出现录音文件,所以我们可以以此为突破口,检测本地是否有生成的录音文件来判断系统是否禁止了我们的录音权限。AudioRecord我没试,大家可以试试。
(二)、Android摄像头权限被禁解决方案
待总结
直接上代码吧!!!
package com.wytiger.mytest.utils;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import android.annotation.TargetApi;
import android.app.AppOpsManager;
import android.content.Context;
import android.hardware.Camera;
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;
import android.os.Binder;
import android.os.Build;
import android.util.Log;
@SuppressWarnings("deprecation")
public class PermissionUtil {
public static final String TAG = "TAG_PERMISSION";
public static final int STATE_NO_PERMISSION = -1;
public static final int STATE_RECORDING = 0;
public static final int STATE_SUCCESS = 1;
/**
* 判断是否有录音权限
*
* @return
*/
public static boolean hasRecordPermission() {
boolean hasPermission = false;
if (getRecordState() == STATE_SUCCESS) {
hasPermission = true;
} else {
hasPermission = false;
}
return hasPermission;
}
/**
* 判断是否有录音权限
*
* @return
*/
// public static boolean hasRecordPermission2() {
// boolean hasPermission = false;
//
//
//
// return hasPermission;
// }
/**
* 获取录音状态
*
* @return
*/
public static int getRecordState() {
int minBuffer = AudioRecord.getMinBufferSize(44100, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT);
short[] point = new short[minBuffer];
int readSize = 0;
AudioRecord audioRecord = null;
try {
audioRecord = new AudioRecord(MediaRecorder.AudioSource.DEFAULT, 44100, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT,
(minBuffer * 100));
// 开始录音
audioRecord.startRecording();// 检测是否可以进入初始化状态
} catch (Exception e) {
Log.e(TAG, "catch, 捕捉到异常, 无录音权限, e = " + e.getMessage());
if (audioRecord != null) {
audioRecord.release();
audioRecord = null;
Log.i(TAG, "catch, 返回对象非空,释放资源");
} else {
Log.i(TAG, "catch, 返回对象非空");
}
return STATE_NO_PERMISSION;
}
// 检测是否在录音中
if (audioRecord.getRecordingState() != AudioRecord.RECORDSTATE_RECORDING) {
// 6.0以下机型都会返回此状态,故使用时需要判断bulid版本
if (audioRecord != null) {
audioRecord.stop();
audioRecord.release();
audioRecord = null;
Log.e(TAG, "无法启动录音, 无法录音");
}
return STATE_RECORDING;
} else {// 正在录音
readSize = audioRecord.read(point, 0, point.length);
// 检测是否可以获取录音结果
if (readSize <= 0) {
if (audioRecord != null) {
audioRecord.stop();
audioRecord.release();
audioRecord = null;
}
Log.e(TAG, "没有获取到录音数据,无录音权限");
return STATE_NO_PERMISSION;
} else {
if (audioRecord != null) {
audioRecord.stop();
audioRecord.release();
audioRecord = null;
}
Log.i(TAG, "获取到录音数据, 有录音权限");
return STATE_SUCCESS;
}
}
}
/**
* 是否有摄像头权限
*
* @return
*/
@SuppressWarnings("deprecation")
public static boolean hasCameraPermission() {
// 无权限可能出现的情况(部分):
// 1, 直接抛异常
// 2, 不抛异常,返回对象为空
// 3, 获取到的数据为空
boolean canUse = true;
Camera mCamera = null;
try {
mCamera = Camera.open();
mCamera.setParameters(mCamera.getParameters());
} catch (Exception e) {
Log.e(TAG, "catch, 捕捉到异常, 无摄像头权限");
// 1, 直接抛异常
canUse = false;
if (mCamera != null) {// 返回对象非空(魅族)
mCamera.release();
mCamera = null;
Log.i(TAG, "catch, 对象非空,释放资源");
} else if (mCamera == null) {// 2, 返回对象为空
Log.e(TAG, "catch, 对象为空");
}
} finally {
if (mCamera != null) {
canUse = true;
mCamera.release();
mCamera = null;
Log.i(TAG, "finally, 对象非空,释放资源");
} else if (mCamera == null) {// 2, 返回对象为空
canUse = false;
Log.e(TAG, "finally, 对象为空");
}
}
Log.i(TAG, "返回");
return canUse;
}
// op flag
public static final int opCameraFlag = 26;
public static final int opRecordAudioFlag = 27;
@TargetApi(Build.VERSION_CODES.KITKAT)
/**
* 是否有指定权限,在api19以上有效
* @param mContext:上下文
* @param opFlag: 指定操作码,see AppOpsManager define
* @return
*/
public boolean hasPermission(Context mContext, int opFlag) {
boolean hasPermission = false;
if (Build.VERSION.SDK_INT >= 19) {
if (checkOp(mContext, opFlag) == 0) {
hasPermission = true;
} else if (checkOp(mContext, opFlag) == 1) {
hasPermission = false;
} else {
hasPermission = false;
Log.e(TAG, "内部出错");
}
return hasPermission;
} else {
Log.e(TAG, "API below 19 cannot invoke!");
}
return true;
}
@TargetApi(Build.VERSION_CODES.KITKAT)
@SuppressWarnings({ "unchecked", "rawtypes" })
/**
* Api19以上有效
* 返回 0 代表有权限,1代表没有权限,-1函数出错啦
*
* @param context
* @param op
* @return
*/
private static int checkOp(Context context, int op) {
if (Build.VERSION.SDK_INT >= 19) {
try {
AppOpsManager manager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
Class clazz = AppOpsManager.class;
Class[] cArg = new Class[3];
cArg[0] = int.class;
cArg[1] = int.class;
cArg[2] = String.class;
Method checkOpMethod = clazz.getDeclaredMethod("checkOp", cArg);
return (Integer) checkOpMethod.invoke(manager, op, Binder.getCallingUid(), context.getPackageName());
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
return -1;
}
}