Android6.0权限处理问题

Android6.0运行时权限处理

Android6.0的发布介绍了一种新的权限机制。用户可以在运行时直接管理应用程序的权限,这个功能提升了权限控制的可见性和可控性,同时简化了安装和自动升级过程,用户可以单独撤销或者授予应用程序某项权限,对应用拥有更多的控制权。

应用程序target是Android 6.0及以上(API level 23),要确保在运行时检查和请求权限,在6.0以后有些权限属于危险权限,清单中声明权限,不能保证权限授权通过,为了确定你的app是否授予某个权限,通过checkSelfPermission()方法判断,请求权限使用requestPermissions()方法。即使你的app不是target Android 6.0,你也应该在新的权限机制下测试你的应用。

本文参考博文:
Hongyang:Android 6.0 运行时权限处理完全解析

1. Android权限管理

Google将权限分为两类,一类是普通权限(Normal Permissions),这类权限一般不涉及用户隐私,也不需要用户进行授权,如网络访问,手机震动等,另外一类是危险权限(Dangerous Permission),涉及用户隐私,需要用户授权,如对sd卡读取、访问用户手机通讯录、拨打电话等。

关于Android的危险权限:Dangerous Permissions:

group:android.permission-group.CONTACTS
  permission:android.permission.WRITE_CONTACTS
  permission:android.permission.GET_ACCOUNTS
  permission:android.permission.READ_CONTACTS

group:android.permission-group.PHONE
  permission:android.permission.READ_CALL_LOG
  permission:android.permission.READ_PHONE_STATE
  permission:android.permission.CALL_PHONE
  permission:android.permission.WRITE_CALL_LOG
  permission:android.permission.USE_SIP
  permission:android.permission.PROCESS_OUTGOING_CALLS
  permission:com.android.voicemail.permission.ADD_VOICEMAIL

group:android.permission-group.CALENDAR
  permission:android.permission.READ_CALENDAR
  permission:android.permission.WRITE_CALENDAR

group:android.permission-group.CAMERA
  permission:android.permission.CAMERA

group:android.permission-group.SENSORS
  permission:android.permission.BODY_SENSORS

group:android.permission-group.LOCATION
  permission:android.permission.ACCESS_FINE_LOCATION
  permission:com.google.android.gms.permission.CAR_SPEED
  permission:android.permission.ACCESS_COARSE_LOCATION

group:android.permission-group.STORAGE
  permission:android.permission.READ_EXTERNAL_STORAGE
  permission:android.permission.WRITE_EXTERNAL_STORAGE

group:android.permission-group.MICROPHONE
  permission:android.permission.RECORD_AUDIO

group:android.permission-group.SMS
  permission:android.permission.READ_SMS
  permission:android.permission.RECEIVE_WAP_PUSH
  permission:android.permission.RECEIVE_MMS
  permission:android.permission.RECEIVE_SMS
  permission:android.permission.SEND_SMS
  permission:android.permission.READ_CELL_BROADCASTS

危险权限我们可以通过adb命令查看:

adb shell pm list permissions -d -g

查看权限以及详细信息:

adb shell pm list permissions -d -g -f

查看到危险的权限是一组一组的,如果某一组里面的某个危险权限已被用户授权,那么系统会立即授权其他的权限,而不需要再向用户授权,比如你的app已经授权READ_SMS,那么当你的app需要SEND_SMS,系统会自动授权。弹出的dialog的文本是对权限组的说明,而非单个权限说明。且dialog不可以自定义。

2. 权限相关处理

2.1 添加权限

在AndroidManifest.xml中正常添加权限。

2.2 检查权限

if (ContextCompat.checkSelfPermission(this,
        Manifest.permission.CALL_PHONE)
        != PackageManager.PERMISSION_GRANTED) {

       // 向用户申请权限

} else {

    // 执行权限通过后的事件

}

这里涉及到一个API,ContextCompat.checkSelfPermission(),主要用于检测某个权限是否已经被授予,方法返回值为PackageManager.PERMISSION_DENIED或者PackageManager.PERMISSION_GRANTED。当返回DENIED就需要进行申请授权了。查看源码:

