您的位置:首页 > 移动开发 > Android开发

Android 6.0 运行时权限理解

2016-10-13 11:39 281 查看
关于 Android 6.0 Changes之Runtime Permissions再记录一下,回顾回顾。

官方文档:
https://developer.android.com/about/versions/marshmallow/android-6.0-changes.html#behavior-runtime-permissions
在Android 6.0 (API level 23)以前,app运行所需的权限都是在AndroidManifest文件中声明的,例如:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>然后在app安装的时候,用户只有全部授权这些权限才能安装。
当你的app要运行在Android
6.0 (API level 23) 或者更高版本时,你就不得不在运行时检查和申请一些dangerous permission了。

dangerous
permission有哪些呢?看下图



以上的dangerous
permission 是按组来划分的,一共九组。

关于Permission groups 官方文档:
https://developer.android.com/guide/topics/security/permissions.html#normal-dangerous
对于同一组的某个危险权限,只要该组其他当中的一个权限被用户授权过了,那么该权限在申请的时候系统会立即授权。比如你的app对
READ_CONTACTS
已经授权了,当你的app申请
WRITE_CONTACTS
时,系统会直接授权通过。

对于这些dangerous
permission 除了在AndroidManifest文件中声明以外,在还需在代码中真正需要用到该权限的时候做一些处理,后面看demo。

除了dangerous
permission,就是一些Normal Permissions,Normal Permissions只需要在在AndroidManifest文件中声明,在安装的时候,系统会自动给予授权。

好了,看个简单demo就明白了:

该demo内容是往外部存储设备写数据,显示是需要下面权限的

WRITE_EXTERNAL_STORAGE

这是一个dangerous permission,现在该app要运行在6.0的机器的上。

首先上代码:

package cj.com.runtimepermissions;

import android.Manifest;
import android.content.Context;
import android.content.DialogInterface;
import android.content.pm.PackageManager;
import android.content.pm.PermissionInfo;
import android.os.Environment;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Toast;

import java.io.File;
import java.io.IOException;

public class MainActivity extends AppCompatActivity {
private static final int MY_PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE = 1;
private boolean mExternalStorageAvailable = false;
private boolean mExternalStorageWriteable = false;

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

/**
* 点击保存按钮,往外部存储设备写数据
*
* @param view
*/
public void click(View view) {
String externalStorageState = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(externalStorageState)) {
mExternalStorageAvailable = true;
mExternalStorageWriteable = true;
} else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(externalStorageState)) {
mExternalStorageAvailable = true;
mExternalStorageWriteable = false;
} else {
mExternalStorageAvailable = mExternalStorageWriteable = false;
}
handleExternalStorageState(mExternalStorageAvailable,mExternalStorageWriteable);
}

/**
*
*/
private void handleExternalStorageState(boolean mExternalStorageAvailable,boolean mExternalStorageWriteable ) {
if(mExternalStorageAvailable && mExternalStorageWriteable){
//STORAGE is dangerous so check
// checkSelfPermission()
int permissionCheck = ContextCompat.checkSelfPermission(this,
Manifest.permission.WRITE_EXTERNAL_STORAGE);
Log.d("permission", "permissionCheck=="+permissionCheck);
if (permissionCheck != PackageManager.PERMISSION_GRANTED) {
//has not granted request permission
// Should we show an explanation?
if (ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.WRITE_EXTERNAL_STORAGE)) {

// Show an expanation to the user *asynchronously* -- don't block
// this thread waiting for the user's response! After the user
// sees the explanation, try again to request the permission.
// Toast.makeText(MainActivity.this, "Permission++++", Toast.LENGTH_SHORT).show();

// ActivityCompat.requestPermissions(this,
// new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
// MY_PERMISSIONS_REQUEST_READ_CONTACTS);
new AlertDialog.Builder(this)
.setMessage("您拒绝过授予访问外部存储设备的权限,但是只有申请该权限,才能往外部存储设备写入数据,你确定要重新申请获取权限吗?")
.setPositiveButton("ok", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
//again request permission
ActivityCompat.requestPermissions(MainActivity.this,
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
MY_PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE);

}
})
.setNegativeButton("no", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
})
.create()
.show();
} else {
// No explanation needed, we can request the permission.
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
MY_PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE);

// MY_PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE is an
// app-defined int constant. The callback method gets the
// result of the request.
}

}else{
//had granted
Toast.makeText(MainActivity.this, "run writeDatasToExternalStorage() method", Toast.LENGTH_SHORT).show();
// writeDatasToExternalStorage();
}
}else{
Log.d("permission", "ExternalStorage can not be write or unAvailable");
}
}

/**
*
*/
private void writeDatasToExternalStorage(){
File directory = Environment.getExternalStorageDirectory();// 6.0 /storage/emulated/0 5.0 /storage/sdcard
/*
Environment.getDataDirectory();// /data
Environment.getRootDirectory();// /system
Environment.getDownloadCacheDirectory();// /cache
Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES);// 6.0 /storage/emulated/0/Pictures 5.0/storage/sdcard/Pictures
*/
File file = new File(directory, "permisson");
if(!file.exists()){
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
}

/**
*
* @param requestCode
* @param permissions
* @param grantResults
*/
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
Log.d("permission", "onRequestPermissionsResult requestCode" + requestCode);
switch (requestCode) {
case MY_PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE: {
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {

// permission was granted, yay! Do the
// contacts-related task you need to do.
Toast.makeText(MainActivity.this, "run writeDatasToExternalStorage() method", Toast.LENGTH_SHORT).show();
// writeDatasToExternalStorage();

} else {
// Permission Denied
Toast.makeText(MainActivity.this, "Permission Denied", Toast.LENGTH_SHORT).show();
// permission denied, boo! Disable the
// functionality that depends on this permission.
}
return;
}

// other 'case' lines to check for other
// permissions this app might request
}
super.onRequestPermissionsResult(requestCode, permissions, grantResults);

}
}


