Android权限管理之Android 6.0运行时权限及解决办法

LilianaWhit 8年前
   <h3><strong>前言:</strong></h3>    <p>今天还是围绕着最近面试的一个热门话题Android 6.0权限适配来总结学习,其实Android 6.0权限适配我们公司是在今年5月份才开始做,算是比较晚的吧,不过现在Android 6.0以上设备越来越多了,所以Android 6.0 权限适配是必不可少的工作,这里主要介绍一下我们公司是如何做Android 6.0权限适配的。</p>    <h3><strong>Android 6.0以下非运行时权限:</strong></h3>    <p>根据上面博客我们很清楚的知道,Android的权限其实就是为了程序之间更加的安全的访问,所以权限有等级之分,比如:Normal 低风险权限 、Dangerous  高风险权限等,虽然有这种安全意识,但是这些权限只会在安装的时候被询问一次,一旦安装之后,如果app申请了高风险权限的话,而且大部分用户在安装的时候很少去关注这些权限列表,再加上很多Android市场都有静默安装的功能用户更加感知不到任何权限提示,就这样app就有可能会在后台做一些对用户带来伤害的事情。如下图所示:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/6fb025ab0d6971ad4691833a504a37f4.jpg"></p>    <h3><strong>Android6.0运行时权限:</strong></h3>    <p>鉴于6.0之前的版本权限管理相对不那么安全,所以Android 6.0 采用新的权限模型,只有在需要权限的时候,才告知用户是否授权,是在runtime时候授权,而不是在原来安装的时候 ,同时默认情况下每次在运行时打开页面时候,需要先检查是否有所需要的权限申请。这样的用户的自主性提高很多,比如用户可以给APP赋予摄像的权限,也可以使用权限。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/a7b3ebbaeadf123128fddec889f0d0b5.png"></p>    <h3><strong>Android 6.0权限适配:</strong></h3>    <p><strong>1.)不进行适配造成的现象</strong></p>    <p>先看下app module的build.gradle配置</p>    <pre>  <code class="language-java"> compileSdkVersion 24      buildToolsVersion "24.0.2"      defaultConfig {          applicationId "com.whoislcj.rxpermissions"          minSdkVersion 15          targetSdkVersion 24          versionCode 1          versionName "1.0"      }  </code></pre>    <p>由于Android 6.0 以上的权限变成了运行时权限,也就是说在需要使用某个权限的时候必须动态去申请使用,直接访问直接导致app崩溃。</p>    <p><img src="https://simg.open-open.com/show/753882d055fab92ce67786acc0159dd4.png"></p>    <p><strong>2.)早期的解决办法</strong></p>    <p>其实判断是否是需要运行时权限的标记就是targetSDKVersion,当targetSDKVersion<23的时候,仅在安装时赋予权限,使用时将不被提醒,当targetSDKVersion≥23的时候才会使用新的运行时权限规则。所有在最早遇见因权限未适配的导致的崩溃的时候,我们团队采用的解决办法是将targetSDKVersion人为的降到小于23,这样就变成了还是默认使用权限,但是这种并不是Google所推荐使用的。</p>    <pre>  <code class="language-java"> compileSdkVersion 24      buildToolsVersion "24.0.2"      defaultConfig {          applicationId "com.whoislcj.rxpermissions"          minSdkVersion 15          targetSdkVersion 22          versionCode 1          versionName "1.0"      }  </code></pre>    <p><strong>3.)判断是否拥有该权限的使用权限</strong></p>    <p>检查是否拥有使用权</p>    <pre>  <code class="language-java">    public boolean isGranted(String permission) {          return !isMarshmallow() || isGranted_(permission);      }  </code></pre>    <p>判断是否是Android 6.0以上</p>    <pre>  <code class="language-java">  private boolean isMarshmallow() {          return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M;      }  </code></pre>    <p>是否申请了该使用权限</p>    <pre>  <code class="language-java"> private boolean isGranted_(String permission) {          int checkSelfPermission = ActivityCompat.checkSelfPermission(this, permission);          return checkSelfPermission == PackageManager.PERMISSION_GRANTED;      }  </code></pre>    <p>ContextCompat.checkSelfPermission ,主要用于检测某个权限是否已经被授予,方法返回值为 PackageManager.PERMISSION_DENIED 或者 PackageManager.PERMISSION_GRANTED 。当返回DENIED就需要进行申请授权了。</p>    <p><strong>4.)申请使用权限</strong></p>    <pre>  <code class="language-java">    private void requestPermission(String permission, int requestCode) {          if (!isGranted(permission)) {              if (ActivityCompat.shouldShowRequestPermissionRationale(this, permission)) {                } else {                  ActivityCompat.requestPermissions(this, new String[]{permission}, requestCode);              }          } else {              //直接执行相应操作了          }      }  </code></pre>    <p>shouldShowRequestPermissionRationale主要用于给用户一个申请权限的解释,该方法只有在用户在上一次已经拒绝过你的这个权限申请。也就是说,用户已经拒绝一次了,你又弹个授权框,你需要给用户一个解释,为什么要授权,则使用该方法。requestCode这个需要在处理的回调的时候 一一对应的。</p>    <p><strong>5.)处理授权回调</strong></p>    <pre>  <code class="language-java">   @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);      }  </code></pre>    <p>6.)完整的Activity示例</p>    <pre>  <code class="language-java">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);      }    }  </code></pre>    <h3><strong>总结:</strong></h3>    <p>本篇总结学习了Android 6.0的运行时权限及如何适配的问题,但是这个并不是我们公司目前最终的解决办法,从上面可以看出实现起来还是蛮麻烦的,申请权限和处理回调在不同的地方代码可读性相对较差,我们最终的解决方案是采用RxJava+RxPermission的方式解决,下一篇将介绍一下如何使用RxPermission解决Android 6.0 权限适配问题。</p>    <p> </p>    <p>来自:http://www.cnblogs.com/whoislcj/p/6064710.html</p>    <p> </p>