[关闭]
@natsumi 2017-01-13T02:32:46.000000Z 字数 6146 阅读 1216

Android M运行时权限【学习篇】【转发+整理】

Android


参考:
大部分转自这一篇
Android M 新的运行时权限开发者需要知道的一切
http://jijiaxin89.com/2015/08/30/Android-s-Runtime-Permission/
Android M(6.0) 权限爬坑之旅
http://www.open-open.com/lib/view/open1445671646351.html

android M 的名字官方刚发布不久,最终正式版即将来临!
android在不断发展,最近的更新 M 非常不同,一些主要的变化例如运行时权限将有颠覆性影响。惊讶的是android社区鲜有谈论这事儿,尽管这事很重要或许在不远的将来会引发很严重的问题。
这是今天我写这篇博客的原因。这里有一切关于android运行时权限你需要知道的,包括如何在代码中实现。现在亡羊补牢还不晚。

1. 新运行时权限

android的权限系统一直是首要的安全概念,因为这些权限只在安装的时候被询问一次。一旦安装了,app可以在用户毫不知晓的情况下访问权限内的所有东西。
难怪一些坏蛋利用这个缺陷恶意收集用户数据用来做坏事了!
android小组也知道这事儿。7年了!权限系统终于被重新设计了。在android6.0棉花糖,app将不会在安装的时候授予权限。取而代之的是,app不得不在运行时一个一个询问用户授予权限。

app将不会在安装的时候授予权限,而是在运行时逐个询问用户

注意权限询问对话框不会自己弹出来。开发者不得不自己调用。如果开发者要调用的一些函数需要某权限而用户又拒绝授权的话,函数将抛出异常直接导致程序崩溃。

用户又拒绝授权,函数将抛出异常,程序崩溃

另外,用户也可以随时在设置里取消已经授权的权限。

取消已经授权的权限

你或许已经感觉到背后生出一阵寒意。。。如果你是个android开发者,意味着要完全改变你的程序逻辑。你不能像以前那样直接调用方法了,你不得不为每个需要的地方检察权限,否则app就崩溃了!
是的。我不能哄你说这是简单的事儿。尽管这对用户来说是好事,但是对开发者来说就是噩梦。我们不得不修改编码不然不论短期还是长远来看都是潜在的问题。
这个新的运行时权限仅当我们设置targetSdkVersion to 23(这意味着你已经在23上测试通过了)才起作用,当然还要是M系统的手机。app在6.0之前的设备依然使用旧的权限系统。

2. 已经发布了的app会发生什么

新运行时权限可能已经让你开始恐慌了。“hey,伙计!我三年前发布的app可咋整呢。如果他被装到android 6.0上,我的app会崩溃吗?!?”
莫慌张,放轻松。android小队又不傻,肯定考虑到了这情况。如果app的targetSdkVersion 低于 23,那将被认为app没有用23新权限测试过,那将被继续使用旧有规则:用户在安装的时候不得不接受所有权限,安装后app就有了那些权限咯!

按照旧有规则,安装时用户不得不接收所有权限

然后app像以前一样奔跑!注意,此时用户依然可以取消已经同意的授权!用户取消授权时,android 6.0系统会警告,但这不妨碍用户取消授权。

用户取消授权收到警告

问题又来了,这时候你的app崩溃吗?
善意的主把这事也告诉了android小组,当我们在targetSdkVersion 低于23的app调用一个需要权限的函数时,这个权限如果被用户取消授权了的话,不抛出异常。但是他将啥都不干,结果导致函数返回值是null或者0.

取消授权后,不同sdk版本的不同反应

别高兴的太早。尽管app不会调用这个函数时崩溃,返回值null或者0可能接下来依然导致崩溃。
好消息(至少目前看来)是这类取消权限的情况比较少,我相信很少用户这么搞。如果他们这么办了,后果自负咯。
但从长远看来,我相信还是会有大量用户会关闭一些权限。我们app不能在新设备完美运行这是不可接受的。
怎样让他完美运行呢,你最好修改代码支持最新的权限系统,而且我建议你立刻着手搞起!
代码没有成功改为支持最新运行时权限的app,不要设置targetSdkVersion 23 发布,否则你就有麻烦了。只有当你测试过了,再改为targetSdkVersion 23 。

警告:现在你在android studio新建项目,targetSdkVersion 会自动设置为 23。如果你还没支持新运行时权限,我建议你首先把targetSdkVersion 降级到22

3. PROTECTION_NORMAL类权限

Android6.0分了几种权限:Normal Permissions(安装时自动授权,用户也不能取消权限) and Dangerous Permissions

