Android权限(Permissions)处理

       Android里面的权限是一种安全机制。Android权限主要用于限制应用程序内部某些具有限制性的功能使用,以及应用程序之间的组件访问。但是呢Android6.0之后,Google对权限做了一些优化,将一些权限的申请放在了应用运行的时候去申请(动态获取权限),所以Android6.0之后将权限向分为两类:Normal Permissions(正常权限,一般涉及用户隐私的风险比较小,系统会自动向应用授予该权限)、Dangerous Permission(敏感权限,一般是涉及到用户隐私的,用户必须明确向应用授予该权限)。

不管是Normal Permissions还是Dangerous permissions权限,如果我们在程序中需要使用,那么在AndroidManifest.xml文件里面都是需要注册的。

一,Normal Permissions

       普通权限涵盖应用程序需要访问应用程序沙箱外部数据或资源的区域,但是对于用户的隐私或其他应用程序的操作几乎没有风险。

       如果应用程序在AndroidManifest.xml文件中注册了需要的Normal Permissions,系统会在安装时自动授予应用该权限。程序使用过程中系统不会提示用户授予正常权限,并且用户无法撤消这些权限(永久授权)。

Normal Permissions权限包含:

权限名 解释
ACCESS_LOCATION_EXTRA_COMMANDS 允许程序访问额外的定位提供者指令
ACCESS_NETWORK_STATE 允许程序获取网络信息状态,如当前的网络连接是否有效
ACCESS_NOTIFICATION_POLICY 通知APP通知显示在状态栏
ACCESS_WIFI_STATE 允许程序获取当前WiFi接入的状态以及WLAN热点的信息
BLUETOOTH 允许程序连接配对过的蓝牙设备
BLUETOOTH_ADMIN 允许程序进行发现和配对新的蓝牙设备
BROADCAST_STICKY 允许程序收到广播后快速收到下一个广播
CHANGE_NETWORK_STATE 允许程序改变网络状态,如是否联网
CHANGE_WIFI_MULTICAST_STATE 允许程序改变WiFi多播状态
CHANGE_WIFI_STATE 允许程序改变WiFi状态
DISABLE_KEYGUARD 允许程序禁用键盘锁
EXPAND_STATUS_BAR 允许程序扩展或收缩状态栏
GET_PACKAGE_SIZE 允许程序获取应用的文件大小
INSTALL_SHORTCUT 创建快捷方式
INTERNET 允许程序访问网络连接,可能产生GPRS流量
KILL_BACKGROUND_PROCESSES 允许程序调用killBackgroundProcesses(String).方法结束后台进程
MANAGE_OWN_CALLS 允许通过自我管理的ConnectionService API管理自己的调用的调用应用程序
MODIFY_AUDIO_SETTINGS 允许程序修改声音设置信息
NFC 允许程序执行NFC近距离通讯操作,用于移动支持
READ_SYNC_SETTINGS 允许程序读取同步设置,读取Google在线同步设置
READ_SYNC_STATS 允许程序读取同步状态,获得Google在线同步状态
RECEIVE_BOOT_COMPLETED 允许程序开机自动运行
REORDER_TASKS 允许程序重新排序系统Z轴运行中的任务
REQUEST_COMPANION_RUN_IN_BACKGROUND 允许伴随应用在后台运行
REQUEST_COMPANION_USE_DATA_IN_BACKGROUND 允许伴随应用在后台使用数据
REQUEST_DELETE_PACKAGES 允许应用程序请求删除软件包
REQUEST_IGNORE_BATTERY_OPTIMIZATIONS 应用程序必须拥有该权限才能使用ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS
SET_ALARM 允许程序设置闹铃提醒
SET_WALLPAPER 允许程序设置桌面壁纸
SET_WALLPAPER_HINTS 允许程序设置壁纸建议
TRANSMIT_IR 允许使用设备的红外发射器
USE_FINGERPRINT 允许应用使用指纹硬件
VIBRATE 允许程序振动
WAKE_LOCK 允许程序在手机屏幕关闭后后台进程仍然运行
WRITE_SYNC_SETTINGS 写入Google在线同步设置

如果程序需要用到Normal Permissions权限,那么这些用到的权限都必须在AndroidManifest.xml中注册

