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

一、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,再点去设置,发现已经允许权限了。这个问题估计是系统的问题,还有一个统一的语音权限界面,需要过去设置,只能写一个帮助文档告诉用户另外一个设置权限的地方在哪里,让用户自己去设置了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值