Android6.0中改进了权限的申请,在安装APP的时候不再需要用户确认任何权限,当APP运行需要某一个“危险”权限时,才需要以对话框的形式向用户申请。这里探究一下Android6.0中权限的问题。
理论摘抄
- 和旧版本一样,需要将申请的所有权限在manifest.xml中进行声明
- Android6.0中将权限大致分为普通权限和“危险”权限
普通权限
用户访问app沙箱外的数据或资源,这些资源并不会泄露用户隐私或影响其他app的操作。eg.设置时区。当app需要普通权限时,系统自动将权限赋予app。
- 普通权限有:
危险权限
app访问沙箱外的数据和资源,而这些数据和资源涉及用户隐私或可能影响用户存储的数据或调用其他app。eg.读取用户联系人。当app申请危险权限时,用户必须明确的同意,app才能获取该权限。
- 危险权限有:
- 相对于普通权限,危险权限多了一个Permission Group 。
- Permission Group作用:
当向用户申请危险权限时,同一个group的会显示同样的申请信息;
当app向用户同时申请一个group的多个权限时,一旦用户同意其中一个,系统自动将其他权限赋予app。
此外还有,特殊权限
SYSTEM_ALERT_WINDOW 和 WRITE_SETTINGS 是非常敏感的权限,但又不属于上述两种权限。使用它的时候,需要在manifest中进行声明,需要通过intent获取用户授权。系统控制对用户的展示信息(这里不进行深究)。
Android6.0申请危险权限模型
- 这里使用调用相机拍照将图片保存到本地。申请权限,展示图片,做为例子。
- 申请危险权限的流程
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
public void applyAccessPermission() {
// check if already got permission
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
// if you think it's necessary explain something to user about
// permission,you can override
// shouldShowRequestPermissionRationale
if (ActivityCompat.shouldShowRequestPermissionRationale(
MainActivity.this,
Manifest.permission.READ_EXTERNAL_STORAGE)) {
} else {
applyPermission();
}
} else {
getAndShowPhoto();
}
}
重复一下申请危险权限的过程:
- 检查是否获取了该权限,已经获取,直接进行相关操作;
- 否则,判断用户是否Override shouldShowRequestPermissionRationale方法,
有,调用该方法向用户解释申请权限的原因,并进一步申请权限;
否则,直接申请权限。
@TargetApi(23)
@Override
public boolean shouldShowRequestPermissionRationale(String permission) {
switch (permission) {
case Manifest.permission.READ_EXTERNAL_STORAGE: {
explainApplyPermissionReason();
}
return true;
default:
break;
}
return super.shouldShowRequestPermissionRationale(permission);
}
protected void explainApplyPermissionReason() {
AlertDialog.Builder builder = new Builder(MainActivity.this);
builder.setTitle("Info");
builder.setMessage("for show external stoage image,please allow me access external storage later !");
builder.setPositiveButton("I Know", new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
applyPermission();
}
});
builder.setOnCancelListener(new OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
applyPermission();
}
});
builder.setCancelable(true);
builder.create().show();
}
-向用户解释,申请权限的原因
@TargetApi(23)
public void applyPermission() {
ActivityCompat.requestPermissions(MainActivity.this,
new String[] { Manifest.permission.READ_EXTERNAL_STORAGE },
REQUEST_PERMISSION_READ_EXTERNAL);
}
- 直接申请危险权限
调用上述方法后,系统会弹出Dialog,让用户对app的权限申请做出响应。用户选择的结果,通过onRequestPermissionsResult传递回来。
@TargetApi(23)
@Override
public void onRequestPermissionsResult(int requestCode,
String[] permissions, int[] grantResults) {
switch (requestCode) {
case REQUEST_PERMISSION_READ_EXTERNAL: {
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
getAndShowPhoto();
} else {
Toast.makeText(MainActivity.this,
"I sorry,lack permission i cann't show image there~",
Toast.LENGTH_LONG).show();
}
}
break;
default:
break;
}
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
- 用户响应申请权限
onRequestPermissionsResult和startActivityForResult非常类似。根据权限申请情况进行相关处理就ok了。
完整代码
- 添加权限/设置targetdkVersion
注意这里必须在android6.0及其以上的机器进行测试
<uses-sdk
android:minSdkVersion="14"
android:targetSdkVersion="23" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
```
- activity_main.xml布局文件
- MainActivity.java
package com.example.android6permissiondemo;
import java.io.File;
import android.Manifest;
import android.annotation.TargetApi;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.content.DialogInterface;
import android.content.DialogInterface.OnCancelListener;
import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.view.View;
import android.widget.ImageView;
import android.widget.Toast;
public class MainActivity extends Activity {
static final String tag = MainActivity.class.getSimpleName();
public static final int REQUEST_TAKE_PHOTO = 0x12;
public static final int REQUEST_PERMISSION_READ_EXTERNAL = 0x18;
private ImageView imageView;
private File mPhotoFile;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imageView = (ImageView) findViewById(R.id.iv);
findViewById(R.id.btn).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// take photo create a external storage file
Intent takePictureIntent = new Intent(
MediaStore.ACTION_IMAGE_CAPTURE);
if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
mPhotoFile = new File(Environment
.getExternalStorageDirectory(), getResources()
.getString(R.string.app_name));
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT,
Uri.fromFile(mPhotoFile));
startActivityForResult(takePictureIntent,
REQUEST_TAKE_PHOTO);
}
}
});
}
/**
* access external storage get and show photo
*/
public void getAndShowPhoto() {
imageView.setVisibility(View.VISIBLE);
imageView.setImageBitmap(BitmapFactory.decodeFile(mPhotoFile
.getAbsolutePath()));
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case REQUEST_TAKE_PHOTO: {
// photo is saved to external storage
if (resultCode == RESULT_OK) {
applyAccessPermission();
}
}
break;
default:
break;
}
super.onActivityResult(requestCode, resultCode, data);
}
/**
* info user why you want to access that permission (if you feel
it's necessary)
*/
protected void explainApplyPermissionReason() {
AlertDialog.Builder builder = new Builder(MainActivity.this);
builder.setTitle("Info");
builder.setMessage("for show external stoage image,please allow me access external storage later !");
builder.setPositiveButton("I Know", new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
applyPermission();
}
});
builder.setOnCancelListener(new OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
applyPermission();
}
});
builder.setCancelable(true);
builder.create().show();
}
@TargetApi(23)
public void applyPermission() {
ActivityCompat.requestPermissions(MainActivity.this,
new String[] { Manifest.permission.READ_EXTERNAL_STORAGE },
REQUEST_PERMISSION_READ_EXTERNAL);
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
public void applyAccessPermission() {
// check if already got permission
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
// if you think it's necessary explain something to user about
// permission,you can override
// shouldShowRequestPermissionRationale
if (ActivityCompat.shouldShowRequestPermissionRationale(
MainActivity.this,
Manifest.permission.READ_EXTERNAL_STORAGE)) {
} else {
applyPermission();
}
} else {
getAndShowPhoto();
}
}
@TargetApi(23)
@Override
public boolean shouldShowRequestPermissionRationale(String permission) {
switch (permission) {
case Manifest.permission.READ_EXTERNAL_STORAGE: {
explainApplyPermissionReason();
}
return true;
default:
break;
}
return super.shouldShowRequestPermissionRationale(permission);
}
@TargetApi(23)
@Override
public void onRequestPermissionsResult(int requestCode,
String[] permissions, int[] grantResults) {
switch (requestCode) {
case REQUEST_PERMISSION_READ_EXTERNAL: {
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
getAndShowPhoto();
} else {
Toast.makeText(MainActivity.this,
"I sorry,lack permission i cann't show image there~",
Toast.LENGTH_LONG).show();
}
}
break;
default:
break;
}
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
“`