当用户安装或更新应用时,系统将授予应用所请求的属于 PROTECTION_NORMAL 的所有权限(安装时授权的一类基本权限)。这类权限包括:

  1. android.permission.ACCESS_LOCATION_EXTRA_COMMANDS
  2. android.permission.ACCESS_NETWORK_STATE
  3. android.permission.ACCESS_NOTIFICATION_POLICY
  4. android.permission.ACCESS_WIFI_STATE
  5. android.permission.ACCESS_WIMAX_STATE
  6. android.permission.BLUETOOTH
  7. android.permission.BLUETOOTH_ADMIN
  8. android.permission.BROADCAST_STICKY
  9. android.permission.CHANGE_NETWORK_STATE
  10. android.permission.CHANGE_WIFI_MULTICAST_STATE
  11. android.permission.CHANGE_WIFI_STATE
  12. android.permission.CHANGE_WIMAX_STATE
  13. android.permission.DISABLE_KEYGUARD
  14. android.permission.EXPAND_STATUS_BAR
  15. android.permission.FLASHLIGHT
  16. android.permission.GET_ACCOUNTS
  17. android.permission.GET_PACKAGE_SIZE
  18. android.permission.INTERNET
  19. android.permission.KILL_BACKGROUND_PROCESSES
  20. android.permission.MODIFY_AUDIO_SETTINGS
  21. android.permission.NFC
  22. android.permission.READ_SYNC_SETTINGS
  23. android.permission.READ_SYNC_STATS
  24. android.permission.RECEIVE_BOOT_COMPLETED
  25. android.permission.REORDER_TASKS
  26. android.permission.REQUEST_INSTALL_PACKAGES
  27. android.permission.SET_TIME_ZONE
  28. android.permission.SET_WALLPAPER
  29. android.permission.SET_WALLPAPER_HINTS
  30. android.permission.SUBSCRIBED_FEEDS_READ
  31. android.permission.TRANSMIT_IR
  32. android.permission.USE_FINGERPRINT
  33. android.permission.VIBRATE
  34. android.permission.WAKE_LOCK
  35. android.permission.WRITE_SYNC_SETTINGS
  36. com.android.alarm.permission.SET_ALARM
  37. com.android.launcher.permission.INSTALL_SHORTCUT
  38. com.android.launcher.permission.UNINSTALL_SHORTCUT

只需要在AndroidManifest.xml中简单声明这些权限就好,安装时就授权。不需要每次使用时都检查权限,而且用户不能取消以上授权。

4. 让你的app支持新运行时权限(Dangerous Permissions)

是时候让我们的app支持新权限模型了,从设置compileSdkVersion and targetSdkVersion 为 23开始吧.

  1. android {
  2. compileSdkVersion 23
  3. ...
  4. defaultConfig {
  5. ...
  6. targetSdkVersion 23
  7. ...
  8. }

例子,用以下方法添加联系人。

  1. private static final String TAG = "Contacts";
  2. private void insertDummyContact() {
  3. // Two operations are needed to insert a new contact.
  4. ArrayList<ContentProviderOperation> operations = new ArrayList<ContentProviderOperation>(2);
  5. // First, set up a new raw contact.
  6. ContentProviderOperation.Builder op =
  7. ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI)
  8. .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, null)
  9. .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, null);
  10. operations.add(op.build());
  11. // Next, set the name for the contact.
  12. op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
  13. .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
  14. .withValue(ContactsContract.Data.MIMETYPE,
  15. ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)
  16. .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME,
  17. "__DUMMY CONTACT from runtime permissions sample");
  18. operations.add(op.build());
  19. // Apply the operations.
  20. ContentResolver resolver = getContentResolver();
  21. try {
  22. resolver.applyBatch(ContactsContract.AUTHORITY, operations);
  23. } catch (RemoteException e) {
  24. Log.d(TAG, "Could not add a new contact: " + e.getMessage());
  25. } catch (OperationApplicationException e) {
  26. Log.d(TAG, "Could not add a new contact: " + e.getMessage());
  27. }
  28. }

上面代码需要WRITE_CONTACTS权限。如果不询问授权,app就崩了。
下一步像以前一样在AndroidManifest.xml添加声明权限。

  1. <uses-permission android:name="android.permission.WRITE_CONTACTS"/>

下一步,不得不再写个方法检查有没有权限。如果没有弹个对话框询问用户授权。然后你才可以下一步创建联系人。
权限被分组了,如下表:
权限分组表格

同一组的任何一个权限被授权了,其他权限也自动被授权。例如,一旦WRITE_CONTACTS被授权了,app也有READ_CONTACTSGET_ACCOUNTS权限了。
源码中被用来检查和请求权限的方法分别是Activity的checkSelfPermissionrequestPermissions。这些方法在API23引入。

  1. final private int REQUEST_CODE_ASK_PERMISSIONS = 123;
  2. private void insertDummyContactWrapper() {
  3. int hasWriteContactsPermission = checkSelfPermission(Manifest.permission.WRITE_CONTACTS);
  4. if (hasWriteContactsPermission != PackageManager.PERMISSION_GRANTED) {
  5. requestPermissions(new String[] {Manifest.permission.WRITE_CONTACTS},
  6. REQUEST_CODE_ASK_PERMISSIONS);
  7. return;
  8. }
  9. insertDummyContact();
  10. }

如果已有权限,insertDummyContact()会执行。否则,requestPermissions被执行来弹出请求授权对话框,如下:

授权对话框

不论用户同意还是拒绝,activity的onRequestPermissionsResult会被回调来通知结果(通过第三个参数),grantResults,如下:

  1. @Override
  2. public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
  3. switch (requestCode) {
  4. case REQUEST_CODE_ASK_PERMISSIONS:
  5. if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
  6. // Permission Granted
  7. insertDummyContact();
  8. } else {
  9. // Permission Denied
  10. Toast.makeText(MainActivity.this, "WRITE_CONTACTS Denied", Toast.LENGTH_SHORT)
  11. .show();
  12. }
  13. break;
  14. default:
  15. super.onRequestPermissionsResult(requestCode, permissions, grantResults);
  16. }
  17. }

这就是新权限模型工作过程。代码真复杂但是只能去习惯它。。。为了让app很好兼容新权限模型,你不得不用以上类似方法处理所有需要的情况。
如果你想捶墙,现在是时候了。。。

5. 处理 “不再提醒”

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注