Android 6.0带来了新的权限管理方式,默认情况下没有任何应用有权限去执行对其他应用、操作系统、用户有不利影响的操作,这涉及到android 6.0的 Security Architecture(安全体系结构),也是Android安全体系结构的核心,记住这句话后面可以对权限管理有个很好的理解。
1.因为每一个Android 应用都是在一个进程沙盒中运行的,应用必须明确分享的资源和数据,通过申明需要的额外权限这种方式(这些额外权限不由基本沙河提供)。应用静态的声明这些权限(在Manifest里面),然后Android系统会请求用户同意这些权限。
2.Android应用沙盒不依赖于创建应用的技术,特别的是Dalvik虚拟机并不是安全边界的,所有的应用都可以运行native code(比如参见NDK),所有类型的应用-Java,native,hybird,都是以同样的方式封装在沙盒内并且相互之间是同样的安全等级。
以上加粗的两句话不是很懂,懂的大神指教一下。
一个新建的Android应用默认是没有权限的,这意味着它不能执行任何可能对用户体验有不利影响的操作或者访问设备数据。为了使用受保护的功能,你必须包含一个或者多个标签在你的app manifest中。
1.普通权限(一般权限)
如果你的应用manifest中只申明了普通权限(也就是说,这些权限对于用户隐私和设备操作不会造成太多危险),系统会自动授予这些权限。
2.运行时权限(危险权限)
如果你的应用manifest中声明了运行时权限(也就是说,这些权限可能会影响用户隐私和设备的普通操作),系统会明确的让用户决定是否授予这些权限。系统请求用户授予这些权限的方式是由当前应用运行的系统版本来决定的。
普通权限(一般权限)和运行时权限(危险权限)的列表就不贴出来了,大家可以去这里参考。
1.如果你的设备运行的是Android6.0(API level 23)及以上的系统,并且你的应用的targetSdkVersion也是23或者更高,那么应用向用户请求这些权限是实时的。这意味着用户可以随时取消 这些运行时权限的授权。所以应用在每次需要用到这些运行时权限的时候都需要去检查是否还有这些权限的授权。
2.Android 5.1及以下的系统
如果你的设备运行在Android5.1(API level 22)及以下的系统中,或者你的app的targetSdkVersion是22或者更低。系统会请求用户在apk安装的时候授予这些权限。
3.如果你的应用更新的时候添加了一个权限,系统会在用户更新应用的时候请求用户授予这个权限,一旦用户安装了这个应用,唯一可以取消授权的方式就是卸载掉这个应用。注意这句话的意思,想一下如果app的targetSdkVersion是22或者以下,但是运行在Android6.0及以上的设备中会有什么问题?
这里说一下:安装过程中,会一起请求用户授予所有权限,如果用户拒绝,将不能安装这个app,只有用户全部同意这些授权,才能安装这个应用,但是问题来了,安装好了这个应用之后,android6.0以上的系统中,用户是可以去设置中取消授权的,而且是随时都可以取消,所以很多运行时权限可能也得不到,目前官方的做法是,如果用户取消该项授权,那么依赖该项授权的方法的返回值为null,所以你的app可能会报空指针异常。以后是否会针对22以下的app做改变还不得而知,毕竟crash是很难让人接受的,但是crash是由用户造成的,用户应该也可以理解。
常常来说一个授权失败会抛出SecurityException,然而这并不是在 所有情况下都会发生。比如,发送一个广播去检查授权(SendBroadcast(Intent)),数据会被发送给所有接收者,但是当这个方法的请求返 回的时候,你不会收到任何一个因为授权失败抛出的异常,其实在大多数情况下,授权失败只会打印系统日志。
任何应用都可以去请求想要授权的权限,以下参考应用demo:
有一个问题,检测授权时,弹出对话框有一个复选框按钮,如果用户拒绝授权不再提示了,那么问题来了,如果我退出第二次启动的时候,应用就无法用了吗?当然是否定的。看下流程图:
多了一步判断,授权失败后,第二次启动的时候就提示自定的对话框让用户选择。
大家了解了这么多,接下来看下demo的代码:
主界面MainActivity:
package com.lai.permissiondemo;
import android.Manifest;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
/**
* Created by LaiYingtang on 2016/5/18.
* 主界面
*/
public class MainActivity extends Activity {
private static final int REQUEST_CODE = 0;//请求码
private CheckPermission checkPermission;//检测权限器
//配置需要取的权限
static final String[] PERMISSION = new String[]{
Manifest.permission.WRITE_EXTERNAL_STORAGE,// 写入权限
Manifest.permission.READ_EXTERNAL_STORAGE, //读取权限
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
checkPermission = new CheckPermission(this);
}
@Override
protected void onResume() {
super.onResume();
//缺少权限时,进入权限设置页面
if (checkPermission.permissionSet(PERMISSION)) {
startPermissionActivity();
}
}
//进入权限设置页面
private void startPermissionActivity() {
PermissionActivity.startActivityForResult(this, REQUEST_CODE, PERMISSION);
}
//返回结果回调
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
//拒绝时,没有获取到主要权限,无法运行,关闭页面
if (requestCode == REQUEST_CODE && resultCode == PermissionActivity.PERMISSION_DENIEG) {
finish();
}
}
}
启动主Activity后去检测android版本,检查权限的工具类:CheckPermission类:
package com.lai.permissiondemo;
import android.content.Context;
import android.content.pm.PackageManager;
import android.support.v4.content.ContextCompat;
/**
* Created by LaiYingtang on 2016/5/18.
*
* 检查权限的工具类
*/
public class CheckPermission {
private final Context context;
//构造器
public CheckPermission(Context context) {
this.context = context.getApplicationContext();
}
//检查权限时,判断系统的权限集合
public boolean permissionSet(String... permissions) {
for (String permission : permissions) {
if (isLackPermission(permission)) {//是否添加完全部权限集合
return true;
}
}
return false;
}
//检查系统权限是,判断当前是否缺少权限(PERMISSION_DENIED:权限是否足够)
private boolean isLackPermission(String permission) {
return ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_DENIED;
}
}
检测SDKAPI如果是大于或等于23的时候,则弹出自定义的对话框提示用户是否授权,拒绝的话提示帮助对话框去设置应用里面去打开权限,否则退出。
package com.lai.permissiondemo;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
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;
/**
* Created by LaiYingtang on 2016/5/18.
* 用户权限获取页面,权限处理
*/
public class PermissionActivity extends Activity {
//首先声明权限授权
public static final int PERMISSION_GRANTED = 0;//标识权限授权
public static final int PERMISSION_DENIEG = 1;//权限不足,权限被拒绝的时候
private static final int PERMISSION_REQUEST_CODE = 0;//系统授权管理页面时的结果参数
private static final String EXTRA_PERMISSION = "com.lai.permissiondemo";//权限参数
private static final String PACKAGE_URL_SCHEME = "package:";//权限方案
private CheckPermission checkPermission;//检测权限类的权限检测器
private boolean isrequestCheck;//判断是否需要系统权限检测。防止和系统提示框重叠
//启动当前权限页面的公开接口
public static void startActivityForResult(Activity activity, int requestCode, String... permission) {
Intent intent = new Intent(activity, PermissionActivity.class);
intent.putExtra(EXTRA_PERMISSION, permission);
ActivityCompat.startActivityForResult(activity, intent, requestCode, null);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.permission_layout);
if (getIntent() == null || !getIntent().hasExtra(EXTRA_PERMISSION))//如果参数不等于配置的权限参数时
{
throw new RuntimeException("当前Activity需要使用静态的StartActivityForResult方法启动");//异常提示
}
checkPermission = new CheckPermission(this);
isrequestCheck = true;//改变检测状态
}
//检测完之后请求用户授权
@Override
protected void onResume() {
super.onResume();
if (isrequestCheck) {
String[] permission = getPermissions();
if (checkPermission.permissionSet(permission)) {
requestPermissions(permission); //去请求权限
} else {
allPermissionGranted();//获取全部权限
}
} else {
isrequestCheck = true;
}
}
//获取全部权限
private void allPermissionGranted() {
setResult(PERMISSION_GRANTED);
finish();
}
//请求权限去兼容版本
private void requestPermissions(String... permission) {
ActivityCompat.requestPermissions(this, permission, PERMISSION_REQUEST_CODE);
}
//返回传递过来的权限参数
private String[] getPermissions() {
return getIntent().getStringArrayExtra(EXTRA_PERMISSION);
}
/**
* 用于权限管理
* 如果全部授权的话,则直接通过进入
* 如果权限拒绝,缺失权限时,则使用dialog提示
*
* @param requestCode 请求代码
* @param permissions 权限参数
* @param grantResults 结果
*/
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (PERMISSION_REQUEST_CODE == requestCode && hasAllPermissionGranted(grantResults)) //判断请求码与请求结果是否一致
{
isrequestCheck = true;//需要检测权限,直接进入,否则提示对话框进行设置
allPermissionGranted(); //进入
} else { //提示对话框设置
isrequestCheck = false;
showMissingPermissionDialog();//dialog
}
}
//显示对话框提示用户缺少权限
private void showMissingPermissionDialog() {
AlertDialog.Builder builder = new AlertDialog.Builder(PermissionActivity.this);
builder.setTitle(R.string.help);//提示帮助
builder.setMessage(R.string.string_help_text);
//如果是拒绝授权,则退出应用
//退出
builder.setNegativeButton(R.string.quit, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
setResult(PERMISSION_DENIEG);//权限不足
finish();
}
});
//打开设置,让用户选择打开权限
builder.setPositiveButton(R.string.settings, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
startAppSettings();//打开设置
}
});
builder.setCancelable(false);
builder.show();
}
//获取全部权限
private boolean hasAllPermissionGranted(int[] grantResults) {
for (int grantResult : grantResults) {
if (grantResult == PackageManager.PERMISSION_DENIED) {
return false;
}
}
return true;
}
//打开系统应用设置(ACTION_APPLICATION_DETAILS_SETTINGS:系统设置权限)
private void startAppSettings() {
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
intent.setData(Uri.parse(PACKAGE_URL_SCHEME + getPackageName()));
startActivity(intent);
}
}
以上是android 6.0申请权限的主要代码,注释说明写的很清楚,博友还感兴趣的话可以到后面下载源码。
来看一下效果:
这里我说下几点要注意的问题:
1.在MainActivity中一定要写入需要请求的权限:
//配置需要取的权限
static final String[] PERMISSION = new String[]{
Manifest.permission.WRITE_EXTERNAL_STORAGE,// 写入权限
Manifest.permission.READ_EXTERNAL_STORAGE, //读取权限
};
2.manifest清单文件里面也要配置需要的权限:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
3.MainActivity中请求的结果回调,请求码与结果码一定要一一对应,否则即使授权后无法回到主界面。
//返回结果回调
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
//拒绝时,没有获取到主要权限,无法运行,关闭页面
if (requestCode == REQUEST_CODE && resultCode == PermissionActivity.PERMISSION_DENIEG) {
finish();
}
}
最后附上demo的下载地址
源码下载