App实战:动态申请权限以及为所欲为之终极扩展

App实战:权限管理

今天下午7酱又要上场了,开森。b话不多说,先来一套泰拳警告。

上一篇博客中,其实遗留了一个比较大的问题。那就是我们保存图片到本地,是需要文件存储权限的,而我是直接在manifests文件中注册。这对于6.0之前系统的手机是没有问题,但是在6.0之后手机就不行了。因为Google认为写入文件的权限属于隐私权限,需要向用户申请,而不是开发者决定。

还是那个妹子!

这里写图片描述

动态申请权限

现在权限管理的库已经有很多了,而我作为葬爱家族的首席Android开发,我选谷歌官方出的EasyPermissions。我就不给各位大兄dei绕弯子了,直接给出几个主要步骤(首先Activity实现EasyPermissions.PermissionCallbacks接口):

  • 第一肯定是要申请权限,项目里面可以这么写:

    String[] perms = {Manifest.permission.WRITE_EXTERNAL_STORAGE};
    EasyPermissions.requestPermissions(this, "保存图片需要文件存储",
          Constants.WRITE_REQUEST_CODE, perms);
  • 请求回调,在Activity里的onRequestPermissionsResult接管:

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults)
    {
      super.onRequestPermissionsResult(requestCode, permissions, grantResults);
      switch (requestCode)
      {
          case Constants.WRITE_REQUEST_CODE:
              //说白了就是用EasyPermissions来接管Activity中的此方法
              EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this);
              break;
          default:
    
              break;
      }
    }
  • 授权成功和失败的回调:

    public void onPermissionsGranted(int requestCode, List<String> perms)
    {
      switch (requestCode)
      {
          case Constants.WRITE_REQUEST_CODE:
              //这里如果调用saveImgToPhone,writePer中还会执行一次
              //如果说此次动态申请的权限全部成功,没有一个拒绝,那么会执行writePer方法
    
              break;
          default:
              break;
      }
    }
    public void onPermissionsDenied(int requestCode, List<String> perms)
    {
      switch (requestCode)
      {
          case Constants.WRITE_REQUEST_CODE:
              Toast.makeText(FlatMap1Activity.this, "缺少文件存储,图片保存失败", Toast.LENGTH_SHORT).show();
              //在拒绝的这个地方来进行终极处理, 这里防止有人点击了不再提醒的选项
    
              break;
          default:
              break;
      }
    }

    注意到EasyPermissions库给我们提供了一个AfterPermissionGranted注解,它的用法如下:

    //注意本方法返回类型必须是void, 而且是无参数
    @AfterPermissionGranted(Constants.WRITE_REQUEST_CODE)
    private void writePer()
    {
      String[] perms = {Manifest.permission.WRITE_EXTERNAL_STORAGE};
      if (EasyPermissions.hasPermissions(this, perms))
      {
          saveImgToPhone();
      } else
      {
          EasyPermissions.requestPermissions(this, "保存图片需要文件存储",
                  Constants.WRITE_REQUEST_CODE, perms);
      }
    }

    根据注解名字也能明白意思,就是在权限授权成功后,会调用此方法。所以我们可以把权限申请放在里面,判断当已经有了存储权限时,我们就不用进行申请权限的步骤;否则才会进行权限申请。这里面有两个点需要注意下的。

    第一是:

    /**
    * Request permissions from an Activity with standard OK/Cancel buttons.
    *
    * @see #requestPermissions(Activity, String, int, int, int, String...)
    */
    public static void requestPermissions(
          @NonNull Activity host, @NonNull String rationale,
          int requestCode, @NonNull String... perms) {
      requestPermissions(host, rationale, android.R.string.ok, android.R.string.cancel,
              requestCode, perms);
    }

    这个方法的第二个参数就是我们给这个权限的解释,即“保存图片需要文件存储权限”。注意到它其实调用了

    多两个参数的requestPermissions方法,那两个参数默认是确定和取消。当然你也可以自定义了,这么写:

    EasyPermissions.requestPermissions(this, "保存图片需要文件存储",
                      getResources().getString(R.string.refuse),
                      getResources().getString(R.string.gladly_agree),
                      Constants.WRITE_REQUEST_CODE, perms);

    第二是:AfterPermissionGranted注解的方法。

    public static void onRequestPermissionsResult(int requestCode,
                                                @NonNull String[] permissions,
                                                @NonNull int[] grantResults,
                                                @NonNull Object... receivers) {
    
          //省略大段代码, 只有当所有的权限都授权成功, 没有权限拒绝时,才会执行
          //被AfterPermissionGranted注解的方法
          // If 100% successful, call annotated methods
          if (!granted.isEmpty() && denied.isEmpty()) {
              runAnnotatedMethods(object, requestCode);
          }
      }
    }

    上面看似已经很完美了,但是Google除了一个比较人性化的选项卡,那就是不再询问!!!当有个用户惦记了不再询问而且拒绝了之后,那么就再也不会调出弹框和解释框了,只会走onDenied拒绝的回调。坑死人啊,咋办咧???

    终极防护措施

    我们先来理一理权限申请的流程,如下图所示:

    这里写图片描述

    所以想要解决问题,我们可以在onPermissionsDenied方法里面做文章了。我们可以这样做:

      @Override
      public void onPermissionsDenied(int requestCode, List<String> perms)
      {
          switch (requestCode)
          {
              case Constants.WRITE_REQUEST_CODE:
                  Toast.makeText(FlatMap1Activity.this, "缺少文件存储,图片保存失败", Toast.LENGTH_SHORT).show();
                  //在拒绝的这个地方来进行终极处理, 这里防止有人点击了不再提醒的选项
                  App.getSp().edit().putInt(Constants.REQUEST_CODE_PERMISSION, Constants.WRITE_REQUEST_CODE).commit();
                  PermissionUtil.showMissingPermissionDialog(this, "存储");
                  break;
              default:
    
                  break;
          }
      }

    我们会在拒绝后给出一个人为的再给一个弹框,并且详细向用户解释发生了什么,如下图所示:

    这里写图片描述

    然后给设置按钮一个点击事件跳转到该应用管理权限的页面。代码如下:

    /**
    * 启动应用的设置
    *
    * @param activity
    */
    public static void startAppSettings(Activity activity)
    {
      Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
      intent.setData(Uri.parse("package:" + activity.getPackageName()));
      activity.startActivityForResult(intent, App.getSp().getInt(Constants.REQUEST_CODE_PERMISSION, 0));
    }

    注意到我们用了一个startActivityForResult启动回调,然后在申请权限的页面我们再这么写:

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data)
    {
      super.onActivityResult(requestCode, resultCode, data);
      switch (requestCode)
      {
          case Constants.WRITE_REQUEST_CODE:
              //页面返回后再次执行此方法
              writePer();
              break;
          default:
              break;
      }
    }

    哦耶,舒服了。恭喜7酱夺下一城!

    Github:Demo

    以上。

上篇博客:App网络请求实战四:rxjava操作符flatMap使用以及rxjava源码解析
下篇博客:App架构设计实战二:基于MVP模式的相似UI界面复用问题解决方案

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值