Android 6.0的权限问题

随着Android 6.0发布以及普及,我们开发者所要应对的主要就是新版本SDK带来的一些变化,首先关注的就是权限机制的变化。ok,本篇文章目的之一就是对运行时权限处理的一个介绍,以及对目前权限相关的库的一些了解。

对于6.0以下的权限及在安装的时候,根据权限声明产生一个权限列表,用户只有在同意之后才能完成app的安装,造成了我们想要使用某个app,就要默默忍受其一些不必要的权限(比如是个app都要访问通讯录、短信等)。而在6.0以后,我们可以直接安装,当app需要我们授予不恰当的权限的时候,我们可以予以拒绝(比如:单机的象棋对战,请求访问任何权限,我都是不同意的)。当然你也可以在设置界面对每个app的权限进行查看,以及对单个权限进行授权或者解除授权。

新的权限机制更好的保护了用户的隐私,Google将权限分为两类,一类是Normal Permissions,这类权限一般不涉及用户隐私,是不需要用户进行授权的,比如手机震动、访问网络等;另一类是Dangerous Permission,一般是涉及到用户隐私的,需要用户进行授权,比如读取sdcard、访问通讯录等。

那怎么查看这些权限呢?
可以通过adb shell pm list permissions -d -g进行查看。

Normal Permissions如下:
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_INSTALL_PACKAGES
SET_ALARM
SET_TIME_ZONE
SET_WALLPAPER
SET_WALLPAPER_HINTS
TRANSMIT_IR
UNINSTALL_SHORTCUT
USE_FINGERPRINT
VIBRATE
WAKE_LOCK
WRITE_SYNC_SETTINGS

Dangerous Permissions如下:

group:android.permission-group.CONTACTS
permission:android.permission.WRITE_CONTACTS
permission:android.permission.GET_ACCOUNTS
permission:android.permission.READ_CONTACTS

group:android.permission-group.PHONE
permission:android.permission.READ_CALL_LOG
permission:android.permission.READ_PHONE_STATE
permission:android.permission.CALL_PHONE
permission:android.permission.WRITE_CALL_LOG
permission:android.permission.USE_SIP
permission:android.permission.PROCESS_OUTGOING_CALLS
permission:com.android.voicemail.permission.ADD_VOICEMAIL

group:android.permission-group.CALENDAR
permission:android.permission.READ_CALENDAR
permission:android.permission.WRITE_CALENDAR

group:android.permission-group.CAMERA
permission:android.permission.CAMERA

group:android.permission-group.SENSORS
permission:android.permission.BODY_SENSORS

group:android.permission-group.LOCATION
permission:android.permission.ACCESS_FINE_LOCATION
permission:android.permission.ACCESS_COARSE_LOCATION

group:android.permission-group.STORAGE
permission:android.permission.READ_EXTERNAL_STORAGE
permission:android.permission.WRITE_EXTERNAL_STORAGE

group:android.permission-group.MICROPHONE
permission:android.permission.RECORD_AUDIO

group:android.permission-group.SMS
permission:android.permission.READ_SMS
permission:android.permission.RECEIVE_WAP_PUSH
permission:android.permission.RECEIVE_MMS
permission:android.permission.RECEIVE_SMS
permission:android.permission.SEND_SMS
permission:android.permission.READ_CELL_BROADCASTS
看到上面的dangerous permissions,会发现一个问题,好像危险权限都是一组一组的,恩,没错,的确是这样的。
那么有个问题:分组对我们的权限机制有什么影响吗?

的确是有影响的,如果app运行在Android 6.x的机器上,对于授权机制是这样的。如果你申请某个危险的权限,假设你的app早已被用户授权了同一组的某个危险权限,那么系统会立即授权,而不需要用户去点击授权。比如你的app对READ_CONTACTS已经授权了,当你的app申请WRITE_CONTACTS时,系统会直接授权通过。此外,对于申请时弹出的dialog上面的文本说明也是对整个权限组的说明,而不是单个权限(ps:这个dialog是不能进行定制的)。

不过需要注意的是,不要对权限组过多的依赖,尽可能对每个危险权限都进行正常流程的申请,因为在后期的版本中这个权限组可能会产生变化。

