.markdown-body { line-height: 1.75; font-weight: 400; font-size: 16px; overflow-x: hidden; color: rgba(37, 41, 51, 1) }
.markdown-body h1, .markdown-body h2, .markdown-body h3, .markdown-body h4, .markdown-body h5, .markdown-body h6 { line-height: 1.5; margin-top: 35px; margin-bottom: 10px; padding-bottom: 5px }
.markdown-body h1 { font-size: 24px; line-height: 38px; margin-bottom: 5px }
.markdown-body h2 { font-size: 22px; line-height: 34px; padding-bottom: 12px; border-bottom: 1px solid rgba(236, 236, 236, 1) }
.markdown-body h3 { font-size: 20px; line-height: 28px }
.markdown-body h4 { font-size: 18px; line-height: 26px }
.markdown-body h5 { font-size: 17px; line-height: 24px }
.markdown-body h6 { font-size: 16px; line-height: 24px }
.markdown-body p { line-height: inherit; margin-top: 22px; margin-bottom: 22px }
.markdown-body img { max-width: 100% }
.markdown-body hr { border-top: 1px solid rgba(221, 221, 221, 1); border-right: none; border-bottom: none; border-left: none; margin-top: 32px; margin-bottom: 32px }
.markdown-body code { border-radius: 2px; overflow-x: auto; background-color: rgba(255, 245, 245, 1); color: rgba(255, 80, 44, 1); font-size: 0.87em; padding: 0.065em 0.4em }
.markdown-body code, .markdown-body pre { font-family: Menlo, Monaco, Consolas, Courier New, monospace }
.markdown-body pre { overflow: auto; position: relative; line-height: 1.75 }
.markdown-body pre>code { font-size: 12px; padding: 15px 12px; margin: 0; word-break: normal; display: block; overflow-x: auto; color: rgba(51, 51, 51, 1); background: rgba(248, 248, 248, 1) }
.markdown-body a { text-decoration: none; color: rgba(2, 105, 200, 1); border-bottom: 1px solid rgba(209, 233, 255, 1) }
.markdown-body a:active, .markdown-body a:hover { color: rgba(39, 91, 140, 1) }
.markdown-body table { display: inline-block !important; font-size: 12px; width: auto; max-width: 100%; overflow: auto; border: 1px solid rgba(246, 246, 246, 1) }
.markdown-body thead { background: rgba(246, 246, 246, 1); color: rgba(0, 0, 0, 1); text-align: left }
.markdown-body tr:nth-child(2n) { background-color: rgba(252, 252, 252, 1) }
.markdown-body td, .markdown-body th { padding: 12px 7px; line-height: 24px }
.markdown-body td { min-width: 120px }
.markdown-body blockquote { color: rgba(102, 102, 102, 1); padding: 1px 23px; margin: 22px 0; border-left: 4px solid rgba(203, 203, 203, 1); background-color: rgba(248, 248, 248, 1) }
.markdown-body blockquote:after { display: block; content: "" }
.markdown-body blockquote>p { margin: 10px 0 }
.markdown-body ol, .markdown-body ul { padding-left: 28px }
.markdown-body ol li, .markdown-body ul li { margin-bottom: 0; list-style: inherit }
.markdown-body ol li .task-list-item, .markdown-body ul li .task-list-item { list-style: none }
.markdown-body ol li .task-list-item ol, .markdown-body ol li .task-list-item ul, .markdown-body ul li .task-list-item ol, .markdown-body ul li .task-list-item ul { margin-top: 0 }
.markdown-body ol ol, .markdown-body ol ul, .markdown-body ul ol, .markdown-body ul ul { margin-top: 3px }
.markdown-body ol li { padding-left: 6px }
.markdown-body .contains-task-list { padding-left: 0 }
.markdown-body .task-list-item { list-style: none }
@media (max-width: 720px) { .markdown-body h1 { font-size: 24px } .markdown-body h2 { font-size: 20px } .markdown-body h3 { font-size: 18px } }.markdown-body pre, .markdown-body pre>code.hljs { color: rgba(51, 51, 51, 1); background: rgba(248, 248, 248, 1) }
.hljs-comment, .hljs-quote { color: rgba(153, 153, 136, 1); font-style: italic }
.hljs-keyword, .hljs-selector-tag, .hljs-subst { color: rgba(51, 51, 51, 1); font-weight: 700 }
.hljs-literal, .hljs-number, .hljs-tag .hljs-attr, .hljs-template-variable, .hljs-variable { color: rgba(0, 128, 128, 1) }
.hljs-doctag, .hljs-string { color: rgba(221, 17, 68, 1) }
.hljs-section, .hljs-selector-id, .hljs-title { color: rgba(153, 0, 0, 1); font-weight: 700 }
.hljs-subst { font-weight: 400 }
.hljs-class .hljs-title, .hljs-type { color: rgba(68, 85, 136, 1); font-weight: 700 }
.hljs-attribute, .hljs-name, .hljs-tag { color: rgba(0, 0, 128, 1); font-weight: 400 }
.hljs-link, .hljs-regexp { color: rgba(0, 153, 38, 1) }
.hljs-bullet, .hljs-symbol { color: rgba(153, 0, 115, 1) }
.hljs-built_in, .hljs-builtin-name { color: rgba(0, 134, 179, 1) }
.hljs-meta { color: rgba(153, 153, 153, 1); font-weight: 700 }
.hljs-deletion { background: rgba(255, 221, 221, 1) }
.hljs-addition { background: rgba(221, 255, 221, 1) }
.hljs-emphasis { font-style: italic }
.hljs-strong { font-weight: 700 }

