Android_动态权限管理的解决方案
目录
1.前言
(1).由于MIUI等部分国产定制系统也有权限管理,没有相关api,故无法判断用户是否允许获取联系人等隐私。在Android 6.0之后,新增权限管理可以通过官方api判断用户的运行状态;
(2).我们指定targetSdkVersion为23或者之后我们还需要在运行时请求这些所需的权限。这很重要,因为已经出现了很多开发者把targetSdkVersion飙到了最新,然后发现自己的app疯狂的崩溃,这是由于他们没有实现执行运行时权限请求的代码。当你已经把一个targeting API 为23或者之后的app发布到了Google Play上,这更是一个问题,你无法立即把那个apk的targeting API替换成更早的版本。
2.权限分析
从Android6.0开始,权限分为普通权限和许可权限。许可权限分类归组,一个权限授权之后,该组下的权限均可使用。
(1)普通权限
只需要在xml申请即可,使用方法和之前6.0以前的一样。在应用安装应用时,会默认获得许可。
(2)许可权限
可执行 $adb shell pm list permissions -d -g
同一组的任何一个权限被授权了,其他权限也自动被授权。例如,一旦WRITE_CONTACTS被授权了,app也有READ_CONTACTS和GET_ACCOUNTS了。
源码中被用来检查和请求权限的方法分别是Activity的checkSelfPermission和requestPermissions,这些方法api23引入。
3.相关方法
(1).ContextCompat.checkSelfPermission()
检查应用是否拥有该权限,被授权返回值为PERMISSION_GRANTED,否则返回PERMISSION_DENIED
(2).ActivityCompat.requestPermissions()
将弹出请求授权对话框,这个方法在M之前版本调用,OnRequestPermissionsResultCallback 直接被调用,带着正确的 PERMISSION_GRANTED或者 PERMISSION_DENIED 。
(3).AppCompatActivity.onRequestPermissionsResult()
该方法类似于Activity的OnActivityResult()的回调方法,主要接收请求授权的返回值
1. //版本判断
2. if (Build.VERSION.SDK_INT >= 23) {
3. //减少是否拥有权限
4. int checkCallPhonePermission = ContextCompat.checkSelfPermission(getApplicationContext(), permission);
5. if (checkCallPhonePermission != PackageManager.PERMISSION_GRANTED) {
6. //弹出对话框接收权限
7. this, new String[]{permission}, id);
8. return;
9. }
1. @Override
2. public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
3. super.onRequestPermissionsResult(requestCode, permissions, grantResults);
4.
5. if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
6. //TODO:已授权
7. else {
8. //TODO:用户拒绝
9. }
10. }
4.封装
1. public class BaseActivity extends AppCompatActivity {
2. private Map<Integer, Runnable> allowablePermissionRunnables = new HashMap<>();
3. private Map<Integer, Runnable> disallowablePermissionRunnables = new HashMap<>();
4.
5. @Override
6. protected void onCreate(Bundle savedInstanceState) {
7. super.onCreate(savedInstanceState);
8. }
9.
10. /**
11. * 请求权限
12. * @param id 请求授权的id 唯一标识即可
13. * @param permission 请求的权限
14. * @param allowableRunnable 同意授权后的操作
15. * @param disallowableRunnable 禁止权限后的操作
16. */
17. protected void requestPermission(int id, String permission, Runnable allowableRunnable, Runnable disallowableRunnable) {
18. if (allowableRunnable == null) {
19. throw new IllegalArgumentException("allowableRunnable == null");
20. }
21.
22. allowablePermissionRunnables.put(id, allowableRunnable);
23. if (disallowableRunnable != null) {
24. disallowablePermissionRunnables.put(id, disallowableRunnable);
25. }
26.
27. //版本判断
28. if (Build.VERSION.SDK_INT >= 23) {
29. //减少是否拥有权限
30. int checkCallPhonePermission = ContextCompat.checkSelfPermission(getApplicationContext(), permission);
31. if (checkCallPhonePermission != PackageManager.PERMISSION_GRANTED) {
32. //弹出对话框接收权限
33. this, new String[]{permission}, id);
34. return;
35. else {
36. allowableRunnable.run();
37. }
38. else {
39. allowableRunnable.run();
40. }
41. }
42.
43. @Override
44. public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
45. super.onRequestPermissionsResult(requestCode, permissions, grantResults);
46.
47. if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
48. Runnable allowRun = allowablePermissionRunnables.get(requestCode);
49. allowRun.run();
50. else {
51. Runnable disallowRun = disallowablePermissionRunnables.get(requestCode);
52. disallowRun.run();
53. }
54. }
55. }
1. public class MainActivity extends BaseActivity implements View.OnClickListener{
2. private Button btCallPhone;
3. private Button btContact;
4.
5. @Override
6. protected void onCreate(Bundle savedInstanceState) {
7. super.onCreate(savedInstanceState);
8. setContentView(R.layout.activity_main);
9.
10. btCallPhone = (Button) findViewById(R.id.call_phone);
11. btContact = (Button) findViewById(R.id.contact);
12.
13. this);
14. this);
15. }
16.
17. @Override
18. public void onClick(View v) {
19. if(v == btCallPhone){
20. //拨打电话
21. 1, Manifest.permission.CALL_PHONE, new Runnable() {
22. @Override
23. public void run() {
24. callPhone();
25. }
26. new Runnable() {
27. @Override
28. public void run() {
29. callPhoneDenied();
30. }
31. });
32. else if(v == btContact){
33. //读取联系人信息
34. 2, Manifest.permission.WRITE_CONTACTS, new Runnable() {
35. @Override
36. public void run() {
37. readContact();
38. }
39. new Runnable() {
40. @Override
41. public void run() {
42. readContactDenied();
43. }
44. });
45. }
46. }
47.
48. private void callPhone() {
49. this, "CALL_PHONE OK", Toast.LENGTH_SHORT)
50. .show();
51. }
52.
53. private void callPhoneDenied() {
54. this, "CALL_PHONE Denied", Toast.LENGTH_SHORT)
55. .show();
56. }
57.
58. private void readContact() {
59. ContentResolver cr = getContentResolver();
60. String str[] = {ContactsContract.CommonDataKinds.Phone.CONTACT_ID, ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME, ContactsContract.CommonDataKinds.Phone.NUMBER,
61. ContactsContract.CommonDataKinds.Phone.PHOTO_ID};
62. Cursor cur = cr.query(
63. null,
64. null, null);
65. int count = cur.getCount();
66. cur.close();
67.
68. this, String.format("发现%s条", count), Toast.LENGTH_SHORT)
69. .show();
70. }
71.
72. private void readContactDenied() {
73. this, "Contact Denied", Toast.LENGTH_SHORT)
74. .show();
75. }
76. }