Andorid的权限控制

概述

默认情况下,Android应用程序是没有任何授权的,当应用程序需要使用设备的任何受保护功能(发送网络请求,访问摄像机,发送短信等)时,必须从用户那里获得相应的权限。

在Marshmallow之前,权限都在安装时在项目中的清单文件中定义。在Marshmallow之后,现在必须在运行时请求权限,然后才能使用。PermissionsDispatcher第三方库用于管理运行时权限。

Marshmallow之前的权限控制

当用户从Google Play商店安装应用程序时,系统会向用户显示该应用程序所需的权限列表(有些人将此称为“权限墙”),用户可以接受所有权限,然后继续安装应用程序或决定不安装应用程序。没有办法只授予该应用程序的某些权限,用户没有办法撤消在安装应用程序后的某些权限。

关于系统的权限列表:permissions

Marshmallow之后的权限控制

Marshmallow引入了运行时权限的概念,意味着你的targetSdkVersion<23的话需要做兼容性处理。

当你需要添加新权限时,请先检查是否属于PROTECTION_NORMAL
。在Marshmallow中,Google已将某些权限指定为“安全”,并称为“常规权限”。 这些东西,比如ACCESS_NETWORK_STATE,INTERNET等。 在安装时自动授予常规权限,并且不会提示用户请求权限。

常规权限必须在清单文件中定义:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.android.app.myapp" >
    
    <uses-permission android:name="android.permission.INTERNET" />
    ...
</manifest>

运行时权限

这类权限将向用户显示一个对话框,类似于以下:


runtime-permission.png
  1. 将运行时权限添加到清单文件中:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.codepath.androidpermissionsdemo" >

    <uses-permission android:name="android.permission.READ_CONTACTS" />
    ...
</manifest>
  1. 你需要使用android.support.v4.app.ActivityCompat这个向系统申请权限的工具类,兼容了各种系统版本。如下:
  • ActivityCompat.requestPermissions向系统申请一个或一组权 限
  • ActivityCompat.checkSelfPermissionApp检查自己是否有某个权限
  • ActivityCompat.shouldShowRequestPermissionRationale判断弹出对话框中是否含有“不再询问”的选择框

步骤

  1. 你要有一个运行Android 6.0系统的设备
  2. 将App的targetSdkVersion设置为23
  3. 把AndroidManifest.xml中申请的并且是危险的所有权限都列出来,用ActivityCompat.requestPermissions方法向系统申请权限
  4. 在所在的Activity中OverrideonRequestPermissionsResult方法接受系统权限申请的回调
  5. 处理回调,比如用户拒绝了某个权限,这时App可以弹出一个对话框描述一下App为何需要这个权限等等
// MainActivity.java
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        // In an actual app, you'd want to request a permission when the user performs an action
        // that requires that permission.
        getPermissionToReadUserContacts();
    }

    // Identifier for the permission request
    private static final int READ_CONTACTS_PERMISSIONS_REQUEST = 1;

    // Called when the user is performing an action which requires the app to read the
    // user's contacts
    public void getPermissionToReadUserContacts() {
        // 1) Use the support library version ContextCompat.checkSelfPermission(...) to avoid
        // checking the build version since Context.checkSelfPermission(...) is only available
        // in Marshmallow
        // 2) Always check for permission (even if permission has already been granted)
        // since the user can revoke permissions at any time through Settings
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS)
                != PackageManager.PERMISSION_GRANTED) {

            // The permission is NOT already granted.
            // Check if the user has been asked about this permission already and denied
            // it. If so, we want to give more explanation about why the permission is needed.
            if (shouldShowRequestPermissionRationale(
                    Manifest.permission.READ_CONTACTS)) {
                // Show our own UI to explain to the user why we need to read the contacts
                // before actually requesting the permission and showing the default UI
            }

            // Fire off an async request to actually get the permission
            // This will show the standard permission request dialog UI
            requestPermissions(new String[]{Manifest.permission.READ_CONTACTS},
                    READ_CONTACTS_PERMISSIONS_REQUEST);
        }
    }

    // Callback with the request from calling requestPermissions(...)
    @Override
    public void onRequestPermissionsResult(int requestCode,
                                           @NonNull String permissions[],
                                           @NonNull int[] grantResults) {
        // Make sure it's our original READ_CONTACTS request
        if (requestCode == READ_CONTACTS_PERMISSIONS_REQUEST) {
            if (grantResults.length == 1 &&
                    grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                Toast.makeText(this, "Read Contacts permission granted", Toast.LENGTH_SHORT).show();
            } else {
                // showRationale = false if user clicks Never Ask Again, otherwise true
                boolean showRationale = shouldShowRequestPermissionRationale( this, Manifest.permission.READ_CONTACTS);

                if (showRationale) {
                   // do something here to handle degraded mode
                } else {
                   Toast.makeText(this, "Read Contacts permission denied", Toast.LENGTH_SHORT).show();
                }
            }
        } else {
            super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        }
    }
}

