# 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 级别日志')
```