概述
最近项目中涉及到权限开发,发现Android 6.0以后权限被官方重新设计了,经过一顿查阅资料加实践,于是决定结合代码对权限开发知识做个梳理总结,希望对大家有用。(ps:个人第一次写论文,有错误的地方希望能留言指正哈~~~~)。
1.为啥使用权限
由于每个Android应用都在进程沙盒中运行,沙盒内的资源数据是共享开放的,但是想要获取沙盒外的资源数据,需要申明所需权限,部分权限需要用户授权。
2.权限级别
(1)正常权限:不会对用户隐私或设备操作造成很大风险的权限。应用运行时,系统会自动授权该权限。
(2)危险权限:可能影响用户隐私或设备正常操作的权限。例如,读取用户的联系人就属于危险权限。应用运行时需要用户手动授权该权限。
3.授权方式
这里先了解一下权限组的概念,任何权限都属于一个权限组,该权限组可以是多个,也可以是一个。举个栗子:
Permission Group | Single Permission |
---|---|
CAMERA | • CAMERA |
CONTACTS | • READ_CONTACTS • WRITE_CONTACTS • GET_ACCOUNTS |
Android 5.1(API22)及之前:你想授权某权限,系统会在安装时要求用户授权权限组。
Android 6.0(API23)及之后:你想授权某权限,系统会弹出一个描述权限组文字的弹出框,栗如你想要给READ_CONTACTS授权,系统会给出“应用需要访问设备联系人”而不是“应用需要读取设备联系人”的提示。
4.检查权限
如果你的应用需要一个危险权限,你每次执行该权限下的操作时都必须检查你是否被授予了该权限。用户可以随便撤销某个权限,所以即使应用昨天还能使用相机,那也没法保证今天也有权使用。为了检查当前是否拥有该权限.可以调用ContextCompat.checkSelfPermission()方法。
/**
* 检查是否对该权限授权
* 拥有该权限返回true
* 否则返回false
*/
private boolean checkPermission(Context context, String permission){
if(ContextCompat.checkSelfPermission(context,permission) == PackageManager.PERMISSION_GRANTED){
return true;
}
return false;
}
5.请求权限
请求权限发生在用户还没对该权限授权的情况下。
笔者把它分成直接请求和间接请求,拿请求相机权限举栗,所谓直接请求就是点击拍照按钮,我直接请求该权限,看看用户会不会授权,假如请求被拒绝了,那下次再请求该权限可以加个提示框,说明下这个权限对应用将要使用沙盒外的拍照功能很重要,这就形成了间接请求,android提供了shouldShowRequestPermissionRationale()方法告诉程序是直接请求还是间接请求。请求权限用方法ActivityCompat.requestPermissions()。
if(!checkPermission(this, Manifest.permission.CAMERA)){
if(ActivityCompat.shouldShowRequestPermissionRationale(this,Manifest.permission.CAMERA)){
//向用户显示一个解释,等用户看完解释后再继续尝试请求权限
//、、、、、、
}else{
//直接请求权限
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.CAMERA},
REQUEST_PERMISSION_OF_CAMERA);
}
}else{
//权限被授权,打开相机
}
REQUEST_PERMISSION_OF_CAMERA 是应用自定义的一个int常量,用户唯一标识一个权限请求以便回调时进行判断
6.处理请求响应结果
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode){
case REQUEST_PERMISSION_OF_CAMERA:
// 如果请求被取消了,那么结果数组就是空的
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// 权限被授予了,打开相机
} else {
// 权限请求被拒绝了,不能继续依赖该权限的相关操作了
}
break;
}
}
7.请求流程
8.核心完整代码
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.little_seven.permission">
<uses-permission android:name="android.permission.CAMERA"/>
</manifest>
package com.little_seven.permission;
import android.Manifest;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.provider.Settings;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.text.StaticLayout;
import android.util.Log;
import android.view.View;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private static final int REQUEST_PERMISSION_OF_CAMERA = 1000;
private PermissionDialogFragment dialogFragment;
private boolean isFirst = true;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initClick();
}
private void initClick(){
findViewById(R.id.btn_click).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
requestPermission();
}
});
}
private void requestPermission(){
if(!checkPermission(this, Manifest.permission.CAMERA)){
if(ActivityCompat.shouldShowRequestPermissionRationale(this,Manifest.permission.CAMERA)){
//向用户显示一个解释,等用户看完解释后再继续尝试请求权限
dialogFragment = PermissionDialogFragment.newInstance("应用需要获取您的相机权限,以便用于扫描二维码");
dialogFragment .show(getSupportFragmentManager(),"dialog for permission");
dialogFragment.setOnDialogClick(new PermissionDialogFragment.OnDialogClick() {
@Override
public void clickSure() {
ActivityCompat.requestPermissions(MainActivity.this,
new String[]{Manifest.permission.CAMERA},
REQUEST_PERMISSION_OF_CAMERA);
}
@Override
public void clickCancle() {
}
});
}else{
//直接请求权限
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.CAMERA},
REQUEST_PERMISSION_OF_CAMERA);
}
}else{
//权限被授权,打开相机
}
}
/**
* 检查是否对该权限授权
* 拥有该权限返回true
* 否则返回false
*/
private boolean checkPermission(Context context, String permission){
if(ContextCompat.checkSelfPermission(context,permission) == PackageManager.PERMISSION_GRANTED){
return true;
}
return false;
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode){
case REQUEST_PERMISSION_OF_CAMERA:
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// 权限被授予了,打开相机
} else {
// 权限请求被拒绝了,不能继续依赖该权限的相关操作了
if(!ActivityCompat.shouldShowRequestPermissionRationale(this,Manifest.permission.CAMERA)){
if(isFirst){
isFirst = false;
return;
}
dialogFragment = PermissionDialogFragment.newInstance("请前往“设置>应用>[你的应用]”页面进行手动授权相机权限");
dialogFragment .show(getSupportFragmentManager(),"dialog for permission");
dialogFragment.setOnDialogClick(new PermissionDialogFragment.OnDialogClick() {
@Override
public void clickSure() {
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
Uri uri = Uri.fromParts("package", MainActivity.this.getPackageName(), null);
intent.setData(uri);
startActivity(intent);
}
@Override
public void clickCancle() {
}
});
}
}
break;
}
}
}
DialogFragment也贴一下
package com.little_seven.permission;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.DialogFragment;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.TextView;
public class PermissionDialogFragment extends DialogFragment implements View.OnClickListener {
private View contentView;
private TextView tv_content;
private String content;
public static PermissionDialogFragment newInstance(String content){
Bundle bundle = new Bundle();
bundle.putString("content",content);
PermissionDialogFragment fragment = new PermissionDialogFragment();
fragment.setArguments(bundle);
return fragment;
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
getDialog().requestWindowFeature(Window.FEATURE_NO_TITLE);
getDialog().getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
if(contentView == null){
contentView = inflater.inflate(R.layout.dialog_permission,null);
initView(contentView);
}
return contentView;
}
@Override
public void onStart() {
super.onStart();
getDialog().getWindow().getAttributes().gravity = Gravity.CENTER;
}
private void initView(View contentView){
content = getArguments().getString("content");
getDialog().setCancelable(false);
getDialog().setCanceledOnTouchOutside(false);
tv_content = contentView.findViewById(R.id.tv_content);
tv_content.setText(content);
contentView.findViewById(R.id.btn_sure).setOnClickListener(this);
contentView.findViewById(R.id.btn_cancle).setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.btn_sure:
onDialogClick.clickSure();
getDialog().dismiss();
break;
case R.id.btn_cancle:
onDialogClick.clickCancle();
getDialog().dismiss();
break;
}
}
private OnDialogClick onDialogClick;
public void setOnDialogClick(OnDialogClick onDialogClick){
this.onDialogClick = onDialogClick;
}
public interface OnDialogClick{
void clickSure();
void clickCancle();
}
}
9.参考文献
https://blog.csdn.net/shangmingchao/article/details/70312824
https://developer.android.google.cn/guide/topics/security/permissions