HarmonyOS NEXT-开发问题汇总

一、问题汇总

1. Ability跳转白屏问题

微信截图_20231215111949.png
微信截图_20231215112156.png
微信截图_20231215112305.png
微信截图_20231215112323.png

原因分析: 在配置文件中未配置相关新创建的ability
解决方案: 添加配置文件如下图:

image.png

2. 相机无法预览问题

使用官方示例代码,集成后无法预览相机界面?
关键问题异常函数:

cameraManager.createPreviewOutput(previewProfilesArray[0], surfaceId);

问题原因:
由于官方示例代码默认使用的是previewProfilesArray[0],所以获取的宽高和下面组件的宽高不一致,这里建议通过动态获取方式去做,如下位置设置宽高和此函数不一致导致无法预览问题:

XComponent({
            id: '',
            type: 'surface',
            libraryname: '',
            controller: this.mXComponentController
          })
            .onLoad(() => {
              // 设置Surface宽高(1920*1080),预览尺寸设置参考前面 previewProfilesArray 获取的当前设备所支持的预览分辨率大小去设置
              // 预览流与录像输出流的分辨率的宽高比要保持一致
              this.mXComponentController.setXComponentSurfaceSize({
                // surfaceWidth: displayWidth,
                // surfaceHeight: displayHeight
                surfaceWidth: 1920,   // 此处宽高要和异常函数一致
                surfaceHeight: 1080
              });
              // 获取Surface ID
              this.surfaceId = this.mXComponentController.getXComponentSurfaceId();
      
            })
            .width('100%')
            .height('100%')

解决方案:
①在page界面初始化函数中,优先获取previewProfilesArray(具体方法参考官方文档):

 // 获取相机设备支持的输出流能力
  let cameraOutputCap: camera.CameraOutputCapability = cameraManager.getSupportedOutputCapability(cameraArray[0]);
  if (!cameraOutputCap) {
    console.error("cameraManager.getSupportedOutputCapability error");
    return;
  }
  console.info(TAG + "outputCapability: " + JSON.stringify(cameraOutputCap));

  let previewProfilesArray: Array<camera.Profile> = cameraOutputCap.previewProfiles;
  if (!previewProfilesArray) {
    console.error("createOutput previewProfilesArray == null || undefined");
  }

②获取完成后使用你想适配的分辨率去设置XComponent中的surfaceWidth和surfaceHeight(这里的page初始方法onPageShow()和aboutToAppear()优先级是高于XComponent组件的加载的)。
比如我使用previewProfilesArray[26]中的width和height也就是1920*1080,如下图:

cameraManager.createPreviewOutput(previewProfilesArray[26], surfaceId);

③ 设置对应surface宽高:

XComponent({
            id: '',
            type: 'surface',
            libraryname: '',
            controller: this.mXComponentController
          })
            .onLoad(() => {
              // 设置Surface宽高(1920*1080),预览尺寸设置参考前面 previewProfilesArray 获取的当前设备所支持的预览分辨率大小去设置
              // 预览流与录像输出流的分辨率的宽高比要保持一致
              this.mXComponentController.setXComponentSurfaceSize({
                // surfaceWidth: displayWidth,
                // surfaceHeight: displayHeight
                surfaceWidth: 1920,   // 此处宽高要和异常函数一致
                surfaceHeight: 1080
              });
              // 获取Surface ID
              this.surfaceId = this.mXComponentController.getXComponentSurfaceId();
      
            })
            .width('100%')
            .height('100%')

这时候我们使用1920*1080的分辨率就可以成功预览,如果想使用其他分辨率也可以用同样的方式,前提是必须在previewProfilesArray里面支持的范围内去选择。

3.进入相机界面偶发无法预览问题

解决了问题2中的预览问题,后续发现会出现偶发的无法预览问题,经过定位是获取的surfaceId没有及时获取到,就对相机预览进行了配置,导致了相机配置到空的surfaceId导致初始化失败。如下图:


成功预览情况下
预览失败的情况

