DEV Community

Cover image for [Learn HarmonyOS Next Knowledge Daily] Package Size Optimization, WebView PDF Download, etc.
kouwei qing
kouwei qing

Posted on

[Learn HarmonyOS Next Knowledge Daily] Package Size Optimization, WebView PDF Download, etc.

[Learn HarmonyOS Next Knowledge Daily] Package Size Optimization, WebView PDF Download, etc.

1. How to Configure Package Size Optimization for Large Packaging?

Problem Scenario:

The package size of a program containing SO libraries is excessively large after packaging, exceeding expectations.

Reference Response:

  1. Check the packaging type first: Debug compilation includes debugging information, making the package larger than the release version. You can reduce the SO size by configuring "strip": true to remove debug information from SO files. This configuration should be applied to both HAP and HSP modules, and works for both release and debug modes. Refer to the configuration guide:
"nativeLib": {
  "debugSymbol": { // This configuration strips debug information and symbol tables from compiled C++ SO files
    "strip": true, // Enable stripping
    "exclude": [] // Regular expression rules for stripping exclusions
  },
Enter fullscreen mode Exit fullscreen mode
  1. Compress SO libraries: By default, DevEco Studio does not compress SO files during packaging. Enabling the SO compression option will package SO files in a compressed format, reducing the app package size. Modify the compressNativeLibs field in the module configuration file module.json5 to true, then recompile and package the app:
{
  "module": {
  // ...
  "compressNativeLibs": true // Indicate that libs should be packaged in compressed storage mode
 }
}
Enter fullscreen mode Exit fullscreen mode

2. PDF Download and Preview Based on WebDownloadDelegate

First, use the web page interception interface onInterceptRequest to determine if the PDF needs to be downloaded and viewed. The identifier is whether the URL's response header contains the Content-Disposition attribute, such as "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: 'File Download', cancel: () => {
          console.log('Cancel download');
        },
        message: 'Download now?', confirm: {
          value: 'Confirm', action: () => {
            this.setDownloadDelegate()
            this.controller.startDownload('https://example.com');
          }
        },
      })
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

To preview the PDF after download, use the startDownload() interface to initiate a download task, which will notify the app of the download progress via the configured DownloadDelegate. First, register a DownloadDelegate with the Web component using setDownloadDelegate() to listen for download tasks triggered by the page. The Web component handles the download and notifies the app of the progress through DownloadDelegate:

setDownloadDelegate() {
  try {
    this.delegate.onBeforeDownload((webDownloadItem: web_webview.WebDownloadItem) => {
      console.log('Will start a download');
      // Specify a download path (sandbox path) and start downloading
      webDownloadItem.start((getContext(this) as common.UIAbilityContext).filesDir + '/test.pdf');
    })
    // Callback during download, providing progress information
    this.delegate.onDownloadUpdated((webDownloadItem: web_webview.WebDownloadItem) => {
      console.log('Download update guid: ' + webDownloadItem.getGuid());
    })
    // Notification for download failure
    this.delegate.onDownloadFailed((webDownloadItem: web_webview.WebDownloadItem) => {
      console.log('Download failed guid: ' + webDownloadItem.getGuid());
    })
    // Notification for download completion
    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))
  }
}
Enter fullscreen mode Exit fullscreen mode

If download is unnecessary, create an HTTP task using http.createHttp(), then initiate an HTTP request with request. Set custom response data in the asynchronous callback:

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));
});
Enter fullscreen mode Exit fullscreen mode

3. Project Compilation Error: TypeError: Cannot read properties of undefined (reading 'newFileToResourceList')

  1. Enable detailed logging: Uncomment the logging section in hvigor/hvigor-config.json5 and set level to debug. Uncomment the debugging section and set stacktrace to true to see the specific error location during compilation.
  2. Modify system file code: If the error occurs in collectResourceInFile, add a log line to the specific error file:
collectResourceInFile(e, t) {
  if (!this.wholeFileInfo[t]) {
    console.log('info' + t);
  }
  this.wholeFileInfo[t].newFileToResourceList.add(e)
}
Enter fullscreen mode Exit fullscreen mode
  1. Recompile to view the printed error file location in the build output. Check for yellow warnings in import/export paths (common issues include case sensitivity and missing paths).

4. Does Asynchrony Affect the Main Thread?

Problem Description:

  1. Is setInterval executed asynchronously? Does frequent timing via setInterval affect the main thread and screen refresh rate?
  2. How to independently determine if an interface is asynchronous?
  3. How to check if a task is running on the main thread or another thread?

Solutions:

  1. setInterval is asynchronous. Its impact on the main thread depends on the callback logic:
    • Minor data processing: Minimal impact.
    • Heavy data processing: Use worker or taskpool.
    • Frequent UI data updates: May affect the main thread, as rendering occurs on the main thread.
  2. Asynchronous interfaces in HarmonyOS are implemented via Promise and async/await. Refer to the Asynchronous Concurrency Overview (Promise and async/await).
  3. Check thread type by comparing thread IDs:
function isMainThread(): boolean {
  return process.pid === process.tid;
}
Enter fullscreen mode Exit fullscreen mode

5. How to Switch Root Views in an HSP Module?

To switch root views in an HSP module, obtain the WindowStage of the program entry and use it for switching. Refer to the sample code:

HspPage Index Code:

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%')
  }
}
Enter fullscreen mode Exit fullscreen mode

Root Page 1:

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%')
  }
}
Enter fullscreen mode Exit fullscreen mode

Root Page 2:

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%')
  }
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)