Android6.0权限分配终极解决方案

来源:互联网 发布:sftp指定端口 编辑:程序博客网 时间:2024/06/02 05:10

1、问题描述

如下一段代码,在Android 5.0设备运行时,可以得到正确结果,但在Android 6.0以上设备运行时,提示没有读写权限。但AndroidManifest文件中已经设置过权限了。

错误提示信息是这样的:

Caused by: java.lang.SecurityException: Permission Denial: reading com.android.providers.media.MediaProvider uri content://media/external/audio/media from pid=3508, uid=10078 requires android.permission.READ_EXTERNAL_STORAGE, or grantUriPermission()

Activity代码

public class MainActivity extends AppCompatActivity {    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        Cursor cursor = getContentResolver().query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, null, null, null, null);        cursor.moveToFirst();        do {            String title = cursor.getString(cursor.getColumnIndex("title"));            Log.e("TITLE",title);        }while (cursor.moveToNext());    }}

AndroidManifest代码

<manifest xmlns:android="http://schemas.android.com/apk/res/android"    package="com.lenovo.permissiontest">    <application        android:allowBackup="true"        android:icon="@mipmap/ic_launcher"        android:label="@string/app_name"        android:roundIcon="@mipmap/ic_launcher_round"        android:supportsRtl="true"        android:theme="@style/AppTheme">        <activity android:name=".MainActivity">            <intent-filter>                <action android:name="android.intent.action.MAIN" />                <category android:name="android.intent.category.LAUNCHER" />            </intent-filter>        </activity>    </application>    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/></manifest>

2、问题诊断

众所周知,我们在做Android开发时,经常会用到一些系统的权限,比如读写外部存储、读写联系人信息、调用电话短信、访问网络等。在Android 6.0之前,对于这些权限调用的声明,只需要在AndroidManifest文件中声明即可,就像下面这样:

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

用户在安装应用时,会提示该程序总共用到哪些权限,但用户没有选择权,只有知情权,那么安全问题就随之而来。所以在android 6.0 棉花糖版本之后,系统不会在软件安装的时候就赋予该app所有其申请的权限,对于一些危险级别的权限,app需要在运行时一个一个询问用户授予权限。这就是所谓的“权限动态授权”。

那么哪些权限是危险权限呢?下表所示即为危险权限,这些权限都必须在应用内部经用户动态授权才能使用。
需要授权的权限列表

3、问题解决

动态权限分配,通过代码如何实现呢?请看下面代码。

Activity代码

public class MainActivity extends AppCompatActivity {    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        //检查当前权限(若没有该权限,值为-1;若有该权限,值为0)        int hasReadExternalStoragePermission = ContextCompat.checkSelfPermission(getApplication(),Manifest.permission.READ_EXTERNAL_STORAGE);        Log.e("PERMISION_CODE",hasReadExternalStoragePermission+"***");        if(hasReadExternalStoragePermission== PackageManager.PERMISSION_GRANTED){            obtainMediaInfo();        }else{            //若没有授权,会弹出一个对话框(这个对话框是系统的,开发者不能自己定制),用户选择是否授权应用使用系统权限            ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},1);        }    }    //用户选择是否同意授权后,会回调这个方法    @Override    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {        if(requestCode==1){            if(permissions[0].equals(Manifest.permission.READ_EXTERNAL_STORAGE)&&grantResults[0]==PackageManager.PERMISSION_GRANTED){                //用户同意授权,执行读取文件的代码                obtainMediaInfo();            }else{                //若用户不同意授权,直接暴力退出应用。                // 当然,这里也可以有比较温柔的操作。                finish();            }        }    }    //将之前获取音乐信息的代码单独封装到了一个方法里面    private void obtainMediaInfo() {        Cursor cursor = getContentResolver().query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, null, null, null, null);        cursor.moveToFirst();        do {            String title = cursor.getString(cursor.getColumnIndex("title"));            Log.e("TITLE",title);        }while (cursor.moveToNext());    }}

总结

这里主要用到两个方法

  • ActivityCompat.requestPermissions()
  • onRequestPermissionsResult()

ActivityCompat.requestPermissions()方法有3个参数:
第1个参数,当前上下文环境;
第2个参数,需要授权的字符串数组;
第3个参数,请求码requestCode,在回调方法中会用到。

onRequestPermissionsResult()是个回调方法,也就是说在用户点击“允许”或“拒绝”按钮后,才会调用。这个方法也有3个参数:
第1个参数,请求码,对应上述方法的第3个参数;
第2个参数,请求权限数组;
第3个参数,请求权限的结果数组。

详细使用方法请看上方代码,注释较为详细。

至此,我们已经完成了单个权限授权的代码。但是,距离完美还差那么一点点,比如:

  • 如果我们想要一次性进行多次授权呢?
  • 如果用户选择了不再弹出之后呢?

请继续往下看

4、更优雅的解决方案

Android6.0提出了如此周到、复杂的权限处理方式,对用户而言是好事,对程序员而言是极大的繁琐。
幸运的是我们有github,早已有人开发了第三方插件。

印度人写的第三方插件(点击查看)

https://github.com/pankaj89/PermissionHelper

build.gradle中编译

compile ‘com.master.android:permissionhelper:1.3’

github链接里已经写了详细的使用步骤,我也写了个例子,亲测非常好用。代码附上。

public class HelloActivity extends AppCompatActivity {    private String TAG = "HelloActivity";    private PermissionHelper permissionHelper;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_hello);        permissionHelper = new PermissionHelper(this, new String[]{Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE}, 100);        permissionHelper.request(new PermissionHelper.PermissionCallback() {            @Override            public void onPermissionGranted() {                //全都授权                Log.e(TAG,"onPermissionGranted...");            }            @Override            public void onIndividualPermissionGranted(String[] grantedPermission) {                //某个授权                Log.e(TAG,"onIndividualPermissionGranted() called with: grantedPermission = [" + TextUtils.join(",",grantedPermission) + "]");            }            @Override            public void onPermissionDenied() {                //某个拒绝                Log.e(TAG,"onPermissionDenied...");            }            @Override            public void onPermissionDeniedBySystem() {                //用户选择了"不再询问"后,点击"拒绝按钮",执行此方法                Log.e(TAG,"onPermissionDeniedBySystem...");            }        });    }    @Override    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {        super.onRequestPermissionsResult(requestCode, permissions, grantResults);        if (permissionHelper != null) {            permissionHelper.onRequestPermissionsResult(requestCode, permissions, grantResults);        }    }}
原创粉丝点击