一款好用的鸿蒙系统上的权限请求框架

描述

   

桃夭是鸿蒙系统上的一款权限请求框架,对请求权限的代码进行高度封装,极大的简化了申请权限的代码逻辑,同时支持在 Ability、FractionAbility、AbilitySlice、Fractiion 里面申请权限。

     建议大家把源码下载下来看看:
https://gitee.com/zhongte/TaoYao
   

申请权限

     

申请权限的一般步骤如下

  • 判断是否有权限,如果有权限,直接进行下一步。

  • 如果没有权限,可以弹窗告知用户申请权限的原因。

  • 弹窗告知用户后,如果用户同意申请权限,则判断用户是否点击了不再提醒。

  • 如果用户没有点击不再提醒,则开始申请权限。

  • 如果用户点击了不再提醒,则弹窗告知用户去设置页面开启权限,用户点击弹窗后,跳转到设置页面。

  • 重写 onRequestPermissionsFromUserResult 方法,判断用户是否授予权限。

 

每次申请权限的时候,都需要经过以上几个步骤,当申请的权限越来越多,大量的重复代码就出现了。

 

为了减少重复代码,我封装了一个权限请求框架,权限请求框架取名为桃夭。    

桃夭的使用方式

     如下代码,先添加依赖,然后你只需要告知权限请求框架你需要什么权限,权限请求框架就会告知你权限申请成功还是失败。

 

你不需要手动判断是否有权限,不需要弹窗告知用户申请权限的原因,不需要判断用户是否点击了不再提醒,不需要跳转设置页面让用户开启权限,不需要重写 onRequestPermissionsFromUserResult 方法。

 

框架把这些代码逻辑都给做了,你只需要关注权限申请成功还是失败。申请权限变得如此之简单。  

添加依赖:

api 'io.gitee.zhongte1.0.1'

 

申请权限:

// 申请多设备协同权限
EasyPermission.requestPermission(this, EasyPermission.DISTRIBUTED_DATASYNC, new PermissionAction() {
            @Override
            public void onGranted(List permissions) {
                // 权限申请成功

            }

            @Override
            public void onDenied(List permissions) {
                // 权限申请失败
            }
        }, SystemPermission.DISTRIBUTED_DATASYNC);

 

申请权限的时候可能会涉及到两个弹窗,一个弹窗是用来告知用户申请权限的原因,另一个弹窗是用来告知用户去设置页面开启权限。

 

这两个弹窗在不同的应用里面可能长得不一样,所以这两个弹窗并没有被封装到桃夭框架里面,而是需要使用者根据你的弹窗样式对桃夭进行二次封装。

 

我在源码里面对桃夭框架进行了二次封装,大家可以把源码下载下来,参考下我是如何对桃夭框架进行二次封装的。二次封装完成后,就可以愉快的使用上面的代码申请权限了。  

实现原理

   

 

①检测申请的权限是否在配置文件中声明

 申请的权限必须在配置文件中声明,否则桃夭会直接抛异常。如何检测申请的权限是否在配置文件中声明。

 

如下代码,获取 bundleManager 对象,通过 bundleManager 对象获取应用信息,之后就可以获取应用在配置文件中声明的权限了。

    /**
     * 获取在配置文件中声明的权限
     *
     * @param context 上下文
     * @return 在配置文件中声明的权限
     */
    private List getConfigPermissions(Context context) {
        // 获取bundleManager对象
        IBundleManager bundleManager = context.getBundleManager();
        String bundleName = context.getBundleName();
        try {
            // 获取应用信息
            BundleInfo bundleInfo = bundleManager.getBundleInfo(bundleName, IBundleManager.GET_BUNDLE_WITH_REQUESTED_PERMISSION);
            // 获取应用在配置文件中声明的权限
            List reqPermissionDetails = bundleInfo.reqPermissions;
            if (reqPermissionDetails == null || reqPermissionDetails.isEmpty()) {
                throw new IllegalStateException("请在配置文件中声明要申请的权限");
            }
            return reqPermissionDetails;
        } catch (RemoteException e) {
            e.printStackTrace();
        }
        return new ArrayList<>();
    }

 

获取到在配置文件中声明的权限后,就可以判断申请的权限是否在配置文件中了。

    /**
     * 检查申请的权限是否在配置文件中声明
     *
     * @param permissions 要申请的权限
     */
    private void checkPermissions(String... permissions) {
        if (permissions == null || permissions.length == 0) {
            throw new IllegalArgumentException("请至少申请一个权限");
        }
        // 获取在配置文件中声明的权限
        mReqPermissions = getConfigPermissions(mOrigin.getContext());
        if (mReqPermissions.isEmpty()) {
            throw new IllegalStateException("请在配置文件中声明要申请的权限");
        }
        for (String target : permissions) {
            if (!mReqPermissions.contains(target)) {
                // 没有在配置中声明要申请的权限,直接抛异常
                throw new IllegalStateException(String.format("%1$s权限没有配置文件中声明", target));
            }
        }
    }

 

②判断是否有权限

 

检测完权限是否在配置中声明后,就可以判断是否有权限了。这里就是通过上下文对象的 verifySelfPermission 方法来判断是否有权限,如果没有权限,可以弹窗告知用户申请的原因。

    /**
     * 是否有权限
     * 
     * @param context
     * @param permissions
     * @return
     */
    @Override
    public boolean hasPermission(Context context, List permissions) {
        for (String permission : permissions) {
            int result = context.verifySelfPermission(permission);
            if (result == IBundleManager.PERMISSION_DENIED) {
                // 没有权限
                return false;
            }
        }
        return true;
    }

 

③判断用户是否点击了不再提醒

 

