运行时权限申请开发精要

主要参考Hongyang大神的这篇文章
//Android 6.0 运行时权限处理完全解析
http://blog.csdn.net/lmj623565791/article/details/50709663

概念

Android 6.0 (API level 23) 引入了运行时权限这个机制, 在之前的Android版本中, 开发者在AndroidManifest.xml中声明所有的权限, 用户在安装apk时进行确认是否同意, 只有在同意后才能成功安装应用, 这就造成了一些权限滥用的问题, 比如很多app会声明去访问手机联系人, 位置定位等用户敏感信息的权限. 而实际上, 这些app本不应该去具有这些权限.
针对这种情况, Android 6.0 把权限划分为普通权限和危险权限, 对于普通权限, 比如访问网络, 和之前的机制是一样的. 对于危险权限, 一方面要求开发者在AndroidManifest.xml中进行声明, 让用户在安装应用时进行确认, 另一方面, 在app运行时, 当用到特定功能时, 比如要获取用户的位置信息, Android 系统还会再次谈出一个对话框询问用户是否同意这一行为, 这也就是运行时权限这个名字的来源.

代码框架

MainActivity.java
以获取位置数据为例, 正常的代码流程是:

private static final int PERMISSION_REQUEST_LOCATION = 0;


    private void getGPSInfo() {
//检查是否用户之前是否同意过这个运行时权限
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED
                && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
//之前没有同意过这个对话框的话, requestPermissions()会触发系统以异步的方式弹出一个对话框进行确认, 回调在onRequestPermissionsResult()中.
            ActivityCompat.requestPermissions(this,
                    new String[]{Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION},
                    PERMISSION_REQUEST_LOCATION);
        } else {
//之前同意过这个对话框的话, 下一步去实际获取位置数据.
            getCityByGpsInfo();
        }

    }


    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        JLog.i();
        if (requestCode == PERMISSION_REQUEST_LOCATION) {
            if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
//用户在系统对话框中选择同意的话, 下一步去实际获取位置数据.
                getCityByGpsInfo();
            } else {
                // Permission Denied
                Toast.makeText(MainActivity.this, "location Permission Denied", Toast.LENGTH_SHORT).show();
            }
        }
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    }

//去实际获取用户的位置数据
    private void getCityByGpsInfo() {
        double latitude = 0.0;
        double longitude = 0.0;

        LocationManager locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);

        Location gpsLocation = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
        JLog.i("gpsLocation: " + gpsLocation);
        if (gpsLocation != null) {
            latitude = gpsLocation.getLatitude();
            longitude = gpsLocation.getLongitude();
        } else {
            Location networkLocation = locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
            JLog.i("networkLocation: " + networkLocation);
            if (networkLocation != null) {
                latitude = networkLocation.getLatitude();
                longitude = networkLocation.getLongitude();

            } else {
                JLog.i("gps和network都没有获取到位置信息.");
                return;
            }

        }
    }
机型适配的问题
  1. 在Android 6.0 以下的手机上, 因为没有运行时权限这个机制, 执行ActivityCompat.checkSelfPermission() 都会返回0, 也就是PackageManager.PERMISSION_GRANTED.
  2. 在国内的某些机型上, 比如Oppo Android 6.0以上的手机, 它把运行时权限这个机制进行了修改. 执行ActivityCompat.checkSelfPermission()会立刻返回0, 把系统的运行时权限弹框移到了真正获取位置数据的API内部, 也就是在locationManager.getLastKnownLocation()内部, 提供了一个默认弹窗, 并且是以阻塞UI的方式. 如果用户选择拒绝的话, 这个API返回null. 如果用户选择同意的话, 下次再调用locationManager.getLastKnownLocation() API的时候也就不会再次弹窗让用户进行确认了.

---DONE.---

推荐阅读更多精彩内容