React-Native开发鸿蒙NEXT-权限处理

原创 悬空八只脚 悬空八只脚 2024年11月01日 20:34 江苏

开发app首先绕不开的就是权限隐私处理。

RN中对app权限的获取判断涉及到原生与js的交互,比起原生app的直接请求需要考虑交互过程中的异常处理。利用RN开发HarmonyOS Next应用同样,下面结合项目看看如何在原生(ArkTS)与RN(js)间进行权限请求交互。

尽管现在越来越强调仅在需要时进行动态请求,一般还是会把权限做成应用启动批量请求默认权限和具体使用时动态申请。为此,在RN代码中,构建了SpecSystemTurboModule类,分别提供初始默认批量请求和动态请求方法。

SpecSystemTurboModule.tsx


import {TurboModule, TurboModuleRegistry} from 'react-native'; // 使用自定义的TurboModules
export interface SpecSystemTurboModule extends TurboModule {
......
// 校验初始化权限
checkedPermission(): void;
// 查询指定权限
queryPermission(permissionEnum: string): void;
...... } export default TurboModuleRegistry.getEnforcing<SpecSystemTurboModule>(
'SystemTurboModule',
);

鉴于Android端对权限请求的要求,将启动时默认的批量请求触发放置在用户同意隐私协议之后。由于目前尚未将新工程适配andorid与ios,暂时判断下操作系统类型只针对鸿蒙系统。

xx页面.tsx

  /**
* 刷新隐私协议同意状态
*/
const doRefreshPrivacyAgreeState = async () => {
// 判断用户是否同意过隐私协议
let _privacyAgreeState = await getData('privacyAgreeState');
if (_privacyAgreeState == null || _privacyAgreeState == 'no') {
setShowPrivacyPolicy(true);
} else {
......
if (Platform.OS === 'harmony') {
// TODO:权限校验
SystemTurboModule.checkedPermission();
}
......
}
};

初始权限请求由于仅会在应用启动时请求一次,对于权限请求的接收,在根页面(app.tsx)中定义消息监听。

App.tsx