程序界面就是一个保存的button,点击该button:
首先会去获取外部存储设备的状态,关于获取外部存储设备的状态,这里再回顾一下Environment的API

String externalStorageState = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(externalStorageState)) {
mExternalStorageAvailable = true;
mExternalStorageWriteable = true;
} else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(externalStorageState)) {
mExternalStorageAvailable = true;
mExternalStorageWriteable = false;
} else {
mExternalStorageAvailable = mExternalStorageWriteable = false;
}

官方API https://developer.android.com/reference/android/os/Environment.html
如果外部存储设备的是mounted,这个时候就准备往该设备写入数据,但是在写入之前就必须检查下面权限

Manifest.permission.WRITE_EXTERNAL_STORAGE

这个dangerous permission 是否已经被授权:

//STORAGE is dangerous so check
// checkSelfPermission()
int permissionCheck = ContextCompat.checkSelfPermission(this,
Manifest.permission.WRITE_EXTERNAL_STORAGE);

返回值就就代表该权限有没有被授权

PackageManager.PERMISSION_GRANTED

该值就表示被授权了,如果已经被授权了,那么可以直接往外部存储设备写数据了,

否则还需要去申请权限:

if (permissionCheck != PackageManager.PERMISSION_GRANTED) {
//has not granted request permission
// Should we show an explanation?
if (ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.WRITE_EXTERNAL_STORAGE)) {

// Show an expanation to the user *asynchronously* -- don't block
// this thread waiting for the user's response! After the user
// sees the explanation, try again to request the permission.
// Toast.makeText(MainActivity.this, "Permission++++", Toast.LENGTH_SHORT).show();

// ActivityCompat.requestPermissions(this,
// new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
// MY_PERMISSIONS_REQUEST_READ_CONTACTS);
new AlertDialog.Builder(this)
.setMessage("您拒绝过授予访问外部存储设备的权限,但是只有申请该权限,才能往外部存储设备写入数据,你确定要重新申请获取权限吗?")
.setPositiveButton("ok", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
//again request permission
ActivityCompat.requestPermissions(MainActivity.this,
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
MY_PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE);

}
})
.setNegativeButton("no", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
})
.create()
.show();
} else {
// No explanation needed, we can request the permission.
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
MY_PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE);

// MY_PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE is an
// app-defined int constant. The callback method gets the
// result of the request.
}

}else{
//had granted
Toast.makeText(MainActivity.this, "run writeDatasToExternalStorage() method", Toast.LENGTH_SHORT).show();
// writeDatasToExternalStorage();
}


如果该权限还没有被授权,就去申请权限,如果该权限之前申请没有被拒绝过,也就是第一次申请该权限,那么下面的方法返回结果为false
ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.WRITE_EXTERNAL_STORAGE)

如果之前申请权限被拒绝过,再一次申请权限的时候,上面方法就会返回true,就是有必要跟用户解释一下,为什么需要要授权该权限之类,解释完后,还是需要去请求权限的。
请求权限的方法如下:

// No explanation needed, we can request the permission.
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
MY_PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE);

// MY_PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE is an
// app-defined int constant. The callback method gets the
// result of the request.

方法有三个参数分别是 thisActivity,权限数组,code值。权限数组表明我们请求权限每次可以请求多个,code值是用于后面,请求权限回调函数判断。
接下来,等待请求权限的回调,看下面回调函数:

public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
Log.d("permission", "onRequestPermissionsResult requestCode" + requestCode);
switch (requestCode) {
case MY_PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE: {
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {

// permission was granted, yay! Do the
// contacts-related task you need to do.
Toast.makeText(MainActivity.this, "run writeDatasToExternalStorage() method", Toast.LENGTH_SHORT).show();
// writeDatasToExternalStorage();

} else {
// Permission Denied
Toast.makeText(MainActivity.this, "Permission Denied", Toast.LENGTH_SHORT).show();
// permission denied, boo! Disable the
// functionality that depends on this permission.
}
return;
}

// other 'case' lines to check for other
// permissions this app might request
}
super.onRequestPermissionsResult(requestCode, permissions, grantResults);

}

回调函数参数包括了,之前请求的code值,之前请求的权限以及这些权限请求的回答(是否被授权的了)。
代码就这些了,看一下运行效果



log如下:

D/permission: permissionCheck==-1
D/permission: onRequestPermissionsResult requestCode1
V/RenderScript: 0x7fcaa0d7b000 Launching thread(s), CPUs 2
E/Surface: getSlotFromBufferLocked: unknown buffer: 0x7fcaa9574110
D/permission: permissionCheck==-1
E/Surface: getSlotFromBufferLocked: unknown buffer: 0x7fcaa9575680
D/permission: onRequestPermissionsResult requestCode1
E/Surface: getSlotFromBufferLocked: unknown buffer: 0x7fcaa95758b0
D/permission: permissionCheck==0
E/Surface: getSlotFromBufferLocked: unknown buffer: 0x7fcaa9575d80
D/permission: permissionCheck==0
E/Surface: getSlotFromBufferLocked: unknown buffer: 0x7fcaa9575e60
最后提一下ubuntu的录制gif动态图的工具:
参考:http://blog.csdn.net/zheng5229875/article/details/47358963
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息