可以看出成功预览是有id的,而偶发的失败情况是没有id的,所以分析到和page的生命周期和函数的使用有关系.

关键问题代码:
可以看到我是在pageshow这个方法中使用了异步处理的方式进行了权限申请和校验,也计时说预览和拍照的方法配置是不同步的。

权限申请异步预览配置

下面的截图是surface预览ui的id获取,可以看到XComponent中的onLoad函数也是通过回调异步方式。


微信截图_20231226143828.png

以上两个关键点就分析出来了,同样的异步操作,会导致偶发性先申请权限成功配置了相机,而XComponent中获取surfaceId的函数还没有成功回调,这样就导致我们使用了空的id去配置了相机。问题原因找到了,下面是优化后的解决方法:

 onPageShow() {
    console.debug(TAG + "onPageShow")
    // 获取屏幕宽高
    getDisplay();
    // TODO 这里我们删除了申请权限和相机配置
  }

然后我们重新在onLoad方法中添加:

.onLoad(() => {
              // 设置Surface宽高(1920*1080),预览尺寸设置参考前面 previewProfilesArray 获取的当前设备所支持的预览分辨率大小去设置
              // 预览流与录像输出流的分辨率的宽高比要保持一致
              this.mXComponentController.setXComponentSurfaceSize({
                // surfaceWidth: displayWidth,
                // surfaceHeight: displayHeight
                surfaceWidth: 1920,
                surfaceHeight: 1080
              });
              // 获取Surface ID
              this.surfaceId = this.mXComponentController.getXComponentSurfaceId();
              // 权限校验
              grantPermission().then(res => {
                console.info(TAG + TAG, `权限申请成功 ${JSON.stringify(res)}`);
                if (res) {
                  //预览和拍照
                  cameraShootingCase(getContext(this), this.surfaceId);
                }
              })
            })

到此解决了偶发性的预览问题。

4.闪光灯设置问题FLASH_MODE_OPEN无效

.onClick(() => {
              console.info(TAG + 'light onClick')
              try {
                if (this.flashsrc) {
                  this.flashsrc = false;
                  // 关闭闪光灯
                  if (captureSession != undefined) {
                    captureSession.setFlashMode(camera.FlashMode.FLASH_MODE_CLOSE);
                  }
                } else {
                  this.flashsrc = true;
                  // 开启闪光灯
                  if (captureSession != undefined) {
                      //FLASH_MODE_OPEN无法开启闪光灯
                    captureSession.setFlashMode(camera.FlashMode.FLASH_MODE_OPEN);
                  }
                }
              } catch (error) {
                let err = error as BusinessError;
                console.error(TAG + 'Failed to set the flash mode. errorCode = ' + err.code);
              }
              //this.context.eventHub.emit('onclick',1);
            })

我们将FLASH_MODE_OPEN改为FLASH_MODE_ALWAYS_OPEN即可

   captureSession.setFlashMode(camera.FlashMode.FLASH_MODE_ALWAYS_OPEN);

具体原因暂时不清楚可能是官方bug.

5. 使用Flex与组件配合使用宽度不正常问题

已解决:
① 添加 justifyContent: FlexAlign.SpaceEvenly , alignItems: ItemAlign.Center

 // 正反面配置
        Flex({ direction: FlexDirection.Row , justifyContent: FlexAlign.SpaceEvenly , alignItems: ItemAlign.Center}) {
          MySelectButton({ buttonText: $r('app.string.idcard_font') })
            .onClick(() => {
            });

          MySelectButton({ buttonText: $r('app.string.idcard_side') })
            .onClick(() => {
            });

        }.width('100%')

② 删除自定义组件的宽度

// 切换按钮组件
@Component
struct MySelectButton {
  private buttonText: Resource = $r('app.string.idcard_font');
  @State isSelect: boolean = false; // 选中状态

  setSelect(select: boolean) {
    this.isSelect = select;
  }