function App(): JSX.Element {
......
// 权限校验/申请结果的监听
const permissionResultEventEmitterRef = useRef(null); useEffect(() => {
......
permissionResultEventEmitterRef.current = DeviceEventEmitter.addListener(
'......name111',
(data: string) => {
console.log(App.TAG + ' permissionResult = ' + data);
try {
// todo:这是针对鸿蒙权限的处理,不同的系统申请的权限名称数量不一样,需要做针对性处理
// todo:现在实现了RN测拿到原生对权限结果的返回,可以在RN里针对原生的权限情况做针对处理了
let result = JSON.parse(data);
let permissions = JSON.parse(result.data.value);
console.log(typeof permissions);
......对数据的业务处理
} catch (e) {
console.log(TAG + 'permissionResult ERROR = ' + e);
}
},
);
return () => {
......
permissionResultEventEmitterRef.current &&
permissionResultEventEmitterRef.current.remove();
......
};
}, []);
......
}

对于动态请求部分,则在需要的时候先请求权限,根据返回结果判断是否执行和请求相关的动作,这里以项目中利用react-native-image-picker更行用户个人头像为例。

在点击用户头像时,先请求相机权限

xxx页面.tsx

const check = async select => {
......
if (select == 0) {
//更新头像
checkPermission();
}
......
} const checkPermission = () => {
if (Platform.OS === 'harmony') {
// TODO:先权限校验,根据结果打开相机
SystemTurboModule.queryPermission('CAMERA');
}
}

同样,这页面中定义用于接收动态权限请求的消息监听,根据返回结果判断是否成功获取了权限,进而决定是提示用户没有权限还是去打开相机。注意这里定义的消息名称和初始化批量获取是两个分开的。动态请求是更加普遍的方式,初始批量请求存在审核被拒风险,尽可能按需请求权限。

const queryPermissionResultEventEmitterRef = useRef(null);
......
queryPermissionResultEventEmitterRef.current =
DeviceEventEmitter.addListener(
'......name222',
(data: string) => {
console.log(TAG + ' queryPermissionResultListener = ' + data);
if (global.TAG === TAG) {
try {
// todo:这是针对鸿蒙权限的处理,不同的系统申请的权限名称数量不一样,需要做针对性处理
let result = JSON.parse(data);
let permissions = JSON.parse(result.data.value);
permissions.map(item => {
if (
item.permission &&
item.permission === 'CAMERA' &&
item.result == 0
) {
// 打开相机
selectPic();
} else {
xnToast('请打开相机权限!');
}
});
} catch (e) {
console.log(TAG + 'permissionResult ERROR = ' + e);
}
}
},
);
......

接下来看下原生部分。用于交互的SystemTurboModule中,通过emitter抛出原生消息

SystemTurboModule.ets

import emitter from '@ohos.events.emitter';
import ConstUtil from '../utils/ConstUtil';
......
export interface TurboModuleEventData {
param: string
} export class SystemTurboModule extends TurboModule {
......
checkedPermission() {
console.log("checkedPermission.log from SystemTurboModule: ")
emitter.emit(ConstUtil.event_id_check_permissions,{});
} queryPermission(permissionEnum: string) {
console.log("queryPermission.log from SystemTurboModule: ")
let data: TurboModuleEventData = { param: permissionEnum }
emitter.emit(ConstUtil.event_id_query_permission,{data});
}
......
}

原生部分对权限的处理集中在EntryAbility.ets。

  1. onWindowStageCreate中定义消息接收。

  2. 通过@ohos.abilityAccessCtrl类完成权限的请求。默认权限其实是在ArkTS中直接定义的,动态申请时,由于ios/android/harmony对于权限名称定义的不同,同样需要根据参数做一对一的匹配(是否存在类似ClassFromName的方式?)。

3.获取的结果同样通过emitter抛给index.ets,之所以这样是因为在index中有构造RN容器的创建方法,可以获取到当前bundle的RN对象实例。


