Andorid的权限控制

概述

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

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

Marshmallow之前的权限控制

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

关于系统的权限列表:permissions

Marshmallow之后的权限控制

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

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

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


    
    
    ...

运行时权限

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


Andorid的权限控制_第1张图片
runtime-permission.png
  1. 将运行时权限添加到清单文件中:


    
    ...

  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可以自动授予此权限,而不会提示用户。

Andorid的权限控制_第2张图片
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);

你可能感兴趣的:(Andorid的权限控制)