Android Permission 权限详解
其实官方文档权限这部分解释的挺清楚的,官方文档也都有能说清的时候和说不清的时候,但不论是否易懂,官方文档都是最权威的,喜欢的童鞋可以查看官方文档:https://developer.android.google.cn/guide/topics/permissions/overview
如果有童鞋不喜欢看很长的官方文档就看我总结的吧:
1.Android的权限改革的一个很明显的分界线是Android 6.0(Api=23),在这之前,只需要在AndroidManifest.xml声明应用需要的权限(需要提醒的是users-permission 不是permission,还有再application标签前,再有就是name别写错,可以用ctrl点击试一下。我就在后面多加了一个空格,搞了半天,找不到问题,明明授权了,应用给的回复也是你没有这个权限。),在用户安装应用的时候,统一授权
而在这之后,不仅需要这样声明(别忘了),还需要在使用到该权限时动态请求相应的权限,这个改革讲道理是合理的,因为用户可能在使用应用时,并不使用其中的一个功能,而这个功能需要权限,那就没必要在安装时统一授权。这样的改革会让开发者麻烦一些,Android 6.0 23之后就要写动态授权的代码逻辑了。同时还会造成一个问题,用户就需要一个一个权限去允许,点多少次。所以android 又出现了权限组的概念,文档中有一段这样的话
这样就中和了,请求频繁的问题,我是这么认为的。
需要注意是官方的提醒:不要因为权限组的设计就逻辑上利用这个,因为andorid版本不同分组可能不一样。
2.那我们就接着先说一下权限的分类,分类中各有什么权限,以及权限组有哪些,里面各有什么权限:
权限分为几个保护级别。保护级别影响是否需要运行时权限请求:
- Normal permissions 正常权限
- Signature permissions 签名权限
- Dangerous permissions 危险权限
需要我们了解的是正常权限和危险权限。
正常权限
正常的权限覆盖了应用程序需要访问沙箱之外的数据或资源的区域,但这些区域对用户隐私或其他应用程序的操作几乎没有风险。例如,设置时区的权限是正常的权限。
如果应用程序在它的清单中声明它需要一个正常的权限,系统会在安装时自动授予该权限。系统不提示用户授予正常权限,用户也不能撤销这些权限。
-
ACCESS_LOCATION_EXTRA_COMMANDS
-
ACCESS_NETWORK_STATE
-
ACCESS_NOTIFICATION_POLICY
-
ACCESS_WIFI_STATE
-
BLUETOOTH
-
BLUETOOTH_ADMIN
-
BROADCAST_STICKY
-
CHANGE_NETWORK_STATE
-
CHANGE_WIFI_MULTICAST_STATE
-
CHANGE_WIFI_STATE
-
DISABLE_KEYGUARD
-
EXPAND_STATUS_BAR
-
GET_PACKAGE_SIZE
-
INSTALL_SHORTCUT
-
INTERNET
-
KILL_BACKGROUND_PROCESSES
-
MODIFY_AUDIO_SETTINGS
-
NFC
-
READ_SYNC_SETTINGS
-
READ_SYNC_STATS
-
RECEIVE_BOOT_COMPLETED
-
REORDER_TASKS
-
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
危险权限
那除了签名权限和普通权限,剩下的就都是危险权限,很多就不详细列举了。我们主要也是要对危险权限动态授权,官方是这么说的
权限组
权限组 | 权限 |
---|---|
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 CALL_PHONE 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 |
STORAGE | READ_EXTERNAL_STORAGE
WRITE_EXTERNAL_STORAGE |
3.最后说一下动态授权怎么使用:声明权限再AndroidManifest.xml 就不贴上去了,最基本的,java代码应该怎么写逻辑呢,官方也给出了示范代码:我做了一些注释
if (ContextCompat.checkSelfPermission(
CONTEXT, Manifest.permission.REQUESTED_PERMISSION) ==
PackageManager.PERMISSION_GRANTED) { //检查是否有该权限
// You can use the API that requires the permission.
performAction(...); //已经获取权限后做自己的功能逻辑
} else if (shouldShowRequestPermissionRationale(...)) {//如果用户选择了已经知道了,不再提醒,这个方法就返回true,你就可以在这里提醒用户,你点了不再提醒,你要用xx功能,那你就自己去授权吧,或者开发者自己做引导的逻辑
// In an educational UI, explain to the user why your app requires this
// permission for a specific feature to behave as expected. In this UI,
// include a "cancel" or "no thanks" button that allows the user to
// continue using your app without granting the permission.
showInContextUI(...); 、、
} else { //去问用户要权限
// You can directly ask for the permission.
// The registered ActivityResultCallback gets the result of this request.
requestPermissionLauncher.launch(
Manifest.permission.REQUESTED_PERMISSION);
}
下面就是我自己写的通讯录的测试例子:
在主Activity中,使用的时候 检查有没有权限,没有权限请求权限
if(ContextCompat.checkSelfPermission(context, Manifest.permission.READ_CONTACTS)!= PackageManager.PERMISSION_GRANTED){
Log.i(TAG, "onCreate: request");
if(ActivityCompat.shouldShowRequestPermissionRationale(this,Manifest.permission.READ_CONTACTS)){
Log.i(TAG, "onCreate: shouldShowRequestPermissionRationale");
Toast.makeText(context,"应用不再提醒获取读取通讯录的权限,如果想获取通讯录,请到应用权限管理中给应用读取通讯录的权限",Toast.LENGTH_SHORT).show();
}else
ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.READ_CONTACTS},1);
}else{
Cursor cursor1 = getContentResolver().query(ContactsContract.Contacts.CONTENT_URI,
null, null, null, null);
Log.i(TAG, "onCreate: 通讯录"+cursor1.getCount());
while (cursor1.moveToNext()) {
int id = cursor1.getInt(cursor1.getColumnIndex(
ContactsContract.Contacts._ID));
String name1 = cursor1.getString(cursor1.getColumnIndex(
ContactsContract.Contacts.DISPLAY_NAME));
Log.i(TAG, "onCreate: 通讯录: "+id+" "+name1);
}
cursor1.close();
}
同时也要在用户做了选择权限是否授权的回调中写逻辑: 重写Activity的 onRequestPermissionsResult
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
Log.i(TAG, "onRequestPermissionsResult: "+permissions[0]+grantResults[0]);
if(requestCode==1){
if(grantResults[0]==0){
Cursor cursor1 = getContentResolver().query(ContactsContract.Contacts.CONTENT_URI,
null, null, null, null);
Log.i(TAG, "onCreate: 通讯录"+cursor1.getCount());
while (cursor1.moveToNext()) {
int id = cursor1.getInt(cursor1.getColumnIndex(
ContactsContract.Contacts._ID));
String name1 = cursor1.getString(cursor1.getColumnIndex(
ContactsContract.Contacts.DISPLAY_NAME));
Log.i(TAG, "onCreate: 通讯录: "+id+" "+name1);
}
cursor1.close();
}else {
Toast.makeText(context,"没有得到权限,无法正常获取通讯录",Toast.LENGTH_SHORT).show();
}
}
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
xiaowang_lj: 我也做了 怎么说这个公司是干啥呢
2301_77986156: 你这个案例和网上一家公司的测试题一模一样
CSDN-Ada助手: 哇, 你的文章质量真不错,值得学习!不过这么高质量的文章, 还值得进一步提升, 以下的改进点你可以参考下: (1)提升标题与正文的相关性;(2)增加条理清晰的目录。
xuanwenchao: 还是你这个方案最靠普!点赞!
zhouzhihao_07: 这是两个实体,虽然结构一样只是名称不一样。