看了上面这么多多的描述,下面我们来看看权限申请的步骤:

1.在AndroidManifest文件中添加需要的权限。
这个步骤和我们之前的开发并没有什么变化,试图去申请一个没有声明的权限可能会导致程序崩溃。
2.检查权限

if (ContextCompat.checkSelfPermission(context,
                Manifest.permission.READ_CONTACTS)
        != PackageManager.PERMISSION_GRANTED) {
  //申请权限
}else{
    //做相关操作
}

这里涉及到一个API,ContextCompat.checkSelfPermission,主要用于检测某个权限是否已经被授予,方法返回值为PackageManager.PERMISSION_DENIED或者PackageManager.PERMISSION_GRANTED。当返回DENIED就需要进行申请授权了。

3.申请权限

ActivityCompat.requestPermissions(activity,
                new String[]{Manifest.permission.READ_CONTACTS},
                MY_PERMISSIONS_REQUEST_READ_CONTACTS);

该方法是异步的,第一个参数是Activity;第二个参数是需要申请的权限的字符串数组;第三个参数为requestCode,主要用于回调的时候检测。可以从第二个参数看出,是支持一次性申请多个权限的,系统会通过对话框逐一询问用户是否授权。

4.处理权限申请回调

@Override
public void onRequestPermissionsResult(int requestCode,
        String permissions[], int[] grantResults) {
        switch (requestCode) {
        case MY_PERMISSIONS_REQUEST_READ_CONTACTS: {
            // If request is cancelled, the result arrays are empty.
            if (grantResults.length > 0
                && grantResults[0] == PackageManager.PERMISSION_GRANTED) {

                // permission was granted, yay! Do the
                // contacts-related task you need to do.

            } else {

                // permission denied, boo! Disable the
                // functionality that depends on this permission.
            }
            return;
        }
    }
}

ok,对于权限的申请结果,首先验证requestCode定位到你的申请,然后验证grantResults对应于申请的结果,如果你同时申请两个权限,那么grantResults的length就为2,分别记录你两个权限的申请结果。

那么将上述几个步骤结合到一起就是:
// Here, thisActivity is the current activity

if (ContextCompat.checkSelfPermission(thisActivity,
                Manifest.permission.READ_CONTACTS)
        != PackageManager.PERMISSION_GRANTED) {

    // Should we show an explanation?
    if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,
            Manifest.permission.READ_CONTACTS)) {

        // Show an expanation to the user *asynchronously* -- don't block
        // this thread waiting for the user's response! After the user
        // sees the explanation, try again to request the permission.

    } else {

        // No explanation needed, we can request the permission.

        ActivityCompat.requestPermissions(thisActivity,
                new String[]{Manifest.permission.READ_CONTACTS},
                MY_PERMISSIONS_REQUEST_READ_CONTACTS);

        // MY_PERMISSIONS_REQUEST_READ_CONTACTS is an
        // app-defined int constant. The callback method gets the
        // result of the request.
    }
}

最后,我们来看看一个小例子:(testCall函数,为xml中定义的点击事件函数)

package com.zhy.android160217;

import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity
{

    private static final int MY_PERMISSIONS_REQUEST_CALL_PHONE = 1;

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

    public void testCall(View view) {
        if (ContextCompat.checkSelfPermission(this,
                Manifest.permission.CALL_PHONE)
                != PackageManager.PERMISSION_GRANTED){

            ActivityCompat.requestPermissions(this,
                    new String[]{Manifest.permission.CALL_PHONE},
                    MY_PERMISSIONS_REQUEST_CALL_PHONE);
        } else{
            callPhone();
        }
    }

    public void callPhone(){
        Intent intent = new Intent(Intent.ACTION_CALL);
        Uri data = Uri.parse("tel:" + "10086");
        intent.setData(data);
        startActivity(intent);
    }

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

        if (requestCode == MY_PERMISSIONS_REQUEST_CALL_PHONE)
        {
            if (grantResults[0] == PackageManager.PERMISSION_GRANTED)
            {
                callPhone();
            } else
            {
                // Permission Denied
                Toast.makeText(MainActivity.this, "Permission Denied", Toast.LENGTH_SHORT).show();
            }
            return;
        }
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值