您的位置:首页 > 移动开发 > Android开发

Android 6.0和7.0权限问题及部分手机的坑

2017-09-15 17:50 501 查看

一、6.0动态授权(注意拒绝权限后的处理)

在6.0下新的权限机制分为两类,Normal Permissions,这类权限是不需要用户授权。一类是Dangerous Permissions,这类权限需要动态的去申请。

常见的危险权限有:

permission:android.permission.CAMERA
permission:android.permission.READ_EXTERNAL_STORAGE
permission:android.permission.CALL_PHONE


等等。

我们常见的第三方库EasyPermission,其使用方法如下:

1.添加依赖

compile 'pub.devrel:easypermissions:0.3.0'


2.定义变量,为要申请的权限

private String[] perms = {Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.CAMERA,Manifest.permission.RECORD_AUDIO};


3.在需要权限的地方,如下代码

try {
if (EasyPermissions.hasPermissions(CameraActivity.this, perms)) {//检查是否获取该权限
method();
//code.....获得权限后要做的事
} else {
//第二个参数是被拒绝后再次申请该权限的解释
//第三个参数是请求码
//第四个参数是要申请的权限
EasyPermissions.requestPermissions(CameraActivity.this,
getString(R.string.necessary_pemmision), 0, perms);
}
}


4.添加回调

@Override   public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults)

super.onRequestPermissionsResult(requestCode, permissions, grantResults);

//把申请权限的回调交由EasyPermissions处理

EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this);

}


5.实现EasyPermissions.PermissionCallbacks接口并添加EasyPermissions的回调

回调中处理成功和失败的逻辑。

//下面两个方法是实现EasyPermissions的EasyPermissions.PermissionCallbacks接口

//分别返回授权成功和失败的权限

@Override

public void onPermissionsGranted(int requestCode, List<String> perms) {

Log.i(TAG, "获取成功的权限" + perms);
method(); //code.....获得权限后要做的事

}

@Override

public void onPermissionsDenied(int requestCode, List<String> perms) {

Log.i(TAG, "获取失败的权限" + perms);
if (EasyPermissions.somePermissionPermanentlyDenied(this, perms)) {
new AppSettingsDialog.Builder(this).setTitle(R.string.permmision_apply).setRationale(
R.string.camera_voice_denied_dsc).setRequestCode(
IConstants.REQ_CODE.PERMISSION).setNegativeButton(getString(R.string.cancel),
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
ToastUtil.showShort(getString(R.string.camera_permmision_dsc));
finish();
}
}).build().show();
}
}


二、7.0 FileProvider的使用

为什么要用FileProvider,简单点说,避免FileUriExposedException异常。

如何使用?

1.manifest定义provider

<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="包名.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />

</provider>


2.指定路径,比如如下路径D:\Project\projectname\app\src\main\res\xml\file_paths.xml

这个xml里放的什么呢?

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path
name="files_root"
path="Android/data/包名/" />
<external-path
name="external_storage_root"
path="." />
<root-path
name="camera_photos"
path="" />
</paths>


root-path是后来补充上去的,因为部分手机可以外置SD卡设置为根目录。

paths>元素必须包含以下一个或多个子元素:

表示应用程序内部存储区域的file/子目录中的文件.这个子文件与Context.getFilesDir()返回的结果相同

表示应用程序内部存储区域的缓存子目录中的文件。该子目录的根路径与getCacheDir()返回值相同

表示外部存储区域根目录中的文件。该子目录的根路径与Environment.getExternalStorageDirectory()返回的值相同

表示应用程序外部存储区域根目录中的文件。该子目录的根路径与Context#getExternalFilesDir(String) 和Context.getExternalFilesDir(null)返回的值相同

表示应用程序外部缓存区域根目录中的文件。该子目录的根路径与Context.getExternalCacheDir()返回的值相同

3.我写的一个获取Uri的工具类,就这么几行

/**
* FileProvider工具类
*/
public static Uri getFileUri(Context context, File dir) {
Uri uri;
if (Build.VERSION.SDK_INT >= 24) {
uri = FileProvider.getUriForFile(context, "包名.fileprovider", dir);
} else {
uri = Uri.fromFile(dir);
}
return uri;
}


4.实际使用,直接贴一个照相部分的代码了

