详解Android权限机制
来源:互联网 发布:r9s怎么清除相机数据 编辑:程序博客网 时间:2024/06/07 07:01
前言
权限是Android中一个非常重要的组成部分,许多操作都需要获取到权限才能进行。在Android6.0
之后,权限机制发生了重大变化,加入了运行时权限这一概念。本文就详细讲解一下Android6.0
前后的权限机制。
Android6.0之前的权限机制
在Android6.0
之前,权限机制是很简单的,应用只需要在AndroidManifest
文件中将自己需要的权限声明即可。示例代码如下:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.permissiondemo"> <!-- 请求网络权限 --> <uses-permission android:name="android.permission.INTERNET"></uses-permission> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme"> ...... </application></manifest>
用户只要安装了应用,就代表授予应用其声明的所有权限。如果用户不认可应用声明的权限,就只能选择不安装该应用了。显然,在这种机制中用户处于弱势的地位。
Android6.0之后的权限机制
为了让用户拥有自主授予应用权限的权利,Android6.0
加入了运行时权限这一概念。对于危险权限,应用必须在使用的时候进行申请,用户可以自主选择是否授予这些权限。如果拒绝授予权限,应用也不会崩溃,只是用户无法使用这一部分功能罢了。这样一来,用户的选择权就被大大加强了。
权限类别
Android大致将权限分为两类,即普通权限
和危险权限
。对于普通权限,依旧使用Android6.0
之前的权限机制,只需要在AndroidManifest
中声明即可。而对于危险权限,则必须在应用运行时主动申请,由用户决定是否授予。危险权限列表如下:
- CALENDAR
READ_CALENDAR
WRITE_CALENDAR
- CAMERA
CAMERA
- CONTACTS
READ_CONTACTS
WRITE_CONTACTS
GET_CONTACTS
- LOCATION
ACCESS_FINE_LOCATION
ACCESS_COARSE_LOCATION
- MICROPHONE
RECORD_AUDIO
- PHONE
READ_PHONE_STATE
CALL_PHONE
READ_CALL_LOG
WRITE_CALL_LOG
ADD_VOICEMAIL
USE_SIP
PROCESS_OUTGOING_CALLS
- SENSORS
BODY_SENSORS
- SMS
SEND_SMS
RECEIVE_SMS
READ_SMS
RECEIVE_MMS
RECEIVE_WAP_PUSH
- STORAGE
READ_EXTERNAL_STORAGE
WRITE_EXTERNAL_STORAGE
可以看到,危险权限一共有9组24种权限。除此之外,其他权限都是普通权限。需要注意的是,对于每组权限,只要用户授予了其中任何一种权限,整个权限组都会被授予给应用。
运行时权限
对于危险权限,除了在AndroidManifest
声明,还需要在代码中进行权限检查、权限申请操作。在涉及危险权限的地方,我们首先需要使用checkSelfPermisson
方法进行权限检查,其原型如下:
public static int checkSelfPermission(Context context,String permission)
这个方法有两个参数,第一个参数是Context
对象,第二个参数则是权限名称。最后,该方法会返回一个int
类型的数据,可能是PackageManager.PERMISSION_GRANTED
或PackageManager.PERMISSION_DENIED
,前者代表已授权、后者代表未授权。
当通过checkSelfPermisson
方法得知权限未被授予,则要使用requestPermissions
方法请求权限,其原型如下:
public static void requestPermissions(final Activity activity, final String[] permissions,final int requestCode)
这个方法有三个参数,第一个参数是Activity
对象。第二个参数是字符串数组,代表我们想要申请的权限,由此也可以知道这个方法能够一次申请多个权限。第三个参数是请求码,用于在回调方法中区分申请权限的请求。这是一个异步方法,因此没有返回值。这个方法会弹出一个权限申请的对话框,在用户做出选择后,onRequestPermissionsResult
方法会被回调,该方法原型如下:
public void onRequestPermissionsResult(int requestCode,String[] permissions,int[] grantResults)
这个方法有三个参数,第一个参数是请求码,第二个参数是请求的权限字符串数组,第三个参数是权限请求的授权结果。通过对第三个参数进行判断,我们就可以知道申请的权限是否已被用户授予。
除了以上三个方法,还有一个优化应用使用体验的方法,即shouldShowRequestPermissionRationale
。当通过requestPermissions
方法申请权限,且用户选择了拒绝授予后,这个方法会返回true
。这时,我们就应该对请求的权限进行详细说明,这有助于用户授予权限。该方法原型如下:
public static boolean shouldShowRequestPermissionRationale(Activity activity,String permission)
这个方法有两个参数,第一个参数是Activity
对象,第二个参数是字符串数组,代表我们想要申请的权限。
一个简单的例子
上面简单讲解了运行时权限的使用步骤,这里提供一个简单的例子,通过在运行时请求权限实现拨打电话的功能。
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); findViewById(R.id.call_phone).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if(ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.CALL_PHONE)!= PackageManager.PERMISSION_GRANTED){ //如果用户已经拒绝过一次权限申请,该方法返回true if(ActivityCompat.shouldShowRequestPermissionRationale( MainActivity.this,Manifest.permission.CALL_PHONE)){ //提示用户这一权限的重要性 Toast.makeText(MainActivity.this,"拨号功能是本应用的核心功能,如果不授予权限,程序是无法正常工作的~", Toast.LENGTH_SHORT).show(); } //请求权限 ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.CALL_PHONE},1); }else{//权限已被授予 callPhone(); } } }); } //拨打电话 private void callPhone(){ try {//可能出现异常,需要使用try-catch语句块 Intent intent=new Intent(Intent.ACTION_CALL); intent.setData(Uri.parse("tel:10086")); startActivity(intent); } catch (SecurityException e) { e.printStackTrace(); } } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { switch (requestCode){ case 1: if(grantResults.length>0&&grantResults[0]== PackageManager.PERMISSION_GRANTED){ //权限已授予 callPhone(); }else{ //权限未授予 Toast.makeText(this,"在未授予权限的情况下,程序无法正常工作", Toast.LENGTH_SHORT).show(); } break; default: break; } }}
可以看到,为了保持兼容性,我们使用了ContextCompat
以及ActivityCompat
,也建议这样使用。除此之外,我们还需要在AndroidManifest
文件中声明需要用到的权限。如下:
<!-- 请求拨号权限 --><uses-permission android:name="android.permission.CALL_PHONE"></uses-permission>
需要注意的是,在部分国产手机中,即使采用以上方式,可能也无法触发未授予权限那部分的逻辑。因此,建议使用开源库的方式进行运行时权限申请。
demo下载地址:PermissionDemo
开源库
- AndPermission
- easypermissions
- PermissionsDispatcher
- PermissionHelper
自定义权限
除了使用系统提供的权限,我们还可以自定义权限。思考这样一个场景,我们的应用有一个Activity
可以被其他应用启动,但是需要其他应用具备相应的权限。这时,我们就可以在自己的应用中定义一个权限,其他应用只有声明这一权限,才可以成功启动我们的Activity
。
首先,在被启动应用的AndroidManifest
文件中自定义权限,示例代码如下:
<!-- 自定义权限 --><permission android:name="com.example.selfpermissondemo.permisson.OPEN_ACTIVITY" android:protectionLevel="normal" android:label="自定义权限"/>
name
是自定义权限的名称,protectionLevel
是权限的安全级别,可以使用normal、dangerous、signature,、signatureOrSystem
中的一种,label
则是权限的简短描述。
对于需要权限才能启动的Activity
,我们需要在<activity>
标签中声明permission
属性,示例代码如下:
<activity android:name=".PermissionActivity" android:permission="com.example.selfpermissondemo.permisson.OPEN_ACTIVITY"> <intent-filter> <action android:name="com.example.selfpermissondemo.permission.activity"></action> <category android:name="android.intent.category.DEFAULT"></category> </intent-filter></activity>
在其他应用中,如果想启动该Activity
,就必须在AndroidManifest
文件中声明该自定义权限,否则将会抛出SecurityException
异常。
<!-- 声明自定义权限 --><uses-permission android:name="com.example.selfpermissondemo.permisson.OPEN_ACTIVITY"></uses-permission>
- 详解Android权限机制
- Android 6.0 权限机制详解
- Android之Android 6.0权限机制及开发流程详解
- Android 6.0权限机制及开发流程详解
- Android 6.0的新特性及权限机制的详解
- Android权限机制
- Android权限机制
- Android权限机制
- Android权限机制
- Android的权限机制
- Android的权限机制
- Android的权限机制
- android 权限机制分析
- android 权限permission机制
- Android安全机制--权限
- Android存储权限机制
- Android的权限机制
- Android中的权限机制
- 新闻客户端
- 产品经理必须知道的三种经典逻辑思考方式
- Flask在windows下安装及在eclipse下开发的环境配置
- Android聊天客户端Demo,开源了.基本功能都有,数据库,服务器都Ok
- 点9图
- 详解Android权限机制
- LeetCode 9. Palindrome Number
- ACM:C: 稳定排序
- Linux系统调用实现文件复制
- 用 Python 实现每秒处理 120 万次 HTTP 请求
- 解决问题能力的关键是什么
- 只能实例化一次的类 (Singleton)
- 统计2 泊松过程 大数定理 正态分布
- qwb与矩阵 简单dp