Android运行时权限

android 6.0运行时权限

Android 中的权限在6.0(API23)之前一直是在安装应用的时候一次性授予的,要不不安装此软件,要么接受此软件申请的所有权限。6.0之后,Google更改了权限的授予方式,用户在安装时可以选择授予或者不授予应用申请的权限,到需要使用此权限的时候再去向用户申请。

权限的两种类型

  • 普通权限

    普通权限指的是那些不涉及用户隐私的一些权限,比如连接网络,读取网络状态,比如去系统相册选择图片,再比如。
    如果应用声明其需要正常权限,系统会自动向应用授予该权限,而不需要应用去向用户实时申请,同时,用户也无法取消授予这些权限。
    下面的这些权限是被称为普通权限(PROTECTION_NORMAL)的
    - ACCESS_LOCATION_EXTRA_COMMANDS
    - ACCESS_NETWORK_STATE
    - ACCESS_NOTIFICATION_POLICY
    - ACCESS_WIFI_STATE
    - BLUETOOTH
    - BLUETOOTH_ADMIN
    - BROADCAST_STICKY
    - CHANGE_NETWORK_STATE
    - CHANGE_WIFI_MULTICAST_STATE
    - CHANGE_WIFI_STATE
    - DISABLE_KEYGUARD
    - EXPAND_STATUS_BAR
    - GET_PACKAGE_SIZE
    - INSTALL_SHORTCUT
    - INTERNET
    - KILL_BACKGROUND_PROCESSES
    - MODIFY_AUDIO_SETTINGS
    - NFC
    - READ_SYNC_SETTINGS
    - READ_SYNC_STATS
    - RECEIVE_BOOT_COMPLETED
    - REORDER_TASKS
    - REQUEST_IGNORE_BATTERY_OPTIMIZATIONS
    - REQUEST_INSTALL_PACKAGES
    - SET_ALARM
    - SET_TIME_ZONE
    - SET_WALLPAPER
    - SET_WALLPAPER_HINTS
    - TRANSMIT_IR
    - UNINSTALL_SHORTCUT
    - USE_FINGERPRINT
    - VIBRATE
    - WAKE_LOCK
    - WRITE_SYNC_SETTINGS

  • 危险权限

    危险权限涵盖应用需要涉及用户隐私信息的数据或资源,或者可能对用户存储的数据或其他应用的操作产生影响的区域。例如,能够读取用户的联系人属于危险权限,拨打电话属于危险权限,获取定位信息属于危险权限...如果应用声明其需要危险权限,则用户必须明确向应用授予该权限。这就需要动态向用户申请,也就是我们说的运行时权限申请。
    这里还有一件事情要说明,就是权限组的概念。
    ####权限组
    如果一个应用既需要写文件也需要读取文件,
    既需要读取联系人也需要添加联系人,
    既需要粗略定位也需要精确定位。
    想象一下,如果不停地向用户去申请这些权限,用户是否会很反感。
    所以,为了提供更好的用户体验,谷歌爸爸创造了一个权限组的概念!
    权限组(这里只说危险权限,其实任何权限都可以属于一个权限组):
    1. 任何一个危险权限都应该属于一个权限组,
    2. 一旦应用申请的某个危险权限被用户授予,
    3. 那么该权限组内的其他危险权限也被授予。
    事实上,在你申请打电话权限的时候,系统弹框也是在询问用户是否授予该应用电话权限。电话权限的权限组包括打电话权限。
    ####9大权限组
    - CALENDAR
    - CAMERA
    - CONTACTS
    - LOCATION
    - MICROPHONE
    - PHONE
    - SENSORS
    - SMS
    - STORAGE

[图片上传失败...(image-3b9dab-1522052782236)]

接下来我们来说说怎么申请运行时权限

1. 先说说几个系统api

  1. checkSelfPermission

    检查是否授予了某一项权限,
    如果权限已经授予,返回0,否则返回-1
    Determine whether you have been granted a particular permission.

  2. shouldShowRequestPermissionRationale

    当你没有某个权限的时候,你最好显示一个UI组件,告诉用户这个应用为什么需要这个权限,告诉用户如果授予该权限会有带来什么好处,
    如果您的权限请求已经被拒绝过一次,返回true,否则返回false
    Gets whether you should show UI with rationale for requesting a permission. You should do this only if you do not have the permission and the context in which the permission is requested does not clearly communicate to the user what would be the benefit from granting this permission.

  3. requestPermissions

    当检查到用户没有授予某项你需要的危险权限的时候,向用户申请该权限
    Requests permissions to be granted to this application. These permissions must be requested in your manifest

  4. onRequestPermissionsResult

    这是 requestPermissions 方法的回调,每次调用requestPermissions都会有一次回调
    Callback for the result from requesting permissions. This method is invoked for every call on requestPermissions(String[], int).

2. 再说说怎么用

