今天仍是围绕着最近面试的一个热门话题Android 6.0权限适配来总结学习,其实Android 6.0权限适配咱们公司是在今年5月份才开始作,算是比较晚的吧,不过如今Android 6.0以上设备愈来愈多了,因此Android 6.0 权限适配是必不可少的工做,这里主要介绍一下咱们公司是如何作Android 6.0权限适配的。html
权限管理相关博客:面试
根据上面博客咱们很清楚的知道,Android的权限其实就是为了程序之间更加的安全的访问,因此权限有等级之分,好比:Normal 低风险权限 、Dangerous 高风险权限等,虽然有这种安全意识,可是这些权限只会在安装的时候被询问一次,一旦安装以后,若是app申请了高风险权限的话,并且大部分用户在安装的时候不多去关注这些权限列表,再加上不少Android市场都有静默安装的功能用户更加感知不到任何权限提示,就这样app就有可能会在后台作一些对用户带来伤害的事情。以下图所示:安全
鉴于6.0以前的版本权限管理相对不那么安全,因此Android 6.0 采用新的权限模型,只有在须要权限的时候,才告知用户是否受权,是在runtime时候受权,而不是在原来安装的时候 ,同时默认状况下每次在运行时打开页面时候,须要先检查是否有所须要的权限申请。这样的用户的自主性提升不少,好比用户能够给APP赋予摄像的权限,也能够使用权限。app
先看下app module的build.gradle配置ide
compileSdkVersion 24
buildToolsVersion "24.0.2"
defaultConfig {
applicationId "com.whoislcj.rxpermissions"
minSdkVersion 15
targetSdkVersion 24
versionCode 1
versionName "1.0"
}
因为Android 6.0 以上的权限变成了运行时权限,也就是说在须要使用某个权限的时候必须动态去申请使用,直接访问直接致使app崩溃。学习
其实判断是不是须要运行时权限的标记就是targetSDKVersion,当targetSDKVersion<23的时候,仅在安装时赋予权限,使用时将不被提醒,当targetSDKVersion≥23的时候才会使用新的运行时权限规则。全部在最先碰见因权限未适配的致使的崩溃的时候,咱们团队采用的解决办法是将targetSDKVersion人为的降到小于23,这样就变成了仍是默认使用权限,可是这种并非Google所推荐使用的。gradle
compileSdkVersion 24
buildToolsVersion "24.0.2"
defaultConfig {
applicationId "com.whoislcj.rxpermissions"
minSdkVersion 15
targetSdkVersion 22
versionCode 1
versionName "1.0"
}
检查是否拥有使用权ui
public boolean isGranted(String permission) { return !isMarshmallow() || isGranted_(permission); }
判断是不是Android 6.0以上this
private boolean isMarshmallow() { return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M; }
是否申请了该使用权限spa
private boolean isGranted_(String permission) { int checkSelfPermission = ActivityCompat.checkSelfPermission(this, permission); return checkSelfPermission == PackageManager.PERMISSION_GRANTED; }
ContextCompat.checkSelfPermission
,主要用于检测某个权限是否已经被授予,方法返回值为PackageManager.PERMISSION_DENIED
或者PackageManager.PERMISSION_GRANTED
。当返回DENIED就须要进行申请受权了。
private void requestPermission(String permission, int requestCode) { if (!isGranted(permission)) { if (ActivityCompat.shouldShowRequestPermissionRationale(this, permission)) { } else { ActivityCompat.requestPermissions(this, new String[]{permission}, requestCode); } } else { //直接执行相应操做了 } }
shouldShowRequestPermissionRationale主要用于给用户一个申请权限的解释,该方法只有在用户在上一次已经拒绝过你的这个权限申请。也就是说,用户已经拒绝一次了,你又弹个受权框,你须要给用户一个解释,为何要受权,则使用该方法。requestCode这个须要在处理的回调的时候 一一对应的。
@Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { if (requestCode == CAMERA) { if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { String jpgPath = getCacheDir() + "test.jpg"; takePhotoByPath(jpgPath, 2); } else { // Permission Denied Toast.makeText(MainActivity.this, "您没有受权该权限,请在设置中打开受权", Toast.LENGTH_SHORT).show(); } return; } super.onRequestPermissionsResult(requestCode, permissions, grantResults); }
public class MainActivity extends AppCompatActivity { private static final int CAMERA = 2; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); findViewById(R.id.request_permission).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { requestPermission(Manifest.permission.CAMERA, CAMERA); } }); } /** * 拍照,返回拍照文件的绝对路径 */ private String takePhotoByPath(String filePath, int requestCode) { File file = new File(filePath); startActivityForResult(getTakePhotoIntent(file), requestCode); return file.getPath(); } private Intent getTakePhotoIntent(File file) { if (file.exists()) { file.delete(); } try { file.createNewFile(); } catch (IOException e) { e.printStackTrace(); } Uri uri = Uri.fromFile(file); Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); intent.putExtra(MediaStore.EXTRA_OUTPUT, uri); return intent; } public boolean isGranted(String permission) { return !isMarshmallow() || isGranted_(permission); } private boolean isGranted_(String permission) { int checkSelfPermission = ActivityCompat.checkSelfPermission(this, permission); return checkSelfPermission == PackageManager.PERMISSION_GRANTED; } private boolean isMarshmallow() { return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M; } //shouldShowRequestPermissionRationale主要用于给用户一个申请权限的解释,该方法只有在用户在上一次已经拒绝过你的这个权限申请。也就是说,用户已经拒绝一次了,你又弹个受权框,你须要给用户一个解释,为何要受权,则使用该方法。 private void requestPermission(String permission, int requestCode) { if (!isGranted(permission)) { if (ActivityCompat.shouldShowRequestPermissionRationale(this, permission)) { } else { ActivityCompat.requestPermissions(this, new String[]{permission}, requestCode); } } else { //直接执行相应操做了 } } @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { if (requestCode == CAMERA) { if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { String jpgPath = getCacheDir() + "test.jpg"; takePhotoByPath(jpgPath, 2); } else { // Permission Denied Toast.makeText(MainActivity.this, "您没有受权该权限,请在设置中打开受权", Toast.LENGTH_SHORT).show(); } return; } super.onRequestPermissionsResult(requestCode, permissions, grantResults); } }
本篇总结学习了Android 6.0的运行时权限及如何适配的问题,可是这个并非咱们公司目前最终的解决办法,从上面能够看出实现起来仍是蛮麻烦的,申请权限和处理回调在不一样的地方代码可读性相对较差,咱们最终的解决方案是采用RxJava+RxPermission的方式解决,下一篇将介绍一下如何使用RxPermission解决Android 6.0 权限适配问题。