import { RNAbility, RNOHCoreContext } from '@rnoh/react-native-openharmony';
import window from '@ohos.window';
import { emitter } from '@kit.BasicServicesKit';
import abilityAccessCtrl, { Context, PermissionRequestResult, Permissions } from '@ohos.abilityAccessCtrl';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { JSON } from '@kit.ArkTS';
import common from '@ohos.app.ability.common';
import { PermissionResult } from '../model/DataModel'
import ConstUtil from '../utils/ConstUtil'
import { App } from '@kit.ArkUI'; export let preferences: dataPreferences.Preferences | null = null
let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();
let appAccessTokenId:number = -1;
export default class EntryAbility extends RNAbility { onWindowStageCreate(windowStage: window.WindowStage): void {
......
// 校验初始化权限
emitter.on(ConstUtil.event_id_check_permissions, () => {
console.info('event_id_check_permissions');
this.checkPermissions();
});
// 使用前申请权限权限
emitter.on(ConstUtil.event_id_query_permission, (data) => {
try{
console.info('event_id_check_permission and data = ' + JSON.stringify(data));
if(data.data){
this.queryPermission(data.data.param);
}
} catch (e) {
}
});
......
} public checkPermissions(){
if (appAccessTokenId != -1) {
atManager.requestPermissionsFromUser(getContext(this),
['ohos.permission.ACCESS_BLUETOOTH',
'ohos.permission.APPROXIMATELY_LOCATION',
'ohos.permission.LOCATION',
]
).then((data: PermissionRequestResult) => {
let authResults:Array<PermissionResult> = new Array;
authResults.push(new PermissionResult('ACCESS_BLUETOOTH',data.authResults[0]))
// authResults.push(new PermissionResult('CAMERA',data.authResults[1]));// 相机权限在使用之前单独申请(鸿蒙要求)
authResults.push(new PermissionResult('APPROXIMATELY_LOCATION',data.authResults[2]));
authResults.push(new PermissionResult('LOCATION',data.authResults[3]));
let eventData: emitter.EventData = {
data: {
"key": "permissionResult",
"value":JSON.stringify(authResults),
}
};
// 发送给index.ets,在index里通过rnInstance发送给js
emitter.emit(ConstUtil.event_id_check_permission_result,eventData);
}).catch((err: BusinessError) => {
console.error('data:' + JSON.stringify(err));
});
}else{
hilog.error(0x0000, 'testTag', 'checkPermissions failed: %{public}s', "xxxxxx");
}
} /**
* 临时申请权限
*/
public queryPermission(permissionString:string){
if (appAccessTokenId != -1) {
let permissions:Array<Permissions> = new Array;
// 转换
if(permissionString === 'CAMERA'){
permissions.push('ohos.permission.CAMERA');
}
if(permissions.length > 0){
atManager.requestPermissionsFromUser(getContext(this),
permissions
).then((data: PermissionRequestResult) => {
let authResults:Array<PermissionResult> = new Array;
authResults.push(new PermissionResult(permissionString,data.authResults[0]));
let eventData: emitter.EventData = {
data: {
"key": "queryPermissionResult",
"value":JSON.stringify(authResults),
}
};
// 发送给index.ets,在index里通过rnInstance发送给js
emitter.emit(ConstUtil.event_id_query_permission_result,eventData);
}).catch((err: BusinessError) => {
console.error('data:' + JSON.stringify(err));
});
}
}else{
hilog.error(0x0000, 'testTag', 'checkPermissions failed: %{public}s', "xxxxxx");
}
}
......
}

在index.ets中,主要实现向RN发送权限获取结果。

  1. 在aboutToAppear生命周期中注册消息接收
  2. 利用当前bundle对应的RN实例向RN发送消息