二、Dangerous permissions

       Dangerous permissions涵盖应用程序需要涉及用户私人信息的数据或资源的区域,或者可能会影响用户的存储数据或其他应用程序的操作。例如,阅读用户联系人的权限是一个危险的权限。如果一个应用程序声明它需要一个危险的权限,用户必须明确授予该应用程序的权限。在用户批准权限之前,您的应用无法提供取决于该权限的功能。

       当我们需要使用Dangerous permissions的时候要特别注意,我们分两种情况来说明:

  • targetSdkVersion >= 23 并且 Android手机 >= 6.0:这个时候Dangerous permissions在使用的时候,就需要我们去动态的申请权限了,比如:当我们需要打开相机拍摄照片的时候需要我们通过代码的方式在需要的地方去申请权限(关于代码的实现方式我们在下文中会提到)。

  • targetSdkVersion < 23 或者 Android手机 < 6.0:和Normal Permissions权限使用一样,系统会在应用程序安装的时候自动授予应用该权限。

Dangerous Permissions权限包含(特别注意下那些权限是属于同权限一组的):

权限名 权限组 解释
READ_CALENDAR CALENDAR(日历组) 允许程序读取用户的日程信息
WRITE_CALENDAR CALENDAR(日历组) 允许程序写入日程,但不可读取
CAMERA CAMERA(相机拍照组) 允许程序访问摄像头进行拍照
READ_CONTACTS CONTACTS(联系人组) 允许程序访问联系人通讯录信息
WRITE_CONTACTS CONTACTS(联系人组) 允许程序写入联系人,但不可读取
GET_ACCOUNTS CONTACTS(联系人组) 允许程序访问账户Gmail列表
ACCESS_FINE_LOCATION LOCATION(定位组) 允许程序通过GPS芯片接收卫星的定位信息
ACCESS_COARSE_LOCATION LOCATION(定位组) 允许程序通过WiFi或移动基站的方式获取用户错略的经纬度信息
RECORD_AUDIO MICROPHONE(麦克风组) 允许程序录制声音通过手机或耳机的麦克
READ_PHONE_STATE PHONE(电话组) 允许程序访问电话状态
READ_PHONE_NUMBERS PHONE(电话组) 允许程序读取设备的电话号码
CALL_PHONE PHONE(电话组) 允许程序从非系统拨号器里拨打电话
ANSWER_PHONE_CALLS PHONE(电话组) 允许程序接听来电
READ_CALL_LOG PHONE(电话组) 允许程序读取通话记录
WRITE_CALL_LOG PHONE(电话组) 允许程序写入(但是不能读)用户的联系人数据
ADD_VOICEMAIL PHONE(电话组) 允许程序添加语音邮件系统
USE_SIP PHONE(电话组) 允许程序使用SIP视频服务
PROCESS_OUTGOING_CALLS PHONE(电话组) 允许程序监视,修改或放弃播出电话
BODY_SENSORS SENSORS(传感器组) 允许应用程序访问用户用来测量身体内部情况的传感器数据,例如心率
SEND_SMS SMS(手机短信服务组) 允许程序发送短信
RECEIVE_SMS SMS(手机短信服务组) 允许程序接收短信
READ_SMS SMS(手机短信服务组) 允许程序读取短信内容
RECEIVE_WAP_PUSH SMS(手机短信服务组) 允许程序接收WAP PUSH信息
RECEIVE_MMS SMS(手机短信服务组) 允许程序接收彩信
READ_EXTERNAL_STORAGE STORAGE(存储组) 允许程序可以读取设备外部存储空间
WRITE_EXTERNAL_STORAGE STORAGE(存储组) 允许程序写入外部存储,如SD卡上写文件

       在介绍Dangerous permissions的时候,提到了权限组,这个权限组有啥用呢。同一组的任何一个权限被授权了,该组的其他权限也自动被授权了。举个例子,例如我们动态授予了READ_CALENDAR权限,这个时候因为READ_CALENDAR是属于CALENDAR组的,CALENDAR组里面同时还包含了WRITE_CALENDAR权限。所以WRITE_CALENDAR权限也被自动授权了。