通过上下文对象的 canRequestPermission 方法来判断用户是否点击了不再提醒。

    /**
     * 用户是否点击了不在提醒
     *
     * @param permission 权限
     * @return
     */
    @Override
    public boolean canRequestPermission(String permission) {
        return mContext.canRequestPermission(permission);
    }
 

④跳转到设置页面

 

如果用户点击了不再提醒,则可以跳转到设置页面让用户开启权限。

    /**
     * 跳转到设置页面
     */
    @Override
    public void gotoSetting() {
        try {
            Intent intent = new Intent();
            intent.setAction(IntentConstants.ACTION_APPLICATION_DETAILS_SETTINGS);
            intent.setUri(Uri.parse("package:" + mOrigin.getContext().getBundleName()));
            mOrigin.startAbility(intent);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

 

⑤启动透明的 Ability 申请权限

 如果没有权限,用户页面没有点击不再提醒,那就可以申请权限了。为了不让调用者重写 onRequestPermissionsFromUserResult 方法,桃夭内部启动了一个 Ability。

 

如下代码:

/**
 * 开启一个透明的Ability来申请权限,这样外界就不需要重写onRequestPermissionsFromUserResult方法
 */
public class PermissionAbility extends Ability {

    private static final int REQUEST_CODE = 0X10;
    public static final String KEY_PERMISSION = "key_permission";
    public static final String KEY_TYPE = "key_type";
    public static final String SENSITIVE_PERMISSION = "sensitive_permission";

    @Override
    public void onStart(Intent intent) {
        super.onStart(intent);
        getWindow().setTransparent(true);
        super.setUIContent(ResourceTable.Layout_ability_permission);
        List permissions = intent.getSerializableParam(KEY_PERMISSION);
        String permissionType = intent.getStringParam(KEY_TYPE);
        // 请求权限
        requestPermissionsFromUser(permissions.toArray(new String[0]), REQUEST_CODE);
    }

    @Override
    public void onRequestPermissionsFromUserResult(int requestCode, String[] permissions, int[] grantResults) {
        super.onRequestPermissionsFromUserResult(requestCode, permissions, grantResults);
        // 权限的回调方法
        Postman.send(permissions, grantResults);
        terminateAbility();
    }

    @Override
    protected void onAbilityResult(int requestCode, int resultCode, Intent resultData) {
        super.onAbilityResult(requestCode, resultCode, resultData);
    }
}

 

直接启动一个 Ability 会发生页面跳转,为了不让页面发生跳转,这里启动了一个透明的 Ability。

 

如何将 Ability 设置透明,如下代码。在 abilities 节点添加 metaData,这里最关键的是 Translucent,也就是透明。

"abilities": [
      {
        "orientation""unspecified",
        "name""com.poetry.taoyao.ability.PermissionAbility",
        "icon""$media:icon",
        "description""$string:permissionability_description",
        "label""$string:taoyao_PermissionAbility",
        "type""page",
        "launchType""standard",
        "metaData": {
          "customizeData": [
            {
              "name""hwc-theme",
              "value""androidhwext:style/Theme.Emui.Translucent.NoTitleBar"
            }
          ]
        }
      }

 

仅仅有上面的步骤好不够,需要在 Ability 或者 AbilitySlice 里面将窗口设置成透明。  

如下代码:

getWindow().setTransparent(true);

 

经过上面两步,也就是将 Ability 的主题和窗口都设置成透明,这样就能将 Ability 变成透明的了,同时也不会发生页面跳转。  

⑥判断用户是否授予权限

 

判断用户是否授予权限,可以使用标准的方式来判断。也就是通过 grantResult 来判断用户是否授予权限。

    @Override
    public boolean hasPermission(int[] grantResults, String... permissions) {
        if (grantResults == null || grantResults.length <= 0) {
            return false;
        }
        for (int grantResult : grantResults) {
            if (grantResult == IBundleManager.PERMISSION_DENIED) {
                return false;
            }
        }
        return true;
    }

 

其实还有另外的方式来判断用户是否授予权限。也就是不管用户是否授权,直接访问相关业务。

 

比如,申请录音权限,当系统回调 onRequestPermissionsFromUserResult 方法时,直接去录音,如果发生异常,捕获异常说明没有权限。

 

如下代码:

    /**
     * 通过直接录音的方式来判断是否有录音权限
     * 
     * @param context
     * @return
     * @throws Throwable
     */
    @Override
    public boolean test(Context context) throws Throwable {
        AudioStreamInfo audioStreamInfo = new AudioStreamInfo.Builder().encodingFormat(
                AudioStreamInfo.EncodingFormat.ENCODING_PCM_16BIT)
                .channelMask(AudioStreamInfo.ChannelMask.CHANNEL_IN_STEREO)
                .sampleRate(AUDIO_SAMPLE_RATE)
                .build();
        AudioCapturerInfo audioCapturerInfo = new AudioCapturerInfo.Builder().audioStreamInfo(audioStreamInfo).build();
        try {
            AudioCapturer capturer = new AudioCapturer(audioCapturerInfo);
            // 录音
            capturer.start();
            new Timer().schedule(new TimerTask() {
                @Override
                public void run() {
                    capturer.stop();
                }
            }, AUDIO_RECORDING_TIME);
            // 没有发生异常,有权限
            return true;
        } catch (Exception e) {
            // 发生异常,无权限
            return false;
        }
    }

 

桃夭在判断用户是否授权时,上面的两种方式都使用了。至此,桃夭框架的原理基本上讲完。有兴趣的同学可以去看看源码。

 

要看懂源码,需要熟悉申请权限的一般步骤,桃夭其实就对这些步骤进行封装。另外还需熟悉面向接口编程、熟悉策略模式等常见设计模式。
编辑:jq
打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分