Android权限详解

使用权限

android应用默认是不使用任何的权限的,当我们使用某些功能的时候,会要求用户添加某些权限,方可使用。例如请求网络和写入文件等等。
当我们使用某些权限的时候,只需要在Manifest使用<uses-permission>添加权限,例如:
 <uses-permission android:name="android.permission.READ_CONTACTS"></uses-permission>

我们为程序添加了读取联系人权限,当用户请求查看联系人时,程序会弹出请求权限的对话框进行询问是否对App进行授权(Android6.0以上并且targetSdkVersion为23会弹出提示框,后面会进行讲解)。

名词介绍

安全权限

对于安全权限来说,这样的权限使用不会对用户造成用户隐私的泄露,系统会自动在安装时授予该应用程序的权限。例如用户设置时区的权限就是安全的权限。
安全权限列表:

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

危险权限

对于危险权限来说,会涉及到用户的隐私信息和数据的存储,例如读取联系人权限和对用户文件读写权限。
像危险权限,采用动态申请的方式。用户在使用的时候应该先检查用户是否已经对改权限进行授权,之后方可使用,如何操作后文会详细讲解。
注意:不管是安全权限和普通权限都应该在AndroidManifest文件中进行注册。

权限组

在Android权限中,任何权限都属于权限组,当用户是运行的设备是Android6.0(API23),并且应用的targetSdkVersion 是 23 或更高版本,则当用户请求危险权限时系统会发生以下行为:

  • 如果应用请求其清单中列出的危险权限,而应用目前在权限组中没有任何权限,则系统会向用户显示一个对话框,描述应用要访问的权限组。对话框不描述该组内的具体权限。例如,如果应用请求 READ_CONTACTS 权限,系统对话框只说明该应用需要访问设备的联系信息。如果用户批准,系统将向应用授予其请求的权限。
  • 如果应用请求其清单中列出的危险权限,而应用在同一权限组中已有另一项危险权限,则系统会立即授予该权限,而无需与用户进行任何交互。例如,如果某应用已经请求并且被授予了 READ_CONTACTS 权限,然后它又请求 WRITE_CONTACTS,系统将立即授予该权限。 、

任何权限都可属于一个权限组,包括正常权限和应用定义的权限。但权限组仅当权限危险时才影响用户体验。可以忽略正常权限的权限组。

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

关于Android版本

Android 6.0(API 级别 23)或更高版本,并且应用的 targetSdkVersion 是 23 或更高版本

当处于这种情况下,用户安装App时,用户可以不授权任何权限即可安装。但是如果不是这种情况(如Android为6.0一下,或者targetSdkVersion为23以下),用户在安装应用的时候必须全部同意所有的权限,才可以安装。
6.0一下或者targe为23以下

用户运行程序时,当用户的接下来要执行一个危险的权限,例如:像程序中写入一个文件,这时我们必须要检查用户是否已经拥有了这个权限,如果用户没有开启这个权限并且还执行了相应的操作,程序会出现报错或者警告提示。

所以我们应该采取如下方案:

a、检查用户是否具有某个权限可以调用checkSelfPermission()方法

这里介绍两个常量

/**
* Permission check result: this is returned by {@link #checkPermission}
* if the permission has been granted to the given package.
*/
权限已经授权
public static final int PERMISSION_GRANTED = 0;

/**
 * Permission check result: this is returned by {@link #checkPermission}
 * if the permission has not been granted to the given package.
 */
 权限没有授权
public static final int PERMISSION_DENIED = -1;

例:

if (ContextCompat.checkSelfPermission(MainActivity.this,    
    Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) {
            System.out.println("用户没有权限了");
        } else {
            System.out.println("已经有权限了");
        }

当已经授权了,我们执行相应的操作即可,例如查看联系人、读写文件等操作。

b、当用户没有授权,我们应该发出请求授权的请求,去让用户手动授权

//第一次请求用户去授权
ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.READ_CONTACTS}, 2);

这里写图片描述

注意,上图是第一次请求授权,这个弹框是系统的弹框,用户不能去更改

c、当用户每次调用requestPermissions时,都会回调onRequestPermissionsResult方法。

 public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
            @NonNull int[] grantResults) {}

第一个参数:requestCode即为requestPermissions请求的第三个参数,我们可以在这里判断是在哪进行请求的
第二个参数:permissions[]从名字就可以看出来这个是用户请求权限时回调的数组,里面存放的是你请求的所有权限。
第三个参数:int[] grantResults第三个参数与第二个参数相对应,分别为第二个参数所对应的是否用户授权的值,授权为0,没有授权为1。

例如(请求了单个权限):

ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.READ_CONTACTS}, 2);

在onRequestPermissionsResult方法中:
requestCode=2;
permissions=android.permission.READ_CONTACTS
grantResults=-1(点击拒绝)

例如(请求了多个权限):

ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.READ_CONTACTS
,Manifest.permission.WRITE_EXTERNAL_STORAGE}, 2);

在onRequestPermissionsResult方法中:
requestCode=2;
permissions=android.permission.READ_CONTACTS,android.permission.WRITE_EXTERNAL_STORAGE
grantResults=-1(点击拒绝),0(点击了允许)


