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手机6.0后权限问题*
- android手机系统 6.0后的权限申请问题
- Android6.0的拍照权限处理在Android6.0的手机时data为null的问题。
- Android 手机6.0 定位权限问题
- android手机震动、提示音、播放系统音乐和研究7.0,6.0弹出Notification出现crash问题
- Android 6.0 ROOT 后部分程序无法检测 ROOT 权限问题
- iscroll 部分android手机(如三星的)不能点击兼容问题
- 基于Android热点的局域网UDP广播,部分手机收不到UDP报文的问题
- Android自定义拍照解决部分手机拍完之后图片不清楚的问题
- 部分国行Android手机缺少谷歌GMS服务包导致HTML5 Geolocation无法定位的问题
- 在android 6.0以上无法获取READ_PHONE_STATE权限的SecurityException的问题
- Mac OS X 下部分Android手机无法连接adb问题之解决方案
- 在android 6.0以上无法获取READ_PHONE_STATE权限的SecurityException的问题
- Android 6.0权限问题
- android 6.0权限问题
- Mac OS X 下部分Android手机无法连接adb问题之解决方案
- 解决了一个cocos2dx 在部分 android手机上模板测试错误的问题
- Mac OS X 下部分Android手机无法连接adb问题之解决方案
- 【转】Mac OS X下Android系统M2、华为部分手机无法连接问题之解决方案