权限组

相关权限分组到一个权限组。 当应用请求属于特定权限组(即READ_CONTACTS)的权限时,Android会向用户询问更高级别的组(CONTACTS)。 这样,当应用程序稍后需要WRITE_CONTACTS权限时,Android可以自动授予此权限,而不会提示用户。

group-permission.png

向后兼容

  1. 你的App指定的targetSdkVersion<23,不过你的设备或者模拟器是Marshmallow
    • 你的app会继续使用旧的权限模型
    • 将在安装时询问AndroidManifest中列出的所有权限。
    • 用户将能够在安装应用程序后撤消权限
    • 使用PermissionChecker.checkSelfPermission方法检查App自身有没有某一个权限,这个方法的返回结果只有三种:
      • PERMISSION_GRANTED: 已授权
      • PERMISSION_DENIED: 没有被授权
      • PERMISSION_DENIED_APP_OP: 没有被授权(如果targetSdkVersion小于23)
  2. 你的App指定的targetSdkVersion>=23,不过你的设备或者模拟器低于Marshmallow
    • 你的app会继续使用旧的权限模型
    • 将在安装时询问AndroidManifest中列出的所有权限。
    • 使用context.checkSelfPermission(permission)方法检查App自身有没有某一个权限。
try {
    final PackageInfo info = context.getPackageManager().getPackageInfo(
        context.getPackageName(), 0);
    targetSdkVersion = info.applicationInfo.targetSdkVersion;
} catch (PackageManager.NameNotFoundException e) {
    e.printStackTrace();
}

public boolean selfPermissionGranted(Context context, String permission) {
    // Android 6.0 以前,全部默认授权
    boolean result = true;

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
       if (targetSdkVersion >= Build.VERSION_CODES.M) {
            // targetSdkVersion >= 23, 使用Context#checkSelfPermission
            result = context.checkSelfPermission(permission)
                    == PackageManager.PERMISSION_GRANTED;
        } else {
            // targetSdkVersion < 23, 需要使用 PermissionChecker
            result = PermissionChecker.checkSelfPermission(context, permission)
                    == PermissionChecker.PERMISSION_GRANTED;
        }
    }
    return result;
}

存储权限

重新思考您是否需要读取/写入存储权限(即android.permission.WRITE_EXTERNAL_STORAGE或android.permission.READ_EXTERNAL_STORAGE),它会为您提供SD卡上的所有文件。 相反,您应该使用Context上的方法来访问外部存储上特定于软件包的目录。 您的应用程序始终可以访问对这些目录的读/写,因此无需请求权限:

// Application-specific call that doesn't require external storage permissions
// Can be Environment.DIRECTORY_PICTURES, Environment.DIRECTORY_PODCASTS, Environment.DIRECTORY_RINGTONES, 
// Environment.DIRECTORY_NOTIFICATIONS, Environment.DIRECTORY_PICTURES, or Environment.MOVIES
File dir = MyActivity.this.getExternalFilesDir(Environment.DIRECTORY_PICTURES);
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,290评论 6 491
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,107评论 2 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,872评论 0 347
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,415评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,453评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,784评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,927评论 3 406
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,691评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,137评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,472评论 2 326
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,622评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,289评论 4 329
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,887评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,741评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,977评论 1 265
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,316评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,490评论 2 348

推荐阅读更多精彩内容