  build() {
    Button({ type: ButtonType.Normal, stateEffect: true }) {
      Text(this.buttonText)
        .fontColor(this.isSelect == true ? $r('app.color.theme_color') : $r('app.color.galy_color'))
        .fontSize(17)
    }
    // .width('50%')    ******************删除此宽度解决
    .height(60)
    .borderRadius(8) // 圆角
    .backgroundColor(Color.White)
    // .border({ width: 1 })
    // .borderColor($r('app.color.galy_color')) // 描边
    .padding(15)
    .margin(8)
    .shadow({ radius: 10, color: Color.Gray }) // 阴影
  }
}

6. 使用imageArrival 视频流回调自动终止问题

如下代码使用: nextImage.release(); 进行释放即可

/**
 * 视频流帧数据回调
 */
function onImageArrival(receiver: image.ImageReceiver): void {
  receiver.on('imageArrival', () => {
    receiver.readNextImage((err: BusinessError, nextImage: image.Image) => {
      if (err || nextImage === undefined) {
        console.warn(TAG + "onImageArrival  readNextImage err:" + err.code)
        return;
      }
      console.warn(TAG + "readNextImage")
      nextImage.getComponent(image.ComponentType.JPEG, (err: BusinessError, imgComponent: image.Component) => {
        console.warn(TAG + "getComponent")
        if (err || imgComponent === undefined) {
          console.warn(TAG + "onImageArrival getComponent err:" + err.code)
          return;
        }
        let buffer: ArrayBuffer;
        if (imgComponent.byteBuffer as ArrayBuffer) {
          buffer = imgComponent.byteBuffer;
          console.warn(TAG + "onImageArrival")
          nextImage.release(); // 必须进行释放否则无法持续获取帧数据
        } else {
          console.warn(TAG + "onImageArrival return")
          nextImage.release();
          return;
        }
        // do something...;

      })
    })
  })
}

7. 如何将raw下的文件保存至沙盒?

*注意保存txt文档和其他类型或者加密类型文件有略微区别,具体需要看下官方的实现方式
这里举例使用了加密的.mnn文件进行演示

/**
   * 初始化文件(流)
   */
  public static async initFilesStream(): Promise<void> {
    context.resourceManager.getRawFd("etcard_obj_moblie.mnn", async (error, value) => {
      if (error != null) {
        console.error(`callback getRawFd failed error code: ${error.code}, message: ${error.message}.`);
      } else {
        let fd = value.fd;
        let offset = value.offset;
        let length = value.length;
        let filesDir = context.filesDir;
        // 打开文件流
        let inputStream = fs.fdopenStreamSync(fd, "r+");
        // let inputStream = fs.createStreamSync(filesDir + '/test.txt', 'r+');
        let outputStream = fs.createStreamSync(filesDir + '/etcard_obj_moblie.mnn', "w+");
        // 以流的形式读取源文件内容并写入目的文件
        let bufSize = 1024;
        let readSize = 0;
        let buf = new ArrayBuffer(bufSize);

        class Option {
          public offset: number = 0;
          public length: number = bufSize;
        }

        let option = new Option();
        option.offset = value.offset;
        if (bufSize > value.length) {
          option.length = value.length;
        }
        let readLen = await inputStream.read(buf, option);
        readSize += readLen;
        while (readLen > 0) {
          await outputStream.write(buf);
          option.offset = readSize + value.offset;
          option.length = value.length - readSize >= bufSize ? bufSize : value.length - readSize;
          readLen = await inputStream.read(buf, option);
          readSize += readLen;
        }
        // 关闭文件流
        inputStream.closeSync();
        outputStream.closeSync();
      }
    });
  }

8. napi中使用OH_LOG_ERROR等函数打印失效

引入的log.h头文件与LOG_TAG宏定义必须定义后才可以打印效果。


image.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,245评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,749评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,960评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,575评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,668评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,670评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,664评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,422评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,864评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,178评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,340评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,015评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,646评论 3 323
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,265评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,494评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,261评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,206评论 2 352

推荐阅读更多精彩内容