这里面有一个坑很多文章都没有讲明白,我也是在项目中遇到了才把这个问题给搞清楚了的。大多数文章的示例都是以Activity为背景讲的,像我这种刚接触运行时权限的菜鸟而言,当然就是copy下别人的用法喽,一不小心就掉进了坑里。
这个坑就是onRequestPermissionsResult方法根本没被调用。帅哥们看这里!

敲黑板划重点:

上面我们说的四个系统api中的后面三个方法其实是有两套的,Activity使用一套, Fragment要使用另外一套!在Activity中使用ActivityCompat.requestPermissions,在Fragment中使用requestPermissions

3. Talk is simple , show u the code!

我们来看示例代码:

看看Fragment怎么用

1.检查权限

if (ContextCompat.checkSelfPermission(mContext, Manifest.permission.CALL_PHONE)
         == PackageManager.PERMISSION_GRANTED) {
    //已经授权,
}else{
    //没有权限
}

2.显示原因

if(shouldShowRequestPermissionRationale(Manifest.permission.CALL_PHONE){
    //如果需要显示请求权限的原因 在这里写        
} else {
    //请求权限                  
}

3.请求权限

requestPermissions(new String[]{Manifest.permission.CALL_PHONE},1);

4.处理回调

@Override
public void onRequestPermissionsResult(int requestCode,String permissions[], int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}

一个完整的Fragment:

package com.example.francis.demo.simpletest;

import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.content.ContextCompat;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import com.example.francis.demo.R;

public class RuntimePermissionFragment extends Fragment {
    private final int REQUEST_CODE_CALL_PHONE = 1;

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        super.onCreateView(inflater, container, savedInstanceState);
        View view = inflater.inflate(R.layout.fragment_runtime_permission, container, false);
        view.findViewById(R.id.call_button).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.CALL_PHONE) == PackageManager.PERMISSION_GRANTED) {
                    //打电话逻辑在哪里写
                } else {
                    if (shouldShowRequestPermissionRationale(Manifest.permission.CALL_PHONE)) {
                        //展示你给用户的解释,这不是必须的,如果你觉得不需要或者你不想解释,那就不要解释了 
                    } else {
                        requestPermissions(new String[]{Manifest.permission.CALL_PHONE}, REQUEST_CODE_CALL_PHONE);
                    }
                }
            }
        });
        return view;
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == REQUEST_CODE_CALL_PHONE) {
            if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                //打电话权限被授予
                //打电话逻辑写这里
            } else {
                //用户拒绝了你的打电话权限
            }
        }
    }
}

看看Activity怎么用

1.检查权限

if (ContextCompat.checkSelfPermission(mContext, Manifest.permission.CALL_PHONE)
         == PackageManager.PERMISSION_GRANTED) {
    //已经授权,
}else{
    //没有权限
}

2.显示原因

if(ActivityCompat.shouldShowRequestPermissionRationale(Manifest.permission.CALL_PHONE){
    //如果需要显示请求权限的原因 在这里写        
} else {
    //请求权限                  
}

3.请求权限

ActivityCompat.requestPermissions(new String[]{Manifest.permission.CALL_PHONE},1);

4.处理回调

@Override
public void onRequestPermissionsResult(int requestCode,String permissions[], int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}

一个简单Activity示例

package com.example.francis.demo.simpletest;

import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
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.view.View;

import com.example.francis.demo.R;

public class RuntimePermissionActivity extends AppCompatActivity {
    private final int REQUEST_CODE_CALL_PHONE = 1;

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

        findViewById(R.id.call_button).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (ContextCompat.checkSelfPermission(RuntimePermissionActivity.this, Manifest.permission.CALL_PHONE) == PackageManager.PERMISSION_GRANTED) {
                    //打电话逻辑在哪里写
                } else {
                    if (ActivityCompat.shouldShowRequestPermissionRationale(RuntimePermissionActivity.this,Manifest.permission.CALL_PHONE)) {
                        //展示你给用户的解释,这不是必须的,如果你觉得不需要或者你不想解释,那就不要解释了
                    } else {
                        //请求权限
                        ActivityCompat.requestPermissions(RuntimePermissionActivity.this,new String[]{Manifest.permission.CALL_PHONE}, REQUEST_CODE_CALL_PHONE);
                    }
                }
            }
        });
    }
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == REQUEST_CODE_CALL_PHONE) {
            if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                //打电话权限被授予
                //打电话逻辑写这里
            } else {
                //用户拒绝了你的打电话权限
            }
        }
    }
}

小结

到了这里,运行时权限的申请流程和细节大家应该已经非常清楚了,接下来你需要做的就是自己写写看,看看源码介绍。虽然也有很多别人写好的很多库可以直接来调用,但是万变不离其宗,知道本质再去使用第三方库吧。
对了,别忘了在清单文件中声明这些危险权限哦😁...

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

推荐阅读更多精彩内容