# BeChat **Repository Path**: leekai/be-chat ## Basic Information - **Project Name**: BeChat - **Description**: 使用ArkTs开发的harmonyOS应用 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 2 - **Forks**: 1 - **Created**: 2023-11-22 - **Last Updated**: 2024-01-08 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Zora Zora提供APP与底层功能的挂接并提供简单易用API,开发者无需关心底层实现,底层修改实现方式也不涉及上层的代码改动。 目前Zora提供的功能有Http、运行时权限、页面路由、日志输出等功能。ZoraCore为核心底层HSP包,entry为Demo模块。 # ZoraCore ZoraCore为框架核心包,通过HSP的方式进行集成并提供服务。 ## 一. 网络请求模块 ### 1. 发送Post请求: ```typescript Zora.http() //获取网络模块 .post('https://your_url/your/path') //请求为Post并设置请求地址 .setDesc("test_post") //给请求起一个名字,方便日志打印和追踪 .addParam('name', 'Zora') //添加参数 .addParam('age', 18) //添加参数 .addHeaderParam('Auth', 'AJSGDJAHGDAS') //添加Header参数 .setCookies('1234556') //可一次性设置Cookie .setPriority(2) //设置请求优先级,不是所有框架都支持,目前Harmony源生是支持的 .send({ beforeSend(request) { //请求发送前调用,这里可以修改请求参数并决定是否发送该请求 //返回 true则发送,返回false则框架直接调用afterSend() return true; }, onResponse(request, response) { //请求正常返回时调用 }, onError(request, err) { // 请求出错时调用 }, afterSend(request) { // 无论请求是否正常返回,afterSend方法必然会在最后被调用 } }) ``` ### 2. 发送Get请求: ```typescript Zora.http() //获取网络模块 .get('https://your_url/your/path') //请求为Post并设置请求地址 .setDesc("test_post") //给请求起一个名字,方便日志打印和追踪 .addParam('name', 'Zora') //添加参数 .addParam('age', 18) //添加参数 .addHeaderParam('Auth', 'AJSGDJAHGDAS') //添加Header参数 .setCookies('1234556') //可一次性设置Cookie .setPriority(2) //设置请求优先级,不是所有框架都支持,目前Harmony源生是支持的 .send({ beforeSend(request) { //请求发送前调用,这里可以修改请求参数并决定是否发送该请求 //返回 true则发送,返回false则框架直接调用afterSend() return true; }, onResponse(request, response) { //请求正常返回时调用 }, onError(request, err) { // 请求出错时调用 }, afterSend(request) { // 无论请求是否正常返回,afterSend方法必然会在最后被调用 } }) ``` ### 3. 文件下载: ```typescript Zora.http() //获取网络模块 .download('https://your_download/url') //设置下载地址 .setTitle('下载测试图片') //设置下载标题 .setDesc('下载一个测试图片') //设置下载描述 .setFilePath(getContext().cacheDir + '/test.jpg') //文件保存地址 .send({ beforeDownload() { //下载前调用 }, progress(current, total) { //下载进度 }, completed() { //下载完成 }, onError(err) { //下载出错 }, afterDownload() { //无论下载成功与否都会调用 } }); ``` ### 4. 文件上传: 待基于Next开发,API9有问题。 ### 5. 拦截器(Interceptor) Request和Response的拦截器均支持,并且可以根据组名称设置多组拦截器。 #### 5.1 请求拦截器 ZoraHttpRequestInterceptor ZoraHttpRequestInterceptor是Request拦截器,在callback的`beforeSend(request)`函数调用之前执行。 使用时需要自定义class继承自`ZoraHttpRequestInterceptor`并实现`interceptRequest(request: ZoraHttpRequest): boolean`方法。 在interceptRequest方法中,可以对现有Request做添加默认参数等操作,返回值为boolean,false表示可以继续执行拦截链中的下一个拦截器,true表示中断拦截链不再执行下一个拦截器。 使用场景举例: * 添加默认参数 * 执行发送前的验签操作 * 对已有参数做检测或兼容 * 对数据进行统一封装等 ```typescript class RequestInterceptor extends ZoraHttpRequestInterceptor { //chainName为该拦截器所属的拦截链的名称 constructor(chainName:string) { super(chainName) } interceptRequest(request: ZoraHttpRequest): boolean { // false 继续执行下一个拦截器,true 中断执行链 return false; } } ``` #### 5.2 响应拦截器 ZoraHttpResponseInterceptor ZoraHttpResponseInterceptor是Response拦截器,在callback的`onResponse(request, response,customResult)`函数调用之前执行。 使用时需要自定义class继承自`ZoraHttpResponseInterceptor`并实现`interceptResponse(request: ZoraHttpRequest, response: ZoraHttpResponse): boolean`方法。 在interceptResponse方法中,可以对现有Response做操作,返回值为boolean,false表示可以继续执行拦截链中的下一个拦截器,true表示中断拦截链不再执行下一个拦截器。 ZoraHttpResponse支持设置一个自定义返回对象,例如您收到的返回为Json数据,但希望真正回调时拿到的是Array ,就可以在拦截器中解析生成对应数据后通过`response.setCustomResult(yourObj)`进行设置,数据会跟随onResponse的第三个参数回调给调用方。 使用场景举例: * 统一判断业务数据正确性 * 修改response的数据 * 进行数据对象转换并生成自定义对象返回 * 可配合`ZoraHttpRequestManager`进行token刷新、参数替换、请求重试 ```typescript class ResponseInterceptor extends ZoraHttpResponseInterceptor { //chainName为该拦截器所属的拦截链的名称 constructor(chainName:string) { super(chainName) } interceptResponse(request: ZoraHttpRequest, response: ZoraHttpResponse): boolean { response.setCustomResult('修改后的返回结果') // false 继续执行下一个拦截器,true 中断执行链 return false; } } ``` #### 5.3 拦截器注册 ZoraHttpInterceptorChain 拦截器的注册由`ZoraHttpInterceptorChain`掌管,底层的存储结构为`HashMap>`,Map中的Key代表某一组拦截器的名称,value为存放该组拦截器的数组。 拦截器对象的构造函数中需要传入一个叫`chainName`的参数,也就是HashMap中的Key,框架会根据chainName获取对应的拦截链。 支持根据场景设置多个拦截链,发送请求时指定对应的`chainName`即可。 管理器分别提供Request和Response拦截器的注册方法: ```typescript // 注册2个请求拦截器 ZoraHttpInterceptorChain.addRequestInterceptor(interceptor1); ZoraHttpInterceptorChain.addRequestInterceptor(interceptor2); // 注册2个响应拦截器 ZoraHttpInterceptorChain.addResponseInterceptor(interceptor3); ZoraHttpInterceptorChain.addResponseInterceptor(interceptor4); ``` 拦截器执行顺序由注册顺序决定,按照先入先执行的规则执行。 #### 5.4 发送请求并启用拦截器 ```typescript Zora.http() .get('https://your_url/your/path') .setDesc("拦截器测试请求") .setRequestInterceptor('YourChainName') // 设置使用哪一组请求拦截器 .setResponseInterceptor('YourChainName') // 设置使用哪一组响应拦截器 .send({ beforeSend(request) { // true 表示允许继续发送,false表示立刻结束此次发送 return true; }, onResponse(request, response, customResult) { // 第三个参数就是自定义返回对象,可以在拦截器中设置 Zora.logInfo('onResponse : customResult = ' + JSON.stringify(customResult)) }, onError(request, err) { Zora.logInfo('onError: ' + JSON.stringify(err)) }, afterSend(request) { Zora.logInfo('afterSend:') } }) ``` ### 6. 请求管理器(ZoraHttpRequestManager) 在一些特定场景下我们必须阻断所有当前的请求,要么取消,要么就算拿到Response后也不能进行回调。 **典型场景:** 当授权token过期时,我们必须阻断当前所有的请求,然后去重新获取一个token后将现有执行请求的token参数全部更新后进行重试,在整体操作过程中希望对调用者无感。 这类需求就可以使用拦截器配合`ZoraHttpRequestManager`完成。 首先,使用响应拦截器进行数据分析,当判定token过期时调用`ZoraHttpRequestManager.cancelAndBlockAll();`来停止当前所有请求。 待token更新完毕后调用`ZoraHttpRequestManager.retryAll();`进行所有请求的重新发送!在框架的管理下,请求callback的函数不会被重复调用,在调用者看来只是走了一个正常访问流程。 更新token可以借助请求拦截器,当发现request中的token与本地新token不一致时进行参数值的替换。 ## 二. 运行时权限 每一个Hap都有自己运行时需要的权限,Zora的Permission框架可以轻松满足权限检测及申请; ### 1. 权限检测 ```typescript Zora.permission() //获取权限模块 .needInternet() //使用网络权限 .needCamera() //使用相机权限 .needReadMedia() //使用媒体读取权限 .need('ohos.permission.READ_CALENDAR') //内置的不够,自行添加其他权限 .checkOnly({ // checkOnly意思是只检测不申请 onDenied(permissions) { // 有权限被禁用,返回禁用权限数组 }, allGrant() { // 全部允许 }, onError(err) { // 执行出错了 } }) ``` ### 2. 权限检测并申请 Zora会先对权限进行过滤,把禁止的权限挑选出来再去申请。 ```typescript Zora.permission() //获取权限模块 .needInternet() //使用网络权限 .needCamera() //使用相机权限 .needReadMedia() //使用媒体读取权限 .need('ohos.permission.READ_CALENDAR') //内置的不够,自行添加其他权限 .checkAndRequest({ // 检测并申请被拒绝的权限 onDenied(permissions) { // 如果用户点击了禁止,在所有权限询问完毕后返回禁用权限数组 }, allGrant() { // 用户全部允许 }, onError(err) { // 执行出错了 } }) ``` ## 三. Router模块 ### 1. 打开Page: 说明:路由回调接口,成功的回调统一回调success()方法,失败及异常情况统一使用error()进行回调处理,如遇到回调后接收参数的情况,可通过success(backParam) 方法接受参数,backParam参数类型根据业务自行进行处理即可; ```typescript // 打开当前module内page,直接使用main_pages.json内的路由配置,如:pages/Index import ZoraRouter from '@zora/router' // 打开新page,不销毁当前页 ZoraRouter //获取路由模块 .page('pages/SecondPage') //设置目标页面 .addParam("from", "PageRouter") //添加文本参数 .addParam("obj", { name: "Raven", age: 99, height: 1.9 }) //添加对象参数:方法 1 // .addParams({name:"Raven",age:99,height:1.9}) // 直接全量赋值:方法 2 .start({ //打开页面 success() { //打开成功 }, error(err) { //出错了 } }) // 打开新page,并销毁当前页 ZoraRouter //获取路由模块 .page('pages/SecondPage') //设置目标页面 .addParam("from", "PageRouter") //添加文本参数 .addParam("obj", { name: "Raven", age: 99, height: 1.9 }) //添加对象参数:方法 1 // .addParams({name:"Raven",age:99,height:1.9}) // 直接全量赋值:方法 2 .replace({ //打开页面 success() { //打开成功 }, error(err) { //出错了 } }) // 跨module打开指定module内的page ZoraRouter.page() .setModuleName("discover") // 设置module名 .setUrl("pages/SearchIndex") // 对应module内的page路由 .start() // 或者 也可以使用url进行指定module下的page路由跳转:@bundle:bundle_name/module_name/ets/pages/Index ZoraRouter.page() .setUrl("@bundle:bundle_name/module_name/ets/pages/Index") .start() ``` ### 2. 页面返回 ```typescript // 直接返回 ZoraRouter .page("pages/Index") // 如需返回上一页并携带参数,可添加参数值(可选) .addParam("from", "PageRouter") //添加文本参数 .addParam("obj", { name: "Raven", age: 99, height: 1.9 }) //添加对象参数:方法 1 // .addParams({name:"Raven",age:99,height:1.9}) // 直接全量赋值:方法 2 .back() //弹出询问框后再进行页面返回 ZoraRouter .page() .setUrl("pages/Index") // 如需返回上一页并携带参数,可添加参数值(可选) .addParam("from", "PageRouter") //添加文本参数 .addParam("obj", { name: "Raven", age: 99, height: 1.9 }) //添加对象参数:方法 1 // .addParams({name:"Raven",age:99,height:1.9}) // 直接全量赋值:方法 2 .backAfterAlert("填写询问的提示信息") // 只有点击确认后,才可正常返回 ``` ### 3.打开新page页面时,进行参数校验 ```text // 在项目内(entry or module)模块的resources/rawfile目录下,添加 pages_router.json配置文件,仅配置当前module内使用的路由) // 支持跨module跳转时的规则检测 // 文件内容如下 { "pages": { "pages/Index": { // 需要检查参数的路由配置,与resources/base/profile/main_page.json内的路由配置对应 "require": [ // 检测规则配置对象 "userData:object" // 规则:"参数名:参数类型" ] }, "pages/Explorer": { "require": [ "url" // 检测规则,可以仅设置参数,不进行参数类型的设置 ] } } } // 添加并配置好以上文件后,在调用Zora.router().page()...内的跳转接口,即可在跳转前进行参数规则校验 ``` ### 4. 添加拦截器 ```typescript // 定义拦截器 let intercepter = { // 进行拦截器注册操作 process(routerData: RouterData, callback: InterceptCallback) { if (routerData.getUrl() === 'pages/xxx') { AlertDialog.show( { title: '提示', message: '被拦截了,点击继续跳转', primaryButton: { value: '取消', action: () => { // 拦截处理 callback.onInterrupt(routerData) } }, secondaryButton: { value: '继续', action: () => { // 继续跳转 callback.onContinue(routerData) } }, cancel: () => { // 取消 } } ) } else { callback.onContinue(routerData); } } } // 注册拦截器 ZoraRouter.page(this.data['url']) .registerInterceptor(intercepter) // 注册拦截器 .start({ success: (re) => { // 跳转陈宫 }, error: (re) => { // 跳转异常 } }) // 拦截器忽略 ZoraRouter .page('pages/xxx') .addParam('uid', 123456) .setIgnoreIntercept() // 设置忽略 .start() // 注销拦截器 ZoraRouter .page('pages/xxx') .addParam('uid', 123456) .unregisterInterceptor() .start() // 拦截器可单独进行调用配置 aboutToAppear() { // 注册监听拦截器 ZoraRouter.page().registerInterceptor(interceptor) } aboutToDisappear() { // 注销拦截器监听 ZoraRouter.page().unregisterInterceptor() } ``` ### 5. 打开普通Ability: ```typescript ZoraRouter //获取路由模块 .ability("SecondAbility") //设置目标Ability 参数为可选参数,可使用 .setAbilityName("SecondAbility") 进行参数设置 .addParam("from", "PageRouter") //添加文本参数 .addParam("obj", { name: "Raven", age: 99, height: 1.9 }) //添加对象参数:方法 1 // .addParams({name:"Raven",age:99,height:1.9}) // 直接全量赋值:方法 2 .start({ //打开页面 success() { // 打开成功 }, error(err) { // 出错了 } }) ``` ### 6.启动一个Ability,可接受返回回调,接收返回结果 ```typescript ZoraRouter .ability("abilityName") .setModuleName("moduleName") .addParam("from", "PageRouter") //添加文本参数 .addParam("obj", { name: "Raven", age: 99, height: 1.9 }) //添加对象参数:方法 1 // .addParams({name:"Raven",age:99,height:1.9}) // 直接全量赋值:方法 2 .startForResult({ success(result) { // 接收并处理返回参数result // 接受通过调用.finishWithResult()返回的结果参数 }, error(err) { // 异常处理 } }) ``` ### 7.关闭当前ability ```typescript ZoraRouter.ability().finish({ success: () => { // 调用成功 }, error: (err) => { // 调用失败 } }) ``` ### 8.关闭ability,并返回上一页结果 ```typescript ZoraRouter .ability() .setResultCode(RESULT_CODE) // 设置 ability拉起、销毁之后返回的结果码 .addParam("from", "PageRouter") //设置返回参数:添加文本参数 .addParam("obj", { name: "Raven", age: 99, height: 1.9 }) //设置返回参数:添加对象参数:方法 1 // .addParams({name:"Raven",age:99,height:1.9}) // 设置返回参数:直接全量赋值:方法 2 .finishWithResult({ success: () => { // 调用成功 }, error: (err) => { // 调用异常 } }) ``` ### 9. 打开当前应用系统信息页 ```typescript ZoraRouter.openSystemSettingsForApp() // or ZoraRouter.ability().openSystemSettingsForApp() ``` ### 10. 其他支持的系统页面路由调用 ```typescript // 跳转相册选择图片、视频 ZoraRouter.system() .setMaxSelectNumber(3) // 设置最大选择数量 .setSelectMIMEType(picker.PhotoViewMIMETypes.IMAGE_TYPE) // 选择的类型:IMAGE_TYPE:仅图片,VIDEO_TYPE:仅视频,IMAGE_VIDEO_TYPE:图片和视频 .photoSelect({ success: (result) => { // 选择文件后成功返回 // 处理选择的文件内容 }, error: (err) => { // 调用出现异常时处理 } }) // 跳转文件夹选择音频文件 ZoraRouter.system() .audioSelect({ success: (result) => { // 选择文件后成功返回 // 处理选择的文件内容 }, error: (err) => { // 调用出现异常时处理 } }) // 跳转文件夹选择各种格式文档 ZoraRouter.system() .documentSelect({ success: (result) => { // 选择文件后成功返回 // 处理选择的文件内容 }, error: (err) => { // 调用出现异常时处理 } }) // 打开拨号界面 ZoraRouter.system() .openCall("138********", { success: () => { // 打开成功 }, error: (error) => { // 打开失败 } }) ``` ### 11. 应用间分享数据 在应用使用场景中,会经常需要将一个应用内的数据(如文字、图片等)分享至另一个应用内继续操作: ```typescript ZoraRouter.ability() .setAction(wantConstant.Action.ACTION_SELECT) // ohos.want.action.select(可选,系统会默认自动添加该action) .addShareFileType(fileType) // 用于传递展示信息给应用选择器,应用选择器根据该字段渲染相应的文件类型图标 .addShareFileNames([path]) // 应用选择器根据该字段展示文件名 .addShareFileSizes([fileSize]) // 应用选择器根据该字段展示文件大小。以字节为单位 .addShareNestedIntentAction(wantConstant.Action.ACTION_SEND_DATA) // "ohos.want.action.sendData"(可选,系统会默认自动添加该action) .addShareNestedIntentFileType(fileType) // 可选(系统会自动填充默认:同步.AddShareFileType的文件类型type) .addShareNestedIntentFileFD(fileFd) // 待传递文件的文件描述符(FD) .start() ``` ### 12. 打开摄像头拍照 ```typescript // 需要在 module.json5配置中增加'ohos.permission.CAMERA', 'ohos.permission.READ_MEDIA',两个权限 ZoraRouter.ability() .openCamera({ success: (result) => { // 打开成功,从want参数中获取拍照文件 }, error: (error) => { // 异常处理 } }) ``` ## 四. 日志管理 为日志功能提供开关控制,目前只是封装了简单的日志功能。 ### 1. 日志开关: ```typescript Zora.setLogEnable(true) // 是否打印日志 ``` ### 2. 日志打印: ```typescript Zora.logInfo('Info 级别日志') Zora.logDebug('Debug 级别日志') Zora.logWarn('Warn 级别日志') Zora.logError('Error 级别日志') Zora.logFatal('Fatal 级别日志') ```