源码:
/**
 * Determine whether <em>you</em> have been granted a particular permission.
 *  确定你是否获得了一个特定的权限
 *  
 * @param permission The name of the permission being checked.
 *  参数permission:你要检测的权限
 *
 * @return {@link android.content.pm.PackageManager#PERMISSION_GRANTED} if you have the
 * permission, or {@link android.content.pm.PackageManager#PERMISSION_DENIED} if not.
 *  返回值PackageManager.PERMISSION_GRANTED授权已成功
 *       PackageManager.PERMISSION_DENIED 授权被拒绝
 *
 * @see android.content.pm.PackageManager#checkPermission(String, String)
 */

public static int checkSelfPermission(@NonNull Context context, @NonNull String permission) {
    if (permission == null) {
        throw new IllegalArgumentException("permission is null");
    }

    return context.checkPermission(permission, android.os.Process.myPid(), Process.myUid());
}

关于返回值,同样到源码里面可以查看:

/**
 * Permission check result: this is returned by {@link #checkPermission}
 * if the permission has been granted to the given package.
 */
public static final int PERMISSION_GRANTED = 0;

/**
 * Permission check result: this is returned by {@link #checkPermission}
 * if the permission has not been granted to the given package.
 */
public static final int PERMISSION_DENIED = -1;

检查完权限之后,如果系统没有授权成功,需要向用户申请权限。

2.3 申请权限

ActivityCompat.requestPermissions(this,
            new String[]{Manifest.permission.CALL_PHONE},
            MY_PERMISSIONS_REQUEST_CALL_PHONE);

该方法是异步的,第一个参数是Context;第二个参数是需要申请的权限的字符串数组;第三个参数为requestCode,主要用于回调的时候检测。

2.4 处理权限申请回调

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

    if (requestCode == MY_PERMISSIONS_REQUEST_CALL_PHONE) {
        if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {

            // 权限申请成功,可以执行事件了

        } else {
            // Permission Denied 权限被拒绝
            // 提示对话框等
        }
        return;
    }
}

对于权限的申请结果,首先验证requestCode定位到你的申请,然后验证grantResults对应于申请的结果,这里的数组对应于申请时的第二个权限字符串数组。如果你同时申请两个权限,那么grantResults的length就为2,分别记录你两个权限的申请结果。

2.5 权限彻底禁止

// Should we show an explanation?
if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,
        Manifest.permission.CALL_PHONE)) 
}

当第一次向用户询问权限时,用户拒绝,而后再申请的弹窗会提示以后不再提示选项,如果用户勾选并拒绝权限,表示用户彻底拒绝权限,那么ActivityCompat.shouldShowRequestPermissionRationale将返回false,一般我们进行判断用户是彻底拒绝,那么想要再申请权限,可以提示用户到设置去修改。

3. 案例实践

public class MainActivity extends AppCompatActivity {

    private static final int CALL_PHONE_RESQUESTCODE = 1;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Button btnCall = (Button) findViewById(R.id.btnCall);
        btnCall.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                testCall();
            }
        });
    }

    public void testCall() {

        // 判断权限是否未被系统授权
        if (ContextCompat.checkSelfPermission(this,
                Manifest.permission.CALL_PHONE)
                != PackageManager.PERMISSION_GRANTED) {

            Log.i("TAG", "is" + ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CALL_PHONE));

            // 重新请求权限
            ActivityCompat.requestPermissions(this,
                    new String[]{Manifest.permission.CALL_PHONE},
                    CALL_PHONE_RESQUESTCODE);
        } else {
            // 否则直接执行事件了
            callPhone();
        }
    }

    public void callPhone() {
        Intent intent = new Intent(Intent.ACTION_CALL);
        Uri data = Uri.parse("tel:" + "10010");
        intent.setData(data);
        startActivity(intent);
    }

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

        if (requestCode == CALL_PHONE_RESQUESTCODE) {
            if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                callPhone();
            } else {
                // Permission Denied,权限被拒绝
//                Toast.makeText(MainActivity.this, "Permission Denied", Toast.LENGTH_SHORT).show();

                // 完全拒绝,可以提示用户跳到设置页面
                if (!ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CALL_PHONE)) {
                    showDialog();
                }
            }
            return;
        }
    }

    public void showDialog() {
        new AlertDialog.Builder(this)
                .setMessage("权限被拒绝,请到权限管理里面设置!")
                .setPositiveButton("去设置", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        Intent intent = new Intent();
                        intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
                        Uri uri = Uri.fromParts("package", MainActivity.this.getPackageName(), null);
                        intent.setData(uri);
                        MainActivity.this.startActivity(intent);
                    }
                })
                .setNegativeButton("取消", null)
                .create().show();
    }
}

基本的处理过程到此结束。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章