权限分为两大类:
Normal Permissions
Dangerous Permission
特点:
对于6.0以下的权限及在安装的时候,根据权限声明产生一个权限列表,用户只有在同意之后才能完成app的安装,造成了我们想要使用某个app,就要默默忍受其一些不必要的权限(比如是个app都要访问通讯录、短信等)。而在6.0以后,我们可以直接安装,当app需要我们授予不恰当的权限的时候,我们可以予以拒绝(比如:单机的象棋对战,请求访问任何权限,我都是不同意的)。当然你也可以在设置界面对每个app的权限进行查看,以及对单个权限进行授权或者解除授权。新的权限机制更好的保护了用户的隐私,Google将权限分为两类,一类是Normal Permissions,这类权限一般不涉及用户隐私,是不需要用户进行授权的,比如手机震动、访问网络等;另一类是Dangerous Permission,一般是涉及到用户隐私的,需要用户进行授权,比如读取sdcard、访问通讯录等。
通俗的说:涉及到比较隐私的权限的时不光要在清单文件中声明权限,还要在代码中动态的获取权限。系统会弹出一个对话框,提示用户。
两种权限的大致内容如下:
Normal Permissions如下
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 //访问GMail账户列表
permission:android.permission.READ_CONTACTS //读取联系人
group:android.permission-group.PHONE //手机状态组
permission:android.permission.READ_CALL_LOG //读取通话记录
permission:android.permission.READ_PHONE_STATE //手机状态 如imei等
permission:android.permission.CALL_PHONE //拨打电话
permission:android.permission.WRITE_CALL_LOG //修改通话记录
permission:android.permission.USE_SIP //允许程序使用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 //通过GPS定位 粗劣位置
permission:android.permission.ACCESS_COARSE_LOCATION //通过wifi定位 精细位置
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 //接收WAP PUSH信息
permission:android.permission.RECEIVE_MMS //允许处理、监控、接受彩信的权限
permission:android.permission.RECEIVE_SMS // 允许处理、监控、接受短信的权限
permission:android.permission.SEND_SMS //发送短信
permission:android.permission.READ_CELL_BROADCASTS //获取小区广播
可以通过adb shell pm list permissions -d -g
进行查看。
授权的机制:
如果你申请某个危险的权限,假设你的app早已被用户授权了同一组的某个危险权限,那么系统会立即授权,而不需要用户去点击授权。比如你的app对READ_CONTACTS
已经授权了,当你的app申请WRITE_CONTACTS
时,系统会直接授权通过。此外,对于申请时弹出的dialog上面的文本说明也是对整个权限组的说明,而不是单个权限(ps:这个dialog是不能进行定制的)。
不过需要注意的是,不要对权限组过多的依赖,尽可能对每个危险权限都进行正常流程的申请,因为在后期的版本中这个权限组可能会产生变化。
具体的操作
1.检查是否已经开启的这个权限
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M) {
//1.先判断是否已经有权限
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS) == PackageManager.PERMISSION_GRANTED) {
//已经有了权限
} else {
//没有权限,去申请全选 100是申请全选的请求码
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_CONTACTS}, 100);
}
}
2.申请权限
//申请权限.参数:1.上下文2.申请的权限字符串数组,请求码
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_CONTACTS}, 100);
3.开启了对话框。我们要知道对话框是被点击了允许还是拒绝。需要用到申请权限的回调
/**
* 申请全选返回,这里可以知道用户是否拒绝了申请
*/
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults) {
switch (requestCode) {
case 100: //100是上面申请权限的申请码
if (grantResults.length > 0 && grantResults[0] == PackageManager
.PERMISSION_GRANTED) {
//用户允许权限
} else {
//用户拒绝权限
}
break;
}
}
对于权限的申请结果,首先验证requestCode定位到你的申请,然后验证grantResults对应于申请的结果,这里的数组对应于申请时的第二个权限字符串数组。如果你同时申请两个权限,那么grantResults的length就为2,分别记录你两个权限的申请结果。如果申请成功,就可以做你的事情了。
有个API值得提一下:
// 是否应该显示解释
if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,
Manifest.permission.PERMISSION_GRANTED)) {
//用户没有点击不在询问
}
}else{
//用户点击不在询问,这里无法开启弹窗,应该引导用户去设置开启全权限
}
例子很简单,不过需要注意的是,对于Intent这种方式,很多情况下是不需要授权的甚至权限都不需要的,比如:你是到拨号界面而不是直接拨打电话,就不需要去申请权限;打开系统图库去选择照片;调用系统相机app去牌照等。更多请参考Consider Using an Intent
这里封装了一个工具类
package com.example.yeqiu.test;
import android.annotation.TargetApi;
import android.app.Activity;
import android.app.Fragment;
import android.content.Context;
import android.content.DialogInterface;
import android.content.pm.PackageManager;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AlertDialog;
import java.util.Arrays;
import java.util.List;
public class PermissionUtils {
private static final int REQUEST_PERMISSION_CODE = 1000;
private Object mContext;
private PermissionListener mListener;
private List<String> mPermissionList;
public PermissionUtils(@NonNull Context context){
checkCallingObjectSuitability(context);
this.mContext = context;
}
/**
* 权限授权申请
* @param hintMessage
* 要申请的权限的提示
* @param permissions
* 要申请的权限
* @param listener
* 申请成功之后的callback
*/
public void requestPermissions(@NonNull CharSequence hintMessage,
@Nullable PermissionListener listener,
@NonNull final String... permissions){
if(listener != null){
mListener = listener;
}
mPermissionList = Arrays.asList(permissions);
//没全部权限
if (!hasPermissions(mContext, permissions)) {
//需要向用户解释为什么申请这个权限
boolean shouldShowRationale = false;
for (String perm : permissions) {
shouldShowRationale =
shouldShowRationale || shouldShowRequestPermissionRationale(mContext, perm);
}
if (shouldShowRationale) {
showMessageOKCancel(hintMessage, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
executePermissionsRequest(mContext, permissions,
REQUEST_PERMISSION_CODE);
}
});
}else {
executePermissionsRequest(mContext, permissions,
REQUEST_PERMISSION_CODE);
}
}else if(mListener != null) { //有全部权限
mListener.doAfterGrand(permissions);
}
}
/**
* 处理onRequestPermissionsResult
* @param requestCode
* @param permissions
* @param grantResults
*/
public void handleRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
switch (requestCode) {
case REQUEST_PERMISSION_CODE:
boolean allGranted = true;
for (int grant: grantResults) {
if(grant != PackageManager.PERMISSION_GRANTED){
allGranted = false;
break;
}
}
if (allGranted && mListener != null) {
mListener.doAfterGrand((String[])mPermissionList.toArray());
}else if(!allGranted && mListener != null){
mListener.doAfterDenied((String[])mPermissionList.toArray());
}
break;
}
}
/**
* 判断是否具有某权限
* @param object
* @param perms
* @return
*/
public static boolean hasPermissions(@NonNull Object object, @NonNull String... perms) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
return true;
}
for (String perm : perms) {
boolean hasPerm = (ContextCompat.checkSelfPermission(getActivity(object), perm) ==
PackageManager.PERMISSION_GRANTED);
if (!hasPerm) {
return false;
}
}
return true;
}
/**
* 兼容fragment
* @param object
* @param perm
* @return
*/
@TargetApi(23)
private static boolean shouldShowRequestPermissionRationale(@NonNull Object object, @NonNull String perm) {
if (object instanceof Activity) {
return ActivityCompat.shouldShowRequestPermissionRationale((Activity) object, perm);
} else if (object instanceof Fragment) {
return ((Fragment) object).shouldShowRequestPermissionRationale(perm);
} else if (object instanceof android.app.Fragment) {
return ((android.app.Fragment) object).shouldShowRequestPermissionRationale(perm);
} else {
return false;
}
}
/**
* 执行申请,兼容fragment
* @param object
* @param perms
* @param requestCode
*/
@TargetApi(23)
private void executePermissionsRequest(@NonNull Object object, @NonNull String[] perms, int requestCode) {
if (object instanceof android.app.Activity) {
ActivityCompat.requestPermissions((Activity) object, perms, requestCode);
} else if (object instanceof android.support.v4.app.Fragment) {
((android.support.v4.app.Fragment) object).requestPermissions(perms, requestCode);
} else if (object instanceof android.app.Fragment) {
((android.app.Fragment) object).requestPermissions(perms, requestCode);
}
}
/**
* 检查传递Context是否合法
* @param object
*/
private void checkCallingObjectSuitability(@Nullable Object object) {
if (object == null) {
throw new NullPointerException("传入的context是null");
}
boolean isActivity = object instanceof android.app.Activity;
boolean isSupportFragment = object instanceof android.support.v4.app.Fragment;
boolean isAppFragment = object instanceof android.app.Fragment;
if (!(isSupportFragment || isActivity || (isAppFragment && isNeedRequest()))) {
if (isAppFragment) {
throw new IllegalArgumentException(
"Target SDK needs to be greater than 23 if caller is android.app.Fragment");
} else {
throw new IllegalArgumentException("Caller must be an Activity or a Fragment.");
}
}
}
@TargetApi(11)
private static Activity getActivity(@NonNull Object object) {
if (object instanceof Activity) {
return ((Activity) object);
} else if (object instanceof android.support.v4.app.Fragment) {
return ((android.support.v4.app.Fragment) object).getActivity();
} else if (object instanceof android.app.Fragment) {
return ((android.app.Fragment) object).getActivity();
} else {
return null;
}
}
public static boolean isNeedRequest(){
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M;
}
public void showMessageOKCancel(CharSequence message, DialogInterface.OnClickListener okListener) {
new AlertDialog.Builder(getActivity(mContext))
.setMessage(message)
.setPositiveButton("确定", okListener)
.setNegativeButton("取消", null)
.create()
.show();
}
public interface PermissionListener {
void doAfterGrand(String... permission);
void doAfterDenied(String... permission);
}
}
使用方式:
在activity或者fragment中需要权限的地方使用
PermissionUtils = new PermissionUtils(MainActivity.this);
PermissionUtils.requestPermissions("请授予xx[相机],[读写]权限!",
new PermissionUtils.PermissionListener() {
@Override
public void doAfterGrand(String... permission) {
}
@Override
public void doAfterDenied(String... permission) {
}
}, Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE);
}
在权限回调里交给PermissionUtils处理
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults) {
PermissionUtils.handleRequestPermissionsResult(requestCode, permissions, grantResults);
}
相关链接:http://blog.csdn.net/lmj623565791/article/details/50709663