【每日学点鸿蒙知识】包体积优化、WebView下载PDF等

时间:2025-01-13 15:45:26

1、打包体积大如何配置优化包体积问题?

问题场景:

含有so库的程序包打包完之后包体积过大,超出预期。

参考答复:

  1. 首先查看打包的类型,debug编译打包含有调试信息相对于release包的体积较大.可以通过配置"strip": true来去除so中的debug信息减小so体积。该配置需要配置在hap和hsp模块,release和debug模式下都可以配置 :
"nativeLib": {
  "debugSymbol": { // 可通过此配置对cpp编译产物so执行strip,移除so中的调试信息与符号表等
    "strip": true, // 执行strip
    "exclude": [] //执行strip的过滤正则表达式规则
  },
  1. 当前DevEco Studio默认打包应用时不压缩so库文件,配置so压缩选项后,DevEco Studio会将so库文件以压缩形式打包到包中,从而减小应用包大小。修改应用模块配置文件module.json5中的compressNativeLibs字段,将值配置为true,重新编译、打包应用。
{
  "module": {
  // ...
  "compressNativeLibs": true // 标识libs库以压缩存储方式打包
 }
}

2、基于WebDownloadDelegate的PDF下载预览

首先用到web的网页拦截接口onInterceptRequest判断该pdf是否需要下载浏览。判断的标识是该url的响应头response header中有没有Content-Disposition属性,如“attachment; filename=XXXX.pdf”。

try {
  if (event) {
    const webResponse = new WebResourceResponse();
    let url = event.request.getRequestUrl()
    let headers = event.request.getRequestHeader()
    if(headers['content-disposition']){
      AlertDialog.show({
        title: '文件下载', cancel: () => {
          console.log('取消下载');
        },
        message: '是否下载', confirm: {
          value: '确认', action: () => {
            this.setDownloadDelegate()
            this.controller.startDownload('https://example.com');
          }
        },

      })
    }
  }
}

如果需要下载后预览则需要使用startDownload()接口发起一个下载,该下载任务也会通过设置的DownloadDelegate来通知app下载的进度。在此之前,先通过setDownloadDelegate()向Web组件注册一个DownloadDelegate来监听页面触发的下载任务。资源由Web组件来下载,Web组件会通过DownloadDelegate将下载的进度通知给应用。

setDownloadDelegate() {
  try {
    this.delegate.onBeforeDownload((webDownloadItem: web_webview.WebDownloadItem) => {
      console.log('will start a download');
      // 传入一个下载路径(沙溪路径),并开始下载。
      webDownloadItem.start((getContext(this) as common.UIAbilityContext).filesDir + '/test.pdf');
    })
    // 下载过程中的回调,通过该回调的参数可以了解下载进度等信息。
    this.delegate.onDownloadUpdated((webDownloadItem: web_webview.WebDownloadItem) => {
      console.log('download update guid: ' + webDownloadItem.getGuid());
    })
    // 下载失败的通知。
    this.delegate.onDownloadFailed((webDownloadItem: web_webview.WebDownloadItem) => {
      console.log('download failed guid: ' + webDownloadItem.getGuid());
    })
    // 下载完成的通知。
    this.delegate.onDownloadFinish(async (webDownloadItem: web_webview.WebDownloadItem) => {
      console.log('download finish guid: ' + webDownloadItem.getGuid());
      let file = fs.statSync((getContext(this) as common.UIAbilityContext).filesDir + '/test.pdf')
      console.log('testTag - size: ' + file.size + 'path:');
    })
    let fileuri = 'file://’ + (getContext(this) as common.UIAbilityContext).filesDir + ‘/test.pdf'
    console.log('testTag - ' + fileuri);
    this.controller.setDownloadDelegate(this.delegate);
    web_webview.WebDownloadManager.setDownloadDelegate(this.delegate)
    this.controller.loadUrl(fileuri)
  } catch (error) {
    console.log(JSON.stringify(error))
  }
}

如果不需要下载,则拿到响应头后通过http.createHttp()创建一个任务,然后再调用request根据URL地址,发起HTTP网络请求,在其异步的回调函数里面设置自定义的响应数据。

let httpRequest = http.createHttp();
let promise = httpRequest.request(url, {
  method: http.RequestMethod.GET,
  connectTimeout: 60000,
  readTimeout: 60000,
  header:headers
});
promise.then((data:http.HttpResponse) => {
  let result = data?.result as ArrayBuffer
  webResponse.setResponseCode(200);
  webResponse.setReasonMessage('OK');
  webResponse.setResponseData(result);
  webResponse.setResponseEncoding('UTF_8')
}).catch((err:Error) => {
  console.info('error:' + JSON.stringify(err));
});

3、项目编译报错TypeError: Cannot read properties of undefined (reading ‘newFileToResourceList’)

  1. hvigor/hvigor-config.json5文件中的logging解开level改成debug,debugging中的stacktrace解开并改成true。从而可以在编译构建报错的时候可以看到具体报错位置,随后排查报错位置是否有问题。
  2. 如果报错位置在collectResourceInFile,需要点进具体报错信息的文件中,并修改系统文件代码添加一行日志,如下:
collectResourceInFile(e,t) {
  if(!this.wholeFileInfo[t]){
    console.log('info'+t);
  }
  this.wholeFileInfo[t].newFileToResourceList.add(e)
}
  1. 然后再重新编译即可在build中看到打印出来的报错文件位置,然后查看该文件导入导出的路径是否有报黄预警,从而排查路径问题(常见路径中大小写、文件路径缺失等) 。

4、异步是否对主线程有影响?

问题描述
1、setInterval 是异步执行的吗?频繁通过setInterval计时是否会影响主线程以及屏幕刷新率;
2、如何自主判断是异步接口?
3、如何在任务中查看当前是否是在主线程还是在其他线程?

解答
1、setinterval是异步的。是否影响主线程,要看setInterval的callback中做了什么(如果仅是少量数据处理,问题不大,如果是大量数据处理,建议使用worker或taskpool。如果是给页面显示的数据重新赋值,过于频繁的确会影响主线程,毕竟绘制是在主线程处理的)
2、HarmonyOS里的异步并发实现有Promise和async/await,有相关实现的就可以理解为该接口是异步接口,详情可以参考文档:异步并发概述 (Promise和async/await)
3、通过process.tid 获取线程id,然后根据线程id 调用不同的接口可以获取线程相关信息。可以通过此方法判断主次线程:

function isMainThread(): boolean {
  return process.pid == process.tid
}

5、如何在HSP模块切换根视图?

如何在HSP模块切换根视图,是否需要在HSP模块拿到程序入口的WindowStage进行切换,具体怎么实现?

在HSP页面中,通过windowStage对象的getMainWindowSync接口获取主窗口,然后使用pushUrl跳转到跟页面,在跟页面通过router.pushNamedRoute切换到HSP页面。具体请参考示例代码:

HspPage页面Index代码:

import { window } from '@kit.ArkUI';

@Entry({ routeName: "HspPage" })
@Component
struct Index {
  @State message: string = 'This is Hsp Page';
  @State windowStage: window.WindowStage | undefined = AppStorage.get<window.WindowStage>("windowStage")

  build() {
    Row() {
      Column() {
        Text(this.message)
          .fontSize(50)
          .fontWeight(FontWeight.Bold)
          .onClick(() => {
            if (this.windowStage) {
              this.windowStage.getMainWindowSync()
                .getUIContext()
                .getRouter()
                .pushUrl({
                  url: "pages/Page"
                })
            }
          })
      }
      .width('100%')
    }
    .height('100%')
  }
}

根页面page1

import { router } from '@kit.ArkUI';

import("lib1/src/main/ets/pages/Index")

@Entry
@Component
struct Index {
  @State message: string = 'Hello World';

  build() {
    RelativeContainer() {
      Text(this.message)
        .id('HelloWorld')
        .fontSize(50)
        .fontWeight(FontWeight.Bold)
        .alignRules({
          center: { anchor: 'container', align: VerticalAlign.Center },
          middle: { anchor: 'container', align: HorizontalAlign.Center }
        })
        .onClick(() => {
          console.log("clicked")
          router.pushNamedRoute({
            name: "HspPage"
          })
        })
    }
    .height('100%')
    .width('100%')
  }
}

根页面page2

import { router } from '@kit.ArkUI';

import("lib1/src/main/ets/pages/Index")

@Entry
@Component
struct Index {
  @State message: string = 'Hello World';

  build() {
    RelativeContainer() {
      Text(this.message)
        .id('HelloWorld')
        .fontSize(50)
        .fontWeight(FontWeight.Bold)
        .alignRules({
          center: { anchor: 'container', align: VerticalAlign.Center },
          middle: { anchor: 'container', align: HorizontalAlign.Center }
        })
        .onClick(() => {
          console.log("clicked")
          router.pushNamedRoute({
            name: "HspPage"
          })
        })
    }
    .height('100%')
    .width('100%')
  }
}