使用权限
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以下),用户在安装应用的时候必须全部同意所有的权限,才可以安装。
用户运行程序时,当用户的接下来要执行一个危险的权限,例如:像程序中写入一个文件,这时我们必须要检查用户是否已经拥有了这个权限,如果用户没有开启这个权限并且还执行了相应的操作,程序会出现报错或者警告提示。
所以我们应该采取如下方案:
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