import {
AnyJSBundleProvider,
ComponentBuilderContext,
FileJSBundleProvider,
MetroJSBundleProvider,
ResourceJSBundleProvider,
RNApp,
RNSurface,
RNOHErrorDialog,
LogBoxDialog,
LogBoxTurboModule,
RNOHLogger,
TraceJSBundleProviderDecorator,
RNOHCoreContext,
RNInstance,
RNComponentContext,
RNOHContext,
buildRNComponentForTag,
} from '@rnoh/react-native-openharmony'
import { SampleView, PropsDisplayer, GeneratedSampleView } from "@rnoh/sample-package"
import {
RNLinearGradient,
LINEAR_GRADIENT_TYPE,
LinearGradientDescriptor
} from "@react-native-oh-tpl/react-native-linear-gradient"
import ConstUtil from '../utils/ConstUtil'
import { emitter } from '@kit.BasicServicesKit';
import window from '@ohos.window';
import PrecreateRN from './PrecreateRN';
import { arkTsComponentNames } from "../rn/LoadBundle"
import { createRNPackages, LoadManager, buildCustomComponent, ENABLE_CAPI_ARCHITECTURE } from '../rn';
import { rnPackageEventData, TurboModuleEventData } from '../TurboModule/SystemTurboModule';
import json from '@ohos.util.json';
@Entry
@Component
struct Index {
private _rnInstance?: RNInstance | null;
aboutToAppear() {
......
// 注册原生发送给rn的emitter
this.registerNativeToJsEmitter();
......
} registerNativeToJsEmitter() {
......
// 权限校验结果
emitter.on(ConstUtil.event_id_check_permission_result, (data) => {
console.info('event_id_check_permissions and data = ' + JSON.stringify(data));
this.sendDeviceEvent(ConstUtil.event_native_to_js_permission_result, JSON.stringify(data));
});
this._rnInstance = LoadManager.metroInstance;
......
} sendDeviceEvent(eventName: string, payload: string) {
if (this._rnInstance != null) {
console.error('向js发送消息, eventName = ' + eventName || '' + " and payload = " + payload || '');
this._rnInstance.emitDeviceEvent(eventName, payload);
} else {
console.error('_rnInstance 为 null!!!无法向js发送消息');
}
}
......
}

以上便是RN开发HarmonyOS NEXT应用中权限处理的一种处理方式,同时也是RN与原生交互的通用方法,整体基于RN的TurboModules。对于ios与android,只需针对各自系统开发原生代码处理权限,在RN端需要针对不同操作系统返回的权限处理结果做分类处理,当然也可以在原生端做下统一的封装。


微信扫一扫

关注该公众号悬空八只脚

