6.0权限管理和框架

现在Android6.0以上系统的手机都需要申请权限啦.开发者再也不能随意的上传用户的手机信息,不能随意的拿到存储空间,通讯录等信息了,此时就需要对权限做一次申请,当用户同意后才可进行操作.

由于每个Android应用程序都在一个进程沙箱中运行,因此应用程序必须明确请求访问其沙箱外的资源和数据。他们通过声明他们所需的权限来申请这个访问权限,以获得基本沙箱不提供的附加功能。根据区域的敏感程度,系统可以自动授予权限,也可以提示用户批准或拒绝请求。

Google官方权限介绍 https://developer.android.google.cn/guide/topics/permissions/requesting.html#permissions

权限组

普通权限(PROTECTION_NORMAL)

普通权限可以直接在清单文件Manifast中注册即用,从API级别23开始,以下权限分类为PROTECTION_NORMAL:以下为普通权限:

权限 用途
ACCESS_LOCATION_EXTRA_COMMANDS 允许应用程序访问额外的位置提供程序命令。
ACCESS_NETWORK_STATE 允许应用程序访问有关网络的信息。
ACCESS_NOTIFICATION_POLICY 对希望访问通知政策的应用程序的标记许可。
ACCESS_WIFI_STATE 允许应用程式存取有关Wi-Fi网路的资讯。
BLUETOOTH 允许应用程序连接到配对的蓝牙设备。
BLUETOOTH_ADMIN 允许应用程式发现并配对蓝牙装置。
BROADCAST_STICKY 允许应用程式广播粘性意图。
CHANGE_NETWORK_STATE 允许应用程序更改网络连接状态。
CHANGE_WIFI_MULTICAST_STATE 允许应用程序进入Wi-Fi多播模式。
CHANGE_WIFI_STATE 允许应用程式更改Wi-Fi连线状态。
DISABLE_KEYGUARD 允许应用程序在不安全的情况下禁用键盘锁。
EXPAND_STATUS_BAR 允许应用程序展开或折叠状态栏。
GET_PACKAGE_SIZE 允许应用程序查找任何包使用的空间。
INSTALL_SHORTCUT 允许应用程序在Launcher中安装快捷方式。
INTERNET 允许应用程序使用网络。
KILL_BACKGROUND_PROCESSES 允许应用程序调用 killBackgroundProcesses(String)。
MODIFY_AUDIO_SETTINGS 允许应用修改全局音频设置。
NFC 允许应用程序通过NFC执行I / O操作。
READ_SYNC_SETTINGS 允许应用程式读取同步设定。
READ_SYNC_STATS 允许应用程序读取同步统计信息。
RECEIVE_BOOT_COMPLETED 允许应用程序ACTION_BOOT_COMPLETED在系统完成启动后接收 广播。
REORDER_TASKS 允许应用程序更改任务的Z顺序。
REQUEST_IGNORE_BATTERY_OPTIMIZATIONS 应用程序必须持有许可才能使用 ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS。
REQUEST_INSTALL_PACKAGES 允许应用程序请求安装软件包。
SET_ALARM 允许应用程序广播一个意图为用户设置警报。
SET_TIME_ZONE 允许应用程序设置系统时区。
SET_WALLPAPER 允许应用程式设定墙纸。
SET_WALLPAPER_HINTS 允许应用程式设定壁纸提示。
TRANSMIT_IR 允许使用设备的红外发射器(如果有的话)。
UNINSTALL_SHORTCUT 此权限不再受支持。
USE_FINGERPRINT 允许应用使用指纹硬件。
VIBRATE 允许访问振动器。
WAKE_LOCK 允许使用电源管理器唤醒锁定功能,让处理器免于睡眠或屏幕变暗。
WRITE_SYNC_SETTINGS 允许应用程式写入同步设定。

这些权限直接写到清单文件中即可使用,

危险权限和权限组(Dangerous permissions and permission groups)

权限组中的权限只需申请一个即可使用权限组内其他权限,但是一样的需要在Manifast中注册

权限组 权限 说明
CALENDAR • READ_CALENDAR
• WRITE_CALENDAR
• 用于与用户日历相关的运行时权限。
CAMERA • CAMERA • 用于与访问摄像头或从设备捕捉图像/视频相关联的权限。
CONTACTS • READ_CONTACTS
• WRITE_CONTACTS
• GET_ACCOUNTS
• 用于与此设备上的联系人和配置文件相关的运行时权限。
LOCATION • ACCESS_FINE_LOCATION
• ACCESS_COARSE_LOCATION
• 用于允许访问设备位置的权限。
MICROPHONE • RECORD_AUDIO • 用于与从设备访问麦克风音频相关联的权限。
PHONE • READ_PHONE_STATE
• READ_PHONE_NUMBERS
• CALL_PHONE
• ANSWER_PHONE_CALLS(必须在运行时请求)
• READ_CALL_LOG
• WRITE_CALL_LOG
• ADD_VOICEMAIL
• USE_SIP
• PROCESS_OUTGOING_CALLS
• 用于与电话功能关联的权限。
SENSORS • BODY_SENSORS • 用于与访问摄像头或从设备捕捉图像/视频相关联的权限。
SMS • SEND_SMS
• RECEIVE_SMS
• READ_SMS
• RECEIVE_WAP_PUSH
• RECEIVE_MMS
• 用于与用户的SMS消息相关的运行时权限。
STORAGE • READ_EXTERNAL_STORAGE
• WRITE_EXTERNAL_STORAGE
• 用于与共享外部存储相关的运行时权限。

