Android里面的权限是一种安全机制。Android权限主要用于限制应用程序内部某些具有限制性的功能使用,以及应用程序之间的组件访问。但是呢Android6.0之后,Google对权限做了一些优化,将一些权限的申请放在了应用运行的时候去申请(动态获取权限),所以Android6.0之后将权限向分为两类:Normal Permissions(正常权限,一般涉及用户隐私的风险比较小,系统会自动向应用授予该权限)、Dangerous Permission(敏感权限,一般是涉及到用户隐私的,用户必须明确向应用授予该权限)。
不管是Normal Permissions还是Dangerous permissions权限,如果我们在程序中需要使用,那么在AndroidManifest.xml文件里面都是需要注册的。
一,Normal Permissions
普通权限涵盖应用程序需要访问应用程序沙箱外部数据或资源的区域,但是对于用户的隐私或其他应用程序的操作几乎没有风险。
如果应用程序在AndroidManifest.xml文件中注册了需要的Normal Permissions,系统会在安装时自动授予应用该权限。程序使用过程中系统不会提示用户授予正常权限,并且用户无法撤消这些权限(永久授权)。
Normal Permissions权限包含:
权限名 | 解释 |
---|---|
ACCESS_LOCATION_EXTRA_COMMANDS | 允许程序访问额外的定位提供者指令 |
ACCESS_NETWORK_STATE | 允许程序获取网络信息状态,如当前的网络连接是否有效 |
ACCESS_NOTIFICATION_POLICY | 通知APP通知显示在状态栏 |
ACCESS_WIFI_STATE | 允许程序获取当前WiFi接入的状态以及WLAN热点的信息 |
BLUETOOTH | 允许程序连接配对过的蓝牙设备 |
BLUETOOTH_ADMIN | 允许程序进行发现和配对新的蓝牙设备 |
BROADCAST_STICKY | 允许程序收到广播后快速收到下一个广播 |
CHANGE_NETWORK_STATE | 允许程序改变网络状态,如是否联网 |
CHANGE_WIFI_MULTICAST_STATE | 允许程序改变WiFi多播状态 |
CHANGE_WIFI_STATE | 允许程序改变WiFi状态 |
DISABLE_KEYGUARD | 允许程序禁用键盘锁 |
EXPAND_STATUS_BAR | 允许程序扩展或收缩状态栏 |
GET_PACKAGE_SIZE | 允许程序获取应用的文件大小 |
INSTALL_SHORTCUT | 创建快捷方式 |
INTERNET | 允许程序访问网络连接,可能产生GPRS流量 |
KILL_BACKGROUND_PROCESSES | 允许程序调用killBackgroundProcesses(String).方法结束后台进程 |
MANAGE_OWN_CALLS | 允许通过自我管理的ConnectionService API管理自己的调用的调用应用程序 |
MODIFY_AUDIO_SETTINGS | 允许程序修改声音设置信息 |
NFC | 允许程序执行NFC近距离通讯操作,用于移动支持 |
READ_SYNC_SETTINGS | 允许程序读取同步设置,读取Google在线同步设置 |
READ_SYNC_STATS | 允许程序读取同步状态,获得Google在线同步状态 |
RECEIVE_BOOT_COMPLETED | 允许程序开机自动运行 |
REORDER_TASKS | 允许程序重新排序系统Z轴运行中的任务 |
REQUEST_COMPANION_RUN_IN_BACKGROUND | 允许伴随应用在后台运行 |
REQUEST_COMPANION_USE_DATA_IN_BACKGROUND | 允许伴随应用在后台使用数据 |
REQUEST_DELETE_PACKAGES | 允许应用程序请求删除软件包 |
REQUEST_IGNORE_BATTERY_OPTIMIZATIONS | 应用程序必须拥有该权限才能使用ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS |
SET_ALARM | 允许程序设置闹铃提醒 |
SET_WALLPAPER | 允许程序设置桌面壁纸 |
SET_WALLPAPER_HINTS | 允许程序设置壁纸建议 |
TRANSMIT_IR | 允许使用设备的红外发射器 |
USE_FINGERPRINT | 允许应用使用指纹硬件 |
VIBRATE | 允许程序振动 |
WAKE_LOCK | 允许程序在手机屏幕关闭后后台进程仍然运行 |
WRITE_SYNC_SETTINGS | 写入Google在线同步设置 |
如果程序需要用到Normal Permissions权限,那么这些用到的权限都必须在AndroidManifest.xml中注册
二、Dangerous permissions
Dangerous permissions涵盖应用程序需要涉及用户私人信息的数据或资源的区域,或者可能会影响用户的存储数据或其他应用程序的操作。例如,阅读用户联系人的权限是一个危险的权限。如果一个应用程序声明它需要一个危险的权限,用户必须明确授予该应用程序的权限。在用户批准权限之前,您的应用无法提供取决于该权限的功能。
当我们需要使用Dangerous permissions的时候要特别注意,我们分两种情况来说明:
targetSdkVersion >= 23 并且 Android手机 >= 6.0:这个时候Dangerous permissions在使用的时候,就需要我们去动态的申请权限了,比如:当我们需要打开相机拍摄照片的时候需要我们通过代码的方式在需要的地方去申请权限(关于代码的实现方式我们在下文中会提到)。
targetSdkVersion < 23 或者 Android手机 < 6.0:和Normal Permissions权限使用一样,系统会在应用程序安装的时候自动授予应用该权限。
Dangerous Permissions权限包含(特别注意下那些权限是属于同权限一组的):
权限名 | 权限组 | 解释 |
---|---|---|
READ_CALENDAR | CALENDAR(日历组) | 允许程序读取用户的日程信息 |
WRITE_CALENDAR | CALENDAR(日历组) | 允许程序写入日程,但不可读取 |
CAMERA | CAMERA(相机拍照组) | 允许程序访问摄像头进行拍照 |
READ_CONTACTS | CONTACTS(联系人组) | 允许程序访问联系人通讯录信息 |
WRITE_CONTACTS | CONTACTS(联系人组) | 允许程序写入联系人,但不可读取 |
GET_ACCOUNTS | CONTACTS(联系人组) | 允许程序访问账户Gmail列表 |
ACCESS_FINE_LOCATION | LOCATION(定位组) | 允许程序通过GPS芯片接收卫星的定位信息 |
ACCESS_COARSE_LOCATION | LOCATION(定位组) | 允许程序通过WiFi或移动基站的方式获取用户错略的经纬度信息 |
RECORD_AUDIO | MICROPHONE(麦克风组) | 允许程序录制声音通过手机或耳机的麦克 |
READ_PHONE_STATE | PHONE(电话组) | 允许程序访问电话状态 |
READ_PHONE_NUMBERS | PHONE(电话组) | 允许程序读取设备的电话号码 |
CALL_PHONE | PHONE(电话组) | 允许程序从非系统拨号器里拨打电话 |
ANSWER_PHONE_CALLS | PHONE(电话组) | 允许程序接听来电 |
READ_CALL_LOG | PHONE(电话组) | 允许程序读取通话记录 |
WRITE_CALL_LOG | PHONE(电话组) | 允许程序写入(但是不能读)用户的联系人数据 |
ADD_VOICEMAIL | PHONE(电话组) | 允许程序添加语音邮件系统 |
USE_SIP | PHONE(电话组) | 允许程序使用SIP视频服务 |
PROCESS_OUTGOING_CALLS | PHONE(电话组) | 允许程序监视,修改或放弃播出电话 |
BODY_SENSORS | SENSORS(传感器组) | 允许应用程序访问用户用来测量身体内部情况的传感器数据,例如心率 |
SEND_SMS | SMS(手机短信服务组) | 允许程序发送短信 |
RECEIVE_SMS | SMS(手机短信服务组) | 允许程序接收短信 |
READ_SMS | SMS(手机短信服务组) | 允许程序读取短信内容 |
RECEIVE_WAP_PUSH | SMS(手机短信服务组) | 允许程序接收WAP PUSH信息 |
RECEIVE_MMS | SMS(手机短信服务组) | 允许程序接收彩信 |
READ_EXTERNAL_STORAGE | STORAGE(存储组) | 允许程序可以读取设备外部存储空间 |
WRITE_EXTERNAL_STORAGE | STORAGE(存储组) | 允许程序写入外部存储,如SD卡上写文件 |
在介绍Dangerous permissions的时候,提到了权限组,这个权限组有啥用呢。同一组的任何一个权限被授权了,该组的其他权限也自动被授权了。举个例子,例如我们动态授予了READ_CALENDAR权限,这个时候因为READ_CALENDAR是属于CALENDAR组的,CALENDAR组里面同时还包含了WRITE_CALENDAR权限。所以WRITE_CALENDAR权限也被自动授权了。
三、动态申请权限
上文中我们也提到了,当我们targetSdkVersion >= 23 并且 Android手机 >= 6.0(两个条件要同时成立)的时候,如果我们想使用Dangerous permissions里面的权限的话,是需要通过代码去动态申请权限的。
动态申请权限相关的一些函数:
/**
* 检查指定的权限是否授权(Context对象调用)
*/
(Context).checkSelfPermission(@NonNull String permission);
/**
* 在没有授权的情况下,有些时候可能需要提示给用户为什么需要改权限,就通过该函数来实现。
* 关于shouldShowRequestPermissionRationale的返回值问题,我们分三种情况
* 1. 第一次打开App时 -> false
* 2. 上次弹出权限点击了禁止(但没有勾选“下次不在询问”) -> true
* 3. 上次选择禁止并勾选:下次不在询问 -> false
*/
(Activity或者Fragment).shouldShowRequestPermissionRationale(@NonNull String permission);
/**
* 申请指定的权限(Activity或者Fragment对象调用)
* @param permissions 权限列表,可以同时申请多个权限
* @param requestCode 该次权限申请对应的requestCode。和 onRequestPermissionsResult()回调函数里面的requestCode对应
*/
(Activity或者Fragment).requestPermissions(@NonNull String[] permissions, int requestCode);
/**
* 处理请求权限的响应,当用户对请求权限的dialog做出响应之后,系统会回调该函数(Activity或者Fragment中重写)
* @param requestCode 申请权限对应的requestCode
* @param permissions 权限列表
* @param grantResults 权限列表对应的返回值,判断permissions里面的每个权限是否申请成功
*/
onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults);
动态申请权限分为以下几个步骤:
- 明确我们页面需要哪些权限。
- 检查需要的权限是否授权 -> checkSelfPermission()。
- 如果没有授权,则判断是否需要向用户解释为何申请权限。-> shouldShowRequestPermissionRationale() 返回true则可能需要弹出框解释下。(大部分情况下都会直接跳过这一步,不做这一步的处理)
- 申请权限。-> requestPermissions()。
- 处理申请的结果信息。-> 回调函数onRequestPermissionsResult()。
关于动态申请权限网上的开源库也很多,这里我们就简单的介绍其中一种PermissionGen。对应GitHub项目地址:https://github.com/lovedise/PermissionGen。
PermissionGen的使用也是非常简单的。我们先定义一个BaseAppActivity。在BaseAppActivity的onRequestPermissionsResult()方法里面调用PermissionGen.onRequestPermissionsResult(this, requestCode, permissions, grantResults);
ublic abstract class BaseAppActivity extends AppCompatActivity {
protected Context mContext;
protected Activity mActivity;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mContext = this;
mActivity = this;
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
PermissionGen.onRequestPermissionsResult(this, requestCode, permissions, grantResults);
}
}
PermissionGen申请权限,
Activity中
PermissionGen.with(MainActivity.this)
.addRequestCode(100)
.permissions(
Manifest.permission.READ_CONTACTS,
Manifest.permission.RECEIVE_SMS,
Manifest.permission.WRITE_CONTACTS)
.request();
Fragment中
PermissionGen.needPermission(ContactFragment.this, 100,
new String[] {
Manifest.permission.READ_CONTACTS,
Manifest.permission.RECEIVE_SMS,
Manifest.permission.WRITE_CONTACTS
}
);
PermissionGen结果确认。
成功结果
@PermissionSuccess(requestCode = 100)
public void doSomething(){
Toast.makeText(this, "Contact permission is granted", Toast.LENGTH_SHORT).show();
}
失败结果
@PermissionFail(requestCode = 100)
public void doFailSomething(){
Toast.makeText(this, "Contact permission is not granted", t.LENGTH_SHORT).show();
}
我们以一个具体的实例来做一个简单的说明,比如我们在Activity中申请摄像头权限。注意哦,我这里是继承的BaseAppActivity,BaseAppActivity中重写了onRequestPermissionsResult(),里面调用了PermissionGen.onRequestPermissionsResult(this, requestCode, permissions, grantResults);。
public class PermissionGenActivity extends BaseAppActivity {
public static void startUp(Context context) {
context.startActivity(new Intent(context, PermissionGenActivity.class));
}
private static final int CAMERA_PERMISSION = 100;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_permission_gen);
//申请Manifest.permission.CAMERA权限
PermissionGen.with(mActivity).addRequestCode(CAMERA_PERMISSION).permissions(Manifest.permission.CAMERA).request();
}
@PermissionSuccess(requestCode = CAMERA_PERMISSION)
public void requestPhotoSuccess() {
//申请权限成功
}
@PermissionFail(requestCode = CAMERA_PERMISSION)
public void requestPhotoFail() {
onBackPressed();
}
}
关于Android权限(Permissions)处理,有以下几个总结点:
不管是Normal Permissions还是Dangerous Permission对应的权限都必须在AndroidManifest.xml文件里面注册。
只有当targetSdkVersion >= 23 并且 Android手机 >= 6.0两个条件同时满足时,Dangerous Permission对应的权限才需要动态申请。否则还是和Normal Permissions使用一样会在程序安装的时候就获取到权限。
Dangerous Permission权限里面某个组里面一个权限被授权了,那么这个组里面其他的权限也跟着授权了。