React-Native开发鸿蒙NEXT-权限处理的更多相关文章

  1. React Native 开发笔记

    ReactNativeDemo 学习ReactNative开发,搭建ReactNative第一个项目 React Native 开发笔记 1.安装Homebrew $ /usr/bin/ruby -e ...

  2. 搭建基本的React Native开发环境

    步骤如下: 1.安装HomeBrew,命令如下: 在终端输入命令:$ ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Home ...

  3. React Native 开发环境安装和配置使用报错: -bash: react-native: command not found

    [React  Native 开发环境安装和配置:-bash: react-native: command not found 报错: 前提是安装homebrew,node.js ,npm ,watc ...

  4. Mac配置React Native开发环境

    一直觉得学习一样东西,不动手怎么也学不会,就像学习swift,看了视频没有动手操作,记住的也就那么点,自己写出东西不是这里有问题就是那里出错. 所以,以后学习自己要多动手. 现在我的学习任务就是: 提 ...

  5. React Native开发入门

    目录: 一.前言 二.什么是React Native 三.开发环境搭建 四.预备知识 五.最简单的React Native小程序 六.总结 七.参考资料   一.前言 虽然只是简单的了解了一下Reac ...

  6. React Native开发技术周报2

    (1).资讯 1.React Native 0.22_rc版本发布 添加了热自动重载功能 (2).技术文章 1.用 React Native 设计的第一个 iOS 应用 我们想为用户设计一款移动端的应 ...

  7. React Native开发技术周报1

    (一).资讯 1.React Native 0.21版本发布,最新版本功能特点,修复的Bug可以看一下已翻译 重要:如果升级 Android 项目到这个版本一定要读! 我们简化了 Android 应用 ...

  8. DECO 一个REACT NAtive 开发IDE工具

    DECO 一个REACT NAtive 开发IDE工具. 目前只支持 OS,NO WINDOWS https://www.decosoftware.com/ 一个方便的快速 ERXPRESS 教程:h ...

  9. React Native 开发之 (02) 用Sublime 3作为React Native的开发IDE

    Sublime Text是一个代码编辑器.也是HTML和散文先进的文本编辑器.漂亮的用户界面和非凡的功能,例如:迷你地图,多选择Python插件,代码段等等.完全可自定义键绑定,菜单和工具栏等等.漂亮 ...

  10. 【转】【React Native开发】

    [React Native开发]React Native控件之ListView组件讲解以及最齐全实例(19)  [React Native开发]React Native控件之Touchable*系列组 ...

随机推荐

  1. C# USB 摄像头 OpenCV 视频picBox呈现,抓拍图像保存呈现。没有注释版本。

    1.winform 应用程序,两个picturebox空间,一个用于视频呈现,一个用于抓拍呈现. 2.引用包OpenCvSharp4.OpenCvSharp4.Extensions.OpenCvSha ...

  2. eslint-plugin-vue配置中文翻译

    eslint-plugin-vue配置中文翻译 由于 ellint 配置太多,很多小伙伴不知道其功能是什么,在此做个记录. //更详细的配置文档请参考:https://github.com/vuejs ...

  3. godoc使用方法介绍

    一.go doc简介 Godoc是go语言的文档化工具,类似于文档化工具godoc,类似于Python的Docstring和Java的Javadoc Godoc通过解析包含注释的Go代码来生成HTML ...

  4. OpenGL与GLSL各版本对应说明

    OpenGL 4.6 (API Core Profile) (API Compatibility Profile) OpenGL Shading Language 4.60 Specification ...

  5. RSA密钥生成-已知p、q、e求私钥d的python脚本

    题目: 在一次RSA密钥对生成中,假设p=473398607161,q=4511491,e=17 求解出d作为flag提交 求解过程: 首先计算n和ϕ(n) n=p*q  ϕ(n)=(p-1)(q-1 ...

  6. Nginx可以同时支持ipv4与 ipv6的监听

    Nginx可以同时支持ipv4与 ipv6的监听,但为了一致性的考虑,新版本Nginx推荐使用分开监听,下面我们开始进入正题. 一.默认IPV4配置 下面我们先来看一看默认的ipv4配置: 二.加入i ...

  7. Delphi让网页只允许在WebBrowser里面打开

    [添加组件] 添加 Internet->WebBrowser //显示网页 [添加事件] 鼠标点击WebBrowser组件,在Events事件选项框中找到. OnNewWindows2,OnSt ...

  8. 【JDBC第9章】Apache-DBUtils实现CRUD操作

    第9章:Apache-DBUtils实现CRUD操作 9.1 Apache-DBUtils简介 commons-dbutils 是 Apache 组织提供的一个开源 JDBC工具类库,它是对JDBC的 ...

  9. 学习unigui【22】unistringGrid的标题栏双击事件

    第一步:在TuniStringGrid的ClientEvents.ExtEvents中定义Ext.grid.Panel的reconfigure事件: function reconfigure(send ...

  10. Chrome谷歌浏览器常用快捷键、开发技巧

    谷歌浏览器作为常用的开发工具,熟悉常用的快捷键,不仅方便快捷,也能间接提高不少工作效率.以下是谷歌浏览器常用快捷键和开发技巧. 标签页和窗口快捷键 1. Ctrl + n 打开新窗口 2. Ctrl ...