[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:
-
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
},
-
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 filemodule.json5
totrue
, then recompile and package the app:
{
"module": {
// ...
"compressNativeLibs": true // Indicate that libs should be packaged in compressed storage 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');
}
},
})
}
}
}
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))
}
}
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));
});
3. Project Compilation Error: TypeError: Cannot read properties of undefined (reading 'newFileToResourceList')
-
Enable detailed logging: Uncomment the
logging
section inhvigor/hvigor-config.json5
and setlevel
todebug
. Uncomment thedebugging
section and setstacktrace
totrue
to see the specific error location during compilation. -
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)
}
- 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:
- Is
setInterval
executed asynchronously? Does frequent timing viasetInterval
affect the main thread and screen refresh rate? - How to independently determine if an interface is asynchronous?
- How to check if a task is running on the main thread or another thread?
Solutions:
-
setInterval
is asynchronous. Its impact on the main thread depends on the callback logic:- Minor data processing: Minimal impact.
- Heavy data processing: Use
worker
ortaskpool
. - Frequent UI data updates: May affect the main thread, as rendering occurs on the main thread.
-
Asynchronous interfaces in HarmonyOS are implemented via
Promise
andasync/await
. Refer to the Asynchronous Concurrency Overview (Promise and async/await). - Check thread type by comparing thread IDs:
function isMainThread(): boolean {
return process.pid === process.tid;
}
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%')
}
}
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%')
}
}
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%')
}
}
Top comments (0)