/**
* 拍照后做裁剪处理,矩形
*/
public void cameraSelectResult(int width, int height) {
//先更新系统文件(让拍照的图片显示在相册中)
File file = new File(path_camera);
if (file.exists()) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
Uri contentUri = CommonUtils.getFileUri(getContext(), file);
//                Uri contentUri = Uri.fromFile(file);
mediaScanIntent.setData(contentUri);
if (mContext != null) {
mContext.sendBroadcast(mediaScanIntent);
} else if (mFragment != null) {
mFragment.getContext().sendBroadcast(mediaScanIntent);
}

} else {
if (mContext != null) {
mContext.sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED,
Uri.parse("file://" + Environment.getExternalStorageDirectory() + path_camera)));
} else if (mFragment != null) {
mFragment.getContext().sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED,
Uri.parse("file://" + Environment.getExternalStorageDirectory() + path_camera)));
}
}
}
doTailor(path_camera, width, height, false);
}


三、接下来开始填坑了

1.部分手机比如魅族在android 5.0对语音权限有类似动态授权的东西。既然是5.0,肯定不会走动态授权的,那就是永远有权限。厂家做的类似授权的东西你如果拒绝了,就会崩。

网上找了一个工具类,借鉴了。如果作者不允许,请告知我删掉。

public class CheckAudioPermission {
public static final int STATE_RECORDING = -1;
public static final int STATE_NO_PERMISSION = -2;
public static final int STATE_SUCCESS = 1;

/**
* 用于检测是否具有录音权限
*/
public static int getRecordState() {
int minBuffer = AudioRecord.getMinBufferSize(44100, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT);
AudioRecord audioRecord = new AudioRecord(MediaRecorder.AudioSource.DEFAULT, 44100, AudioFormat.CHANNEL_IN_MONO,
AudioFormat.ENCODING_PCM_16BIT, (minBuffer * 100));
short[] point = new short[minBuffer];
int readSize = 0;
try {
audioRecord.startRecording();//检测是否可以进入初始化状态
} catch (Exception e) {
if (audioRecord != null) {
audioRecord.release();
audioRecord = null;
Log.e("CheckAudioPermission", "无法进入录音初始状态");
}
return STATE_NO_PERMISSION;
}
if (audioRecord.getRecordingState() != AudioRecord.RECORDSTATE_RECORDING) {
//6.0以下机型都会返回此状态,故使用时需要判断bulid版本
//检测是否在录音中
if (audioRecord != null) {
audioRecord.stop();
audioRecord.release();
audioRecord = null;
Log.e("CheckAudioPermission", "录音机被占用");
}
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("CheckAudioPermission", "录音的结果为空");
return STATE_NO_PERMISSION;

} else {
if (audioRecord != null) {
audioRecord.stop();
audioRecord.release();
audioRecord = null;

}

return STATE_SUCCESS;
}
}
}
}


这个的使用稍微看一下

@Override
public void onVoiceBtnClicked() {
boolean hasPermissions = EasyPermissions.hasPermissions(getContext(), permissions);
if (hasPermissions) {
if (CheckAudioPermission.getRecordState() == CheckAudioPermission.STATE_SUCCESS) {
inputMenu.toggleVoice(1);
listView.setSelection(conversation.getAllMessages().size() - 1);
} else {
inputMenu.toggleVoice(2);
if (Build.VERSION.SDK_INT >= 21 && Build.VERSION.SDK_INT < 23) {
ToastUtil.showLong(getString(R.string.not_camera_denied1));
}
EasyPermissions.requestPermissions(CommonEaseChatFragment.this, getString(R.string.not_camera_denied), PERMI_MIC, permissions);
}
} else {
inputMenu.toggleVoice(2);
if (Build.VERSION.SDK_INT >= 21 && Build.VERSION.SDK_INT < 23) {
ToastUtil.showLong(getString(R.string.not_camera_denied1));
}
EasyPermissions.requestPermissions(CommonEaseChatFragment.this, getString(R.string.not_camera_denied), PERMI_MIC, permissions);
}

}


2.小米5s有一个版本的系统,权限申请有两个地方,你拒绝权限以后手动设置改动一个地方,另外一个地方不会自动跟着变化。比如你拒绝了语音权限,然后再次申请权限时会弹出dialog提示用户去设置权限,按了去设置后跳到系统的应用权限界面,设置为允许权限,返回后发现还是会弹出dialog,再点去设置,发现已经允许权限了。这个问题估计是系统的问题,还有一个统一的语音权限界面,需要过去设置,只能写一个帮助文档告诉用户另外一个设置权限的地方在哪里,让用户自己去设置了。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android