@Override
    public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
        int flag = -1;
        switch (requestCode) {
            case 2:
                //单个权限             
                if (grantResults.length > 0
                            && grantResults[0] == PackageManager.PERMISSION_GRANTED){ 
                    // 用户同意权限
                } else {
                    // 用户不同意权限
                }
                //多个权限
                if (grantResults.length > 0) {
                    for (int i = 0; i < grantResults.length; i++) {
                        if (grantResults[i] == PackageManager.PERMISSION_GRANTED) {
                            flag = 0;
                        } else {
                            flag=-1;
                            break;
                        }
                    }
                }

                if (flag==0){
                    // 用户同意权限
                }else {
                    // 用户不同意权限
                }
            }
        }
    }

d、shouldShowRequestPermissionRationale,该方法返回一个boolean类型的值这个方法有一个特点,当用户第一次对某一个权限点击拒绝的时候,该方法返回true,当第二次用户再次点击拒绝的时候并且勾选了[不在提示]对话框。该方法返回的是false;

这里写图片描述
上图为第二次申请权限,因为系统已经检测到你已经拒绝过一次权限,所以第二次会弹出不在提示选框

当我们第二次请求权限的时候,我们可以在onRequestPermissionsResult方法中,观察是否为false,并且提醒用户,该权限的重要性,如果无法开启这个权限会严重影响app的使用。

完整代码逻辑,这里讲述的是一个页面中,同时申请了两个权限,缺一不可的情况下,用户可以根据自己的需求进行增加和删除。

package com.example.mac.studypermission1;

import android.Manifest;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.provider.Settings;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

    private Button bt;
    private Button bt1;
    private String[] permissions = new String[]{Manifest.permission.READ_CONTACTS, Manifest.permission.WRITE_EXTERNAL_STORAGE};


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        bt = (Button) findViewById(R.id.bt);
        bt1 = (Button) findViewById(R.id.bt1);
        //第二次授权
        bt1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.READ_CONTACTS, Manifest.permission.WRITE_EXTERNAL_STORAGE}, 2);
            }
        });
        //第一次授权
        bt.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                boolean allPermissions = false;
                for (int i = 0; i < permissions.length; i++) {
                    if (ContextCompat.checkSelfPermission(MainActivity.this, permissions[i]) == PackageManager.PERMISSION_GRANTED) {

                        allPermissions = true;
                    } else {
                        allPermissions = false;
                        break;
                    }
                }
                if (allPermissions == false) {
                    //应该请求授权
                    ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.READ_CONTACTS
                            , Manifest.permission.WRITE_EXTERNAL_STORAGE}, 2);
                } else {
                    System.out.println("以授权");
                }
            }
        });
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
        int flag = -1;
        switch (requestCode) {
            case 2: {
                if (grantResults.length > 0) {

                    for (int i = 0; i < grantResults.length; i++) {
                        if (grantResults[i] == PackageManager.PERMISSION_GRANTED) {
                            flag = 0;
                        } else {
                            flag = -1;
                            break;
                        }
                    }
                }
                if (flag == 0) {
                    System.out.println("以授权");
                } else {
                    // 用户不同意权限,第二次shouldShowRequestPermissionRationale为false
                    boolean isJump = false;
                    for (int i = 0; i < permissions.length; i++) {
                        //第二次还是拒绝权限,这时shouldShowRequestPermissionRationale为false,应该提醒用户,此权限的重要性
                        if (ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this, permissions[i]) == false) {
                            isJump = true;
                        } else {
                            isJump = false;
                            break;
                        }
                    }

                    if (isJump) {
                        AlertDialog.Builder builder = new AlertDialog.Builder(
                                MainActivity.this);
                        builder.setTitle("提示");
                        builder.setMessage("这是一个很重要的权限!");
                        builder.setIcon(R.mipmap.ic_launcher);
                        builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {

                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                Toast.makeText(MainActivity.this, "确定被点击",
                                        Toast.LENGTH_SHORT).show();

                                Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
                                Uri uri = Uri.fromParts("package", getPackageName(), null);
                                intent.setData(uri);
                                startActivityForResult(intent, 10);
                                dialog.dismiss();
                            }
                        });
                        builder.setCancelable(false);
                        builder.show();

                    }

                }

            }
        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        boolean allPermissions = false;
        for (int i = 0; i < permissions.length; i++) {
            //这里从App的设置中心返回时,应该二次检察权限
            if (ContextCompat.checkSelfPermission(MainActivity.this, permissions[i]) == PackageManager.PERMISSION_GRANTED) {
                allPermissions = true;
            } else {
                allPermissions = false;
                break;
            }
        }
        if (allPermissions == false) {
            //应该请求授权
            ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.READ_CONTACTS
                    , Manifest.permission.WRITE_EXTERNAL_STORAGE}, 2);
        } else {
            System.out.println("以授权");
        }
        super.onActivityResult(requestCode, resultCode, data);
    }
}

Android5.1(API 级别 22)或更低版本,并且应用的 targetSdkVersion 是 22 或更低版本

则系统会在用户安装应用时要求用户授予权限。如果将新权限添加到更新的应用版本,系统会在用户更新应用时要求授予该权限。用户一旦安装应用,他们撤销权限的唯一方式是卸载应用。

关于Android 6.0(API 级别 23)或更高版本,并且应用的 targetSdkVersion 是 23 或更高版本的流程图

列表内容

微信公众号:
这里写图片描述

QQ群:365473065

参考文章

https://developer.android.com/index.html?hl=zh-cn

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值