一、概述
Andorid6.0版本自发布以来,相信不少开发者对于Android6.0版本来说,最大的手头工作无非就是做好6.0申请权限的工作,我们一旦没动态申请权限的话,那么将会报出一系列异常。当然有些权限的需要我们去动态申请的,而有些是不需要的,那么,这里估计会有人问:哪些需要或者哪些不需要?其实说白了,需要我们动态去申请的权限是属于危险权限,而我们不需要去动态申请的权限则是普通权限。下面我们将会介绍。所以,我们开发者有务必对6.0新版本sdk带来权限机制的变化。好的,那么今天我们将介绍Andorid6.0带来的运行时权限的一些处理。
二、运行时权限的变化
新的权限机制更好的保护了用户的隐私,Google将权限分为两类,一类是Normal Permissions正常的权限,这类权限一般不涉及用户隐私,是不需要用户进行授权的,比如手机震动、访问网络等,这类权限也是属于我们文章中概述介绍的非6.0该有申请的权限;另一类是Dangerous Permission危险的权限,一般是涉及到用户隐私的,需要用户进行授权,比如读取sdcard、访问通讯录等,这类权限正好是我们6.0新版本所需要申请的权限。
三、Normal Permissions权限和Dangerous Permissions列表
Normal Permissions:
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_INSTALL_PACKAGES
SET_ALARM
SET_TIME_ZONE
SET_WALLPAPER
SET_WALLPAPER_HINTS
TRANSMIT_IR
UNINSTALL_SHORTCUT
USE_FINGERPRINT
VIBRATE
WAKE_LOCK
WRITE_SYNC_SETTINGS
Dangerous Permissions:
group:android.permission-group.CONTACTS
permission:android.permission.WRITE_CONTACTS
permission:android.permission.GET_ACCOUNTS
permission:android.permission.READ_CONTACTS
group:android.permission-group.PHONE
permission:android.permission.READ_CALL_LOG
permission:android.permission.READ_PHONE_STATE
permission:android.permission.CALL_PHONE
permission:android.permission.WRITE_CALL_LOG
permission:android.permission.USE_SIP
permission:android.permission.PROCESS_OUTGOING_CALLS
permission:com.android.voicemail.permission.ADD_VOICEMAIL
group:android.permission-group.CALENDAR
permission:android.permission.READ_CALENDAR
permission:android.permission.WRITE_CALENDAR
group:android.permission-group.CAMERA
permission:android.permission.CAMERA
group:android.permission-group.SENSORS
permission:android.permission.BODY_SENSORS
group:android.permission-group.LOCATION
permission:android.permission.ACCESS_FINE_LOCATION
permission:android.permission.ACCESS_COARSE_LOCATION
group:android.permission-group.STORAGE
permission:android.permission.READ_EXTERNAL_STORAGE
permission:android.permission.WRITE_EXTERNAL_STORAGE
group:android.permission-group.MICROPHONE
permission:android.permission.RECORD_AUDIO
group:android.permission-group.SMS
permission:android.permission.READ_SMS
permission:android.permission.RECEIVE_WAP_PUSH
permission:android.permission.RECEIVE_MMS
permission:android.permission.RECEIVE_SMS
permission:android.permission.SEND_SMS
permission:android.permission.READ_CELL_BROADCASTS
PS:Normal Permissions普通权限是不需要我们在Andorid6.0去动态申请权限的,因为他属于普通权限,一般如果需要用到,我们直接在清单文件里面添加即可。Dangerous Permissions危险权限,则是需要我们在Android6.0中去动态申请权限的。看到Dangerous Permissions危险权限的时候,大家有没有发现都是一组一组的,没错,正是如此!其实之所以一组一组的,是因为Google故意设计的。
那么估计会有人问到,分组的权限机智对于我们开发者有影响吗,确实是有的,好比你现在拿一只安卓6.0版本的手机在运行程序,如果你申请某个危险的权限,假设你的app早已被用户授权了同一组的某个危险权限,那么系统会立即授权,而不需要用户去点击授权。比如你的app对READ_CONTACTS
已经授权了,当你的app申请WRITE_CONTACTS
时,系统会直接授权通过。此外,对于申请时弹出的dialog上面的文本说明也是对整个权限组的说明,而不是单个权限(ps:这个dialog是不能进行定制的)。
最后值得注意的是:我们在Android6.0申请危险权限的时候,我们要根据需求从而申请对应的权限列表,不能申请错误的权限列表哦!否则会出现一些错误,有关对应的权限名称,可自行上网百度。
四、相关的API以及动态申请权限的步骤
大概的步骤分为:检查权限--申请权限--处理申请权限的回调事件,其中,还有一个申请权限时的拒绝事件,也就是说点击了拒绝申请权限的事件,通常这个容易开开发者忽略,不过下面我们将会讲到。首先我们先来看看申请权限的几个基本步骤:
1、检查权限
if (ContextCompat.checkSelfPermission(MainActivity.this, CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {
}
这里主要涉及到了一个API:
ContextCompat.
checkSelfPermission主要是用于检测当前某个权限是否已经被赋予了。如果当前权限不被
PackageManager.PERMISSION_GRANTED所赋予了,我们就需要对他进行权限的申请。
2、申请权限
ActivityCompat.requestPermissions(MainActivity.this, new String[]{CALL_PHONE}, MY_PERMISSIONS_REQUEST_CALL_PHONE);
该方法是用来申请权限的,
第一个参数是Context;第二个参数是需要申请的权限的字符串数组;第三个参数为requestCode,主要用于回调的时候检测。也就是我们自己定义的一个结果码,可以从方法名
requestPermissions
以及第二个参数看出,是支持一次性申请多个权限的,因为他是数组,系统会通过对话框
逐一
询问用户是否授权。
3、处于申请权限回调事件
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
switch (requestCode) {
case MY_PERMISSIONS_REQUEST_CALL_PHONE: {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
CallPhone();
} else {
Toast.makeText(this, "授权失败!", Toast.LENGTH_LONG).show();
}
break;
}
}
}
对于申请权限的回调事件,我们首先先判断一个和我们申请权限的结果码是否一致,如果一致的话,才能对我们申请的结果进行处理,这里的CallPhone方法我们主要是模拟打电话。具体代码下面会给出。
4、最后有一个API值得提醒一下,就是文章中所说的拒绝回调事件,不知道大家记起来了没有,在文章中曾说过,没记得没关系,下面我将为大家隆重介绍。
if (ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this, CALL_PHONE)) {
}
该方法就是我们讲的拒绝权限申请回调事件,比如用户现在点击拒绝了,他会立刻执行到申请回调事件中的失败事件,在该事件中我们一般提示用户一下申请拒绝就可以了。然后等到第二次申请权限的时候,就会立刻执行 shouldShowRequestPermissionRationale方法体,在这个方法体中你需要给用户一个解释,为什么要授权,则使用该方法。一般在开发当中,我们可以在该方法体中用Toast提示用户说--请同意授权后,才能拨打电话,这是常见的用法之一,但是还有一种比较流氓的手法,就是直接打开手机的设置面板,让用户手动开启该权限,这种做法确实比较流氓的,哈哈....下面最终案例里面会有涉及到这种用法,这么说一来,我确实也是流氓的
好了,相关的API以及动态申请权限的步骤我已经介绍到这里。下面让我们一起来看看简单的例子。
五、简单的例子
这里我们将模拟打电话,大家也知道电话权限是属于危险权限的,在6.0版本中需要我们动态去申请该权限,所以我正好拿来这里做讲解,大家顺便看看6.0打电话是如何处理动态申请权限的。
当然我们这里代码也是非常简单的,无非就是检查权限,申请权限,处理权限回调事件。
MainActivity文件
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.provider.Settings;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import static android.Manifest.permission.CALL_PHONE;
public class MainActivity extends AppCompatActivity {
//初始按钮控件和申请权限结果码
private Button call_phone;
private static final int MY_PERMISSIONS_REQUEST_CALL_PHONE = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
call_phone = (Button) this.findViewById(R.id.call_phone);
call_phone.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//这里我们将模拟申请打电话权限
TellPhone();
}
});
}
private void TellPhone() {
//检查当前是否有CALL_PHONE电话这个权限
if (ContextCompat.checkSelfPermission(MainActivity.this, CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {
//用户拒绝申请该权限的回调事件
if (ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this, CALL_PHONE)) {
//提示该用户拒绝了权限信息
Toast.makeText(MainActivity.this, "请同意授权后,才能拨打电话", Toast.LENGTH_LONG).show();
// 帮跳转到该应用的设置界面,让用户手动同意打开电话授权(这里采用了流氓做法....)
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
Uri uri = Uri.fromParts("package", getPackageName(), null);
intent.setData(uri);
startActivity(intent);
Log.i("申请拒绝", "申请拒绝");
} else {
Log.i("正在申请", "正在申请");
ActivityCompat.requestPermissions(MainActivity.this, new String[]{CALL_PHONE}, MY_PERMISSIONS_REQUEST_CALL_PHONE);
}
} else {
Log.i("申请成功", "申请成功");
CallPhone();
}
}
//拨打电话
private void CallPhone() {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_CALL); // 设置动作
Uri data = Uri.parse("tel:" + 10086); // 设置数据
intent.setData(data);
startActivity(intent);
}
//处理权限申请的回调事件
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
switch (requestCode) {
//如果当前跟我们申请权限的结果码一致,则立刻执行
case MY_PERMISSIONS_REQUEST_CALL_PHONE: {
//申请权限成功
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
CallPhone();
} else {
//申请权限失败
Toast.makeText(this, "申请权限失败", Toast.LENGTH_LONG).show();
}
break;
}
}
}
}
布局文件我就不贴了,没什么撒,就只有一个Button而已。
当我们在安卓6.0机器上运行的时候,点击按钮后会立刻弹出权限窗口,这时候你可以选择拒绝或者同意。
当你选择拒绝后会提示申请回调事件中的申请权限失败信息,此时我们还执行不到拒绝回调事件当中。拒绝回调事件会在拒绝后第二次打开时候才执行的。
当你选择同意后会立刻执行CallPhone方法拨打电话。
例子很简单,但是以后遇到6.0危险权限的时候,我们记得动态去申请对应的权限。好了,今天关于Andorid--6.0运行时权限的处理讲解到这里,希望对大家有所帮助!