Android中运行时权限机制

概述

在Android 6.0以前,我们安装App时会列出安装的App的访问权限,而且只有安装时会出现一次。一旦我们同意并安装了此App,这个App就可以在用户毫不知情的情况下访问权限内的所有东西,比如用户的通信信息,用户的位置等,这会侵犯用户的隐私。
在Android6.0以后,将不会在安装的时候授予权限;取而代之的是,App不得不在运行时一个一个询问用户来授予权限。开发者不能像以前一样随意地调用方法,否则你的App就会崩溃,开发者需要在每个需要权限的地方检查权限,

Normal Permissions与Dangerous Permission
Google将权限分为两类:
一类是Normal Permissions,这类权限一般不涉及用户隐私,是无须用户进行授权的,比如手机振动、访问网络等,这些权限只需要在AndroidManifest.xml中简单声明就好,安装时授权,无须每次使用时都检查权限,而且用户不能取消以上授权;

Normal Permissions如下
android.permission.ACCESS_LOCATION_EXTRA_COMMANDS
android.permission.ACCESS_NETWORK_STATE
android.permission.ACCESS_NOTIFICATION_POLICY
android.permission.ACCESS_WIFI_STATE
android.permission.BLUETOOTH
android.permission.BLUETOOTH_ADMIN
android.permission.BROADCAST_STICKY
android.permission.CHANGE_NETWORK_STATE
android.permission.CHANGE_WIFI_MULTICAST_STATE
android.permission.CHANGE_WIFI_STATE
android.permission.DISABLE_KEYGUARD
android.permission.EXPAND_STATUS_BAR
android.permission.GET_PACKAGE_SIZE
android.permission.INSTALL_SHORTCUT
android.permission.INTERNET
android.permission.KILL_BACKGROUND_PROCESSES
android.permission.MODIFY_AUDIO_SETTINGS
android.permission.NFC
android.permission.READ_SYNC_SETTINGS
android.permission.READ_SYNC_STATS
android.permission.RECEIVE_BOOT_COMPLETED
android.permission.REORDER_TASKS
android.permission.REQUEST_INSTALL_PACKAGES
android.permission.SET_ALARM
android.permission.SET_TIME_ZONE
android.permission.SET_WALLPAPER
android.permission.SET_WALLPAPER_HINTS
android.permission.TRANSMIT_IR
android.permission.UNINSTALL_SHORTCUT
android.permission.USE_FINGERPRINT
android.permission.VIBRATE
android.permission.WAKE_LOCK

android.permission.WRITE_SYNC_SETTINGS

另一类是Dangerous Permissions,一般会涉及用户隐私,需要用户进行授权,比如读取sdcard、访问通信录等。

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: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.permissio

注意:Dangerous Permissions中同一组的任何一个权限被授权了,其他权限也自动被授权。此外,对于申请时弹出的提示框上面的文本说明也是对整个权限组的说明,而不是单个权限的说明。

实现支持运行时权限

通过一个打电话的例子来说明6.0以后如何支持运行时权限

首先我们要在build.gradle中将targetSdkVersion设置为23。

android {
    compileSdkVersion 23
    buildToolsVersion "23.0.3"
    defaultConfig {
        applicationId "com.wen.asyl.permissionsdemo"
        minSdkVersion 15
        targetSdkVersion 23
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

点击按钮调用打电话,进行打电话之前需要先判断当前App是否有android.permission.CALL_PHONE这个权限。如果有,直接进行打电话的操作,如果没有,需要弹出提示框来进行权限的申请。

public void callOnClick(View view){
    //检查app是否有拨打电话的权限
    if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE)!= PackageManager.PERMISSION_GRANTED){
        //如果没有就进行申请
        ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.CALL_PHONE},PERMISSION_REQUEST_CODE);
    }else {
        callPhone();
    }

}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    if (requestCode==PERMISSION_REQUEST_CODE){
        if (grantResults[0]==PackageManager.PERMISSION_GRANTED){
            callPhone();
        }else{
            Toast.makeText(this, "权限被拒绝", Toast.LENGTH_SHORT).show();

        }
        return;
    }
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}

进行拨打电话

Intent intent=new Intent(Intent.ACTION_CALL);
Uri data= Uri.parse("tel:"+"10086");
intent.setData(data);
try {
    startActivity(intent);
} catch (Exception e) {
    e.printStackTrace();
}

 如图所示,onRequestPermissionsResult就是申请权限的回调,如果用户选择“允许”,就会进行打电话操作;


如果用户选择“拒绝”,就弹出Toast显示权限被拒绝。

需要注意的是,如果我们选择“允许”,下一次就不会弹出权限申请提示框了;如果我们点击“拒绝”,则下一次还会弹出权限申请提示框,只不过这一次会多出一个选择项,叫做“不再询问”,如果我们勾选了该选项,则下一次就不会弹出权限申请提示框,而直接调用onRequestPermissionsResult,回调结果为最后一次用户的选择,也就是会弹出我们的toast:“权限被拒绝”

如图所示:


处理“不再询问”选项

如果用户选择了“不再询问”,那么我们每次调用需要访问该权限的API都会失效,为了解决这个问题,我们可以使用shouldShowRequestPermissionRational方法。具体实现方法如下:

if (!ActivityCompat.shouldShowRequestPermissionRationale(this,Manifest.permission.CALL_PHONE)){
        AlertDialog dialog=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);
                //发起跳转
                startActivity(intent);
            }
        }).create();
        dialog.show();
    }
}

我们通过弹出AlertDialog来提醒用户允许访问权限的重要性,点击“确定”的时候会跳到应用开启权限页面,需要用户手动开启权限。如图所示:


小结

在Android6.0以后,权限需手动申请的还有很多,但大部分都是大同小异的,会了一个,基本都差不多理解了。Android中提供了很多框架来简化这个动态添加权限的框架。这种每次需要判断权限很是麻烦,而Android中提供的框架为我们简化了这一操作,我们下一篇博客来讲解一下PermissionsDispatcher框架的使用。

Demo:

https://download.csdn.net/download/wen_haha/10513915

GitHub:

https://github.com/kongkongdaren/PermissionsDemo

猜你喜欢

转载自blog.csdn.net/wen_haha/article/details/80885338