权限申请

单个权限申请

我们先申请一个内部存储权限READ_EXTERNAL_STORAGE

  • 首先在Manifast.xml中添加权限
1
2
3
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<!--如果你还需要写入权限-->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
检查是否拥有权限
1
2
3
4
5
6
7
8
int permissionCheck = ContextCompat.checkSelfPermission(activity, Manifest.permission.READ_EXTERNAL_STORAGE);
if (permissionCheck == PackageManager.PERMISSION_GRANTED) {
//拥有了权限
return true;
} else if (permissionCheck == PackageManager.PERMISSION_DENIED) {
//未拥有此权限
return false;
}

完整的检查权限封装

1
2
3
4
5
6
7
8
9
10
//检查是否拥有单一权限
public static boolean checkPermission(Activity activity, String permission) {
int permissionCheck = ContextCompat.checkSelfPermission(activity, permission);
if (permissionCheck == PackageManager.PERMISSION_GRANTED) {
return true;
} else if (permissionCheck == PackageManager.PERMISSION_DENIED) {
return false;
}
return false;
}

判断权限是否被拒绝过
1
ActivityCompat.shouldShowRequestPermissionRationale(activity, permission)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
if (ActivityCompat.shouldShowRequestPermissionRationale(activity, permission)) {
new MaterialDialog.Builder(activity)
.title("提示")
.content("你必须给予该应用权限,以保证程序的正常运行")
.positiveText("我知道了")
.cancelable(true)
.canceledOnTouchOutside(false)
.onPositive(new MaterialDialog.SingleButtonCallback() {
@Override
public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) {
ActivityCompat.requestPermissions(activity, new String[]{permission}, MY_PERMISSION_REQUEST);
}
})
.cancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialogInterface) {
activity.finish();
}
})
.show();
}
权限返回状态处理
1
2
3
4
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}

requestCode是请求权限时的权限码,permissions是权限集合,单个权限的话取`permissions[0]即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//请求权限返回接口
@Override
public static void onRequestPermissionsResult(Activity activity, int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
switch (requestCode) {
case MY_PERMISSION_REQUEST: //单个权限的申请结果
//如果请求被取消,结果数组为空。
if (grantResults.length > 0) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
//同意了权限
if (mPermissionCallbacks != null) {
mPermissionCallbacks.onPermissionsGranted(permissions);
}
} else {
//未同意权限
new MaterialDialog.Builder(activity)
.title("警告")
.content("拒绝权限将导致部分功能不可用,请谨慎选择")
.cancelable(true)
.canceledOnTouchOutside(true)
.cancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialogInterface) {
mPermissionCallbacks.onPermissionsGranted(null);
}
})
.show();
}
}
break;
多个权限申请思路

要申请多个权限,可以循环判断权限是否拥有,再将没有拥有的权限添加到集合中

1
2
String[] arr = permissionList.toArray(new String[permissionList.size()]);
ActivityCompat.requestPermissions(activity, arr, MY_PERMISSIONS_REQUEST);

权限的返回结果也需要进行判断

第三方权限申请库

RxPermisssion

RxPermission是一个非常好用的第三方权限申请库,由RxJava支持

请在GitHub上使用最新的版本 https://github.com/tbruyelle/RxPermissions

Glide

1
compile 'com.tbruyelle.rxpermissions:rxpermissions:0.9.4@aar'

初始化

1
RxPermissions rxPermissions = new RxPermissions(this); // where this is an Activity instance

  • 请求单个权限
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // 必须在初始化阶段 如onCreate中
    rxPermissions
    .request(Manifest.permission.CAMERA)
    .subscribe(granted -> {
    if (granted) { // Always true pre-M
    // 已经获取了相机权限
    } else {
    // 权限被拒绝
    }
    });

注: granted ->是Retrolambda 写法,Android Studio3.0已全面支持Java8

  • 请求多个权限并统一处理

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    rxPermissions
    .request(Manifest.permission.CAMERA,
    Manifest.permission.READ_PHONE_STATE)
    .subscribe(granted -> {
    if (granted) {
    // 获得了所有权限
    } else {
    // 并未获得所有权限
    }
    });
  • 请求多个权限并分别判定处理

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    rxPermissions
    .requestEach(Manifest.permission.CAMERA,
    Manifest.permission.READ_PHONE_STATE)
    .subscribe(permission -> { // will emit 2 Permission objects
    if (permission.granted) {
    // `permission.name` is granted !
    } else if (permission.shouldShowRequestPermissionRationale) {
    // Denied permission without ask never again
    } else {
    // Denied permission with ask never again
    // Need to go to the settings
    }
    });