三、动态申请权限

       上文中我们也提到了,当我们targetSdkVersion >= 23 并且 Android手机 >= 6.0(两个条件要同时成立)的时候,如果我们想使用Dangerous permissions里面的权限的话,是需要通过代码去动态申请权限的。

       动态申请权限相关的一些函数:

    /**
     * 检查指定的权限是否授权(Context对象调用)
     */
    (Context).checkSelfPermission(@NonNull String permission);

    /**
     * 在没有授权的情况下,有些时候可能需要提示给用户为什么需要改权限,就通过该函数来实现。
     * 关于shouldShowRequestPermissionRationale的返回值问题,我们分三种情况
     * 1. 第一次打开App时 -> false
     * 2. 上次弹出权限点击了禁止(但没有勾选“下次不在询问”) -> true
     * 3. 上次选择禁止并勾选:下次不在询问 -> false
     */
    (Activity或者Fragment).shouldShowRequestPermissionRationale(@NonNull String permission);

    /**
     * 申请指定的权限(Activity或者Fragment对象调用)
     * @param permissions 权限列表,可以同时申请多个权限
     * @param requestCode 该次权限申请对应的requestCode。和 onRequestPermissionsResult()回调函数里面的requestCode对应
     */
    (Activity或者Fragment).requestPermissions(@NonNull String[] permissions, int requestCode);


    /**
     * 处理请求权限的响应,当用户对请求权限的dialog做出响应之后,系统会回调该函数(Activity或者Fragment中重写)
     * @param requestCode 申请权限对应的requestCode
     * @param permissions 权限列表
     * @param grantResults 权限列表对应的返回值,判断permissions里面的每个权限是否申请成功
     */
    onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
                               @NonNull int[] grantResults);

       动态申请权限分为以下几个步骤:

  1. 明确我们页面需要哪些权限。
  2. 检查需要的权限是否授权 -> checkSelfPermission()。
  3. 如果没有授权,则判断是否需要向用户解释为何申请权限。-> shouldShowRequestPermissionRationale() 返回true则可能需要弹出框解释下。(大部分情况下都会直接跳过这一步,不做这一步的处理)
  4. 申请权限。-> requestPermissions()。
  5. 处理申请的结果信息。-> 回调函数onRequestPermissionsResult()。

       关于动态申请权限网上的开源库也很多,这里我们就简单的介绍其中一种PermissionGen。对应GitHub项目地址:https://github.com/lovedise/PermissionGen

       PermissionGen的使用也是非常简单的。我们先定义一个BaseAppActivity。在BaseAppActivity的onRequestPermissionsResult()方法里面调用PermissionGen.onRequestPermissionsResult(this, requestCode, permissions, grantResults);

ublic abstract class BaseAppActivity extends AppCompatActivity {

    protected Context  mContext;
    protected Activity mActivity;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mContext = this;
        mActivity = this;
    }

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

       PermissionGen申请权限,

Activity中

PermissionGen.with(MainActivity.this)
    .addRequestCode(100)
    .permissions(
        Manifest.permission.READ_CONTACTS,
        Manifest.permission.RECEIVE_SMS,
        Manifest.permission.WRITE_CONTACTS)
    .request();

Fragment中

PermissionGen.needPermission(ContactFragment.this, 100, 
    new String[] {
        Manifest.permission.READ_CONTACTS, 
        Manifest.permission.RECEIVE_SMS,
        Manifest.permission.WRITE_CONTACTS
    }
);

       PermissionGen结果确认。

成功结果

@PermissionSuccess(requestCode = 100)
public void doSomething(){
    Toast.makeText(this, "Contact permission is granted", Toast.LENGTH_SHORT).show();
}

失败结果

@PermissionFail(requestCode = 100)
public void doFailSomething(){
    Toast.makeText(this, "Contact permission is not granted", t.LENGTH_SHORT).show();
}

       我们以一个具体的实例来做一个简单的说明,比如我们在Activity中申请摄像头权限。注意哦,我这里是继承的BaseAppActivity,BaseAppActivity中重写了onRequestPermissionsResult(),里面调用了PermissionGen.onRequestPermissionsResult(this, requestCode, permissions, grantResults);。

public class PermissionGenActivity extends BaseAppActivity {

    public static void startUp(Context context) {
        context.startActivity(new Intent(context, PermissionGenActivity.class));
    }

    private static final int CAMERA_PERMISSION = 100;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_permission_gen);
        //申请Manifest.permission.CAMERA权限
        PermissionGen.with(mActivity).addRequestCode(CAMERA_PERMISSION).permissions(Manifest.permission.CAMERA).request();
    }

    @PermissionSuccess(requestCode = CAMERA_PERMISSION)
    public void requestPhotoSuccess() {
        //申请权限成功
    }

    @PermissionFail(requestCode = CAMERA_PERMISSION)
    public void requestPhotoFail() {
        onBackPressed();
    }
}

       关于Android权限(Permissions)处理,有以下几个总结点:

  • 不管是Normal Permissions还是Dangerous Permission对应的权限都必须在AndroidManifest.xml文件里面注册。

  • 只有当targetSdkVersion >= 23 并且 Android手机 >= 6.0两个条件同时满足时,Dangerous Permission对应的权限才需要动态申请。否则还是和Normal Permissions使用一样会在程序安装的时候就获取到权限。

  • Dangerous Permission权限里面某个组里面一个权限被授权了,那么这个组里面其他的权限也跟着授权了。


       文章中涉及到的DEMO下载地址

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

推荐阅读更多精彩内容