Android 6.0 运行时权限说明与封装

运行时权限说明

Android 6.0引入了一种新的权限模式,将系统的权限分为正常权限和危险权限。开发者在使用到危险权限相关的功能时不仅要在manifest中配置,还要在代码中进行权限的判断和获取。

  • 正常权限:不涉及用户的隐私,不需要在运行时向其授权。主要是震动,蓝牙,访问网络等权限。查看所有正常权限
  • 危险权限:涉及用户的隐私,需要在运行时向其授权。主要是使用相机,拨打电话,收发短信,获取位置信息,读写sd卡等权限。此页面下拉查看所有危险权限及分组

系统将危险权限进行了分组,当用户对组内的某一个权限进行了授权,下次请求组内其他权限时系统会默认直接授予。

相关API

  • 检查权限
    当应用需要危险权限时,每次执行这一操作都必须检查是否具有该权限。检查是否具有某项权限调用 ContextCompact.checkSelfPermission() 方法。
/**
*  @return 
*  已授权 PackageManager.PERMISSION_GRANTED
*  未授权 PERMISSION_DENIED。
*/
public static int checkSelfPermission(Context context,
    String permission) {
}
  • 判断用户是否拒绝过授予权限
    Android提供了 shouldShowRequestPermissionRationale() 方法,让APP在用户拒绝权限后,再次请求权限时,可以在合适的时候可以提示用户使用权限的原因。
/**
*  @return 
*  true--用户上次请求权限时被拒绝
*  false-- 用户已经同意权限,拒绝权限并点击了不再提示
*/
public static boolean shouldShowRequestPermissionRationale(
                        Activity activity,String permission){
}
  • 请求权限
    如果应用不具有某一权限,在调用该功能时必须先调用
    requestPermissions() 方法,来请求相应的权限。
/**
* activity         当前activity
* permission       请求的权限
* requestCode      请求码
*/
public static void requestPermissions(Activity activity,
                                    String[] permissions,
                                    int requestCode) {
}
  • 处理权限的回调
@Override
/**
* requestCode     请求码
* permission      请求的权限
* grantResults    请求结果  
*/
public void onRequestPermissionsResult(int requestCode, 
                                String[] permissions, 
                                int[] grantResults) {
    
}

运行时权限的封装

首先大家可以 点击这里 查看官方文档给的请求权限写法。

为了简化对权限请求的操作,下面介绍一种对权限请求操作封装的方法。代码是看完郭霖的直播课 Android 6.0运行时权限讲解 记录后写下来的。

  1. 首先定义一个 Listener 接口,对权限请求操作回调。
public interface PermissionListener {
    void onGranted();
    void onDenied(List<String> deniedPermission);
}
  1. 因为权限请求操作时需要用到 Activity 作为参数,为了使权限请求可以在util类等其他非 Activity 类中使用,定义一个 ActivityCollector 来作为
    Activity 管理器以便在其他类中获取当前运行栈顶 Activity 。
public class ActivityCollector {

    private static List<Activity> activityList = new ArrayList<>();

    public static void addActivity(Activity activity) {
        activityList.add(activity);
    }

    public static void removeActivity(Activity activity) {
        activityList.remove(activity);
    }
    // 获取栈顶 Activity
    public static Activity getTopActivity() {
        if (activityList.isEmpty())
            return null;
        return activityList.get(activityList.size() - 1);
    }
}
  1. 在 Android 开发中都会用到 BaseActivity 来对公共内容进行整理封装,然后让其他 Activity 都继承 BaseActivity,接下来我们将权限请求操作封装在 BaseActivity 中。为了向下兼容,我们让 BaseActivity 继承 v7 包中的
    AppCompatActivity 。
public class BaseActivity extends AppCompatActivity {

    public static PermissionListener mListener;
    public static final int REQUEST_CODE = 1;

    // onCreate/onDestory 中保存 Activity
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ActivityCollector.addActivity(this);
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        ActivityCollector.removeActivity(this);
    }
    
    public static void requestRuntimePermission(String[] permissions, 
                                      PermissionListener listener) {
        // 获取栈顶 Activity
        Activity topActivity = ActivityCollector.getTopActivity();
        if (topActivity == null)
            return;
        mListener = listener;
        // 需要请求的权限列表
        List<String> requestPermisssionList = new ArrayList<>();
        // 检查权限  是否已被授权
        for (String permission : permissions) {
            if (ActivityCompat.checkSelfPermission(topActivity, permission) 
                      != PackageManager.PERMISSION_GRANTED)
                // 未授权时添加该权限
                requestPermisssionList.add(permission);
        }

        if (requestPermisssionList.isEmpty())
            // 所有权限已经被授权过 回调 Listener onGranted 方法 已授权
            listener.onGranted();
        else
            // 进行请求权限操作
            ActivityCompat.requestPermissions(topActivity,  
                    requestPermisssionList.toArray(new String[requestPermisssionList.size()]), 
                    REQUEST_CODE);

    }

    // 请求权限的回调
    @Override
    public void onRequestPermissionsResult(int requestCode, 
            @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);

        switch (requestCode) {
            case REQUEST_CODE: {
                
                List<String> deniedPermissionList = new ArrayList<>();
                // 检查返回授权结果不为空
                if (grantResults.length > 0) {
                    // 判断授权结果
                    for (int i = 0; i < grantResults.length; i++) {
                        int result = grantResults[i];
                        if (result != PackageManager.PERMISSION_GRANTED)
                            // 保存被用户拒绝的权限
                            deniedPermissionList.add(permissions[i]);
                    }
                    if (deniedPermissionList.isEmpty())
                        // 都被授权  回调 Listener onGranted 方法 已授权
                        mListener.onGranted();
                    else
                        // 有权限被拒绝 回调 Listner onDeynied 方法
                        mListener.onDenied(deniedPermissionList);
                }
                break;
            }
            default:
                break;
        }
    }
}
  1. 封装完成后,使用危险权限相关操作时在 Activity 中只需要重写requestRuntimePermission() 就可以,其他类中则只需要调用BaseActivity.requestRuntimePermission() 方法。
BaseActivity.requestRuntimePermission(
                new String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, 
                new PermissionListener() {

      @Override
      public void onGranted() {
        // 已被授权 可执行相应权限操作
      }

      @Override
      public void onDenied(List<String> deniedPermission) {
          // 权限被拒绝

      }
});
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容