【高心星出品】

鸿蒙6.0应用开发面试——应用程序包问题

HAR与HSP包的区别是什么?

HAR(静态共享包)
 编译时打包:代码和资源在构建阶段被完整复制到每个依赖它的HAP中。
 启动即加载:随应用启动直接载入内存,调用时无额外开销。
 适用场景:少量模块引用、对加载速度敏感的场景(如基础工具库)。
HSP(动态共享包)
  运行时共享:仅存储一份实例,多个HAP在运行时动态加载同一份HSP。
  按需加载:首次调用需查找、初始化,有性能损耗但避免重复拷贝。
  适用场景:被大量HAP引用的公共模块(如UI组件库),可显著减少包体积。

HSP打包后,为什么会生成HAR包,它是否会导致App包大小膨胀?

HSP编译生成的HAR包仅包含配置文件和接口定义,不包含代码逻辑。该HAR包仅用于开发阶段,不会影响App包的大小。

从包管理的角度,保证代码安全的措施有哪些?

编译:编译时,HAR和HSP支持代码混淆。
打包:打包时为每个HSP/HAP单独签名,签名后的应用才允许安装。
安装:终端设备上的应用市场用于安装和卸载应用,不支持其他安装方式。
运行时:提供应用沙箱机制,这是一种以安全防护为目的的隔离机制,防止数据遭受恶意路径穿越访问。

HSP/HAR包中如何引用外部编译的so库文件?

  1. libxxx.so库文件放入HAR或HSP的libs/arm64-v8a目录。设备类型不同时,需添加对应子目录。新版的arm64为libs/arm64-v8a,老版的arm64为libs/armeabi-v7a,x86模拟器为libs/x86_64。

    在这里插入图片描述

  2. 在src/main/cpp/CMakeLists.txt文件中链接so库文件。例如:target_link_libraries(entry PUBLIC libxxx)

如何判断应用可被卸载?

1.使用bundleManager.getApplicationInfo获取应用程序信息。
2.ApplicationInfo具有removable属性,可用于判断应用是否可卸载。

Entry模块的HAP和Feature模块的HAP在使用和功能上的区别是什么?

1.Entry类型的HAP:是应用的主模块,在module.json5配置文件中的type标签配置为“entry”类型。在同一个应用中,同一设备类型只支持一 个Entry类型的HAP,通常用于实现应用的入口界面、入口图标、主特性功能等。
2.Feature类型的HAP:是应用的动态特性模块,在module.json5配置文件中的type标签配置为“feature”类型。一个应用程序包可以包含一个或多个Feature类型的HAP,也可以不包含;Feature类型的HAP通常用于实现应用的特性功能,可以配置成按需下载安装,也可以配置成随Entry类型的HAP一起下载安装。

HSP包编译之后会生成什么文件?

HSP包编译后会生成.hsp文件和.har文件。.hsp文件用于安装,.har文件仅暴露接口,不包含具体实现。

HSP包中导出的方法头文件位于.har文件中,实现在.hsp文件中。

HAR如何转换为HSP?

HAR转为HSP主要是通过修改配置文件实现。具体步骤如下:

  1. 在HAR的module.json5中,将type字段的值改为“shared”,并配置deliveryWithInstall字段为“true”。
  2. 若HSP需要对外声明可跳转的页面,在module.json5文件中添加pages字段,并在“resources/base”目录下创建“profile/main_pages.json”文件,配置“src”。
  3. 将HAR的hvigorfile.ts文件中的“harTasks”更改为“hspTasks”。
  4. HAR的build-profile.json5文件中默认生成consumerFiles字段,该项字段HAR可配置,为默认导出的混淆加固规则,需要删除。

一个HSP模块如何快速切换成HAR模块?

1.在HSP下的module.json5中,把"type": “shared"修改为"type”: “har”,删除"deliveryWithInstall"、"pages"字段;

2.删除HSP下的oh-package.json5中"packageType"字段;

3.删除HSP中的页面,如果要以页面的形式使用到的话,就需要改为命名路由的写法;

4.然后再找到HSP下的hvigorfile.ts文件,将里面的hspTasks改为harTasks;

5.最后编译该模块即可。

编译过程中遇到其他错误时,根据提示找到对应位置并进行修改。

如何实现跨模块的页面跳转功能?

在业务体系庞大或复杂的情况下,会将业务拆分成多个子业务模块,每个子业务模块为一个HAR/HSP。在该场景下,存在从主业务入口跳转到不同子页面模块,或从一个子业务模块A页面跳转到另一个子业务模块B页面的需求。例如,从应用首页跳转到登录子业务模块页面。针对该场景,有以下三种解决方案:

  • 方案一:使用router的命名路由接口router.pushNamedRoute()跳转。

  • 方案二:使用navigation组件跳转。

    以从应用入口模块的页面NavigationPage跳转到Login子业务模块页面LoginPage为例。主要包含以下步骤:

    1. 在Login模块中开发自定义组件LoginPage作为路由跳转目的地,并对外导出。

      @Component
      export struct LoginPage {
        @Consume('pathStack') pathStack: NavPathStack;
        @State message: string = 'Login Page';
      
        build() {
          NavDestination() {
            Column() {
              Text(this.message)
                .fontSize(50)
                .fontWeight(FontWeight.Bold)
            }
            .width('100%')
            .height('100%')
          }
          .onBackPressed(() => {
            this.pathStack.pop();
            return true;
          })
        }
      }
      

      代码逻辑走读:

      1. 组件定义:使用@Component装饰器定义了一个名为LoginPage的结构体组件。
      2. 依赖注入:通过@Consume('pathStack')注解,组件从其父组件中获取NavPathStack实例,用于页面导航。
      3. 状态声明:使用@State声明了一个字符串类型的状态变量message,初始值为'Login Page'
      4. UI构建:在build方法中,使用NavDestination构建导航目标,内部包含一个Column布局。
      5. 文本显示:在Column中,使用Text组件显示message变量的值,并设置字体大小为50,字体加粗。
      6. 布局设置:设置Column的宽度和高度为100%。
      7. 返回按钮处理:使用.onBackPressed方法注册返回按钮的事件处理函数,当返回按钮被按下时,调用pathStack.pop()方法返回到上一个页面,并返回true表示事件已被处理。
    2. 在Login模块的入口文件Index.ets中导出自定义组件。

      export { LoginPage } from './src/main/ets/pages/loginPage';
      
    3. 在入口模块的oh-package.json5文件中添加对Login模块的依赖项。

      {
        // ...
        "dependencies": {
          "@ohos/login": "file:../LoginModule"
        }
      }
      
    4. 入口模块导入Login模块的自定义组件,并添加到Navigation组件的路由表中。

      // Import a custom component of the Login module
      import { LoginPage } from '@ohos/login';
      
      @Entry
      @Component
      struct NavigationPage {
        @Provide('pathStack') pathStack: NavPathStack = new NavPathStack();
      
        @Builder
        pageMap(name: string) {
          if (name === 'loginPage') {
            LoginPage()
          }
        }
      
        build() {
          Navigation(this.pathStack) {
            Button('jump to login page')
              .onClick(() => {
                // The second parameter of NavPathInfo is a custom parameter that can be used for message transfer
                let pathInfo: NavPathInfo = new NavPathInfo('loginPage', new Object());
                this.pathStack.pushDestination(pathInfo, true);
              })
          }
          .navDestination(this.pageMap)
        }
      }
      

      代码逻辑走读:

      1. 导入自定义组件
        • 代码首先导入了LoginPage组件,这是从@ohos/login模块中导入的。
      2. 定义NavigationPage组件
        • 使用@Entry@Component装饰器定义了一个名为NavigationPage的组件。
      3. 提供pathStack
        • 使用@Provide装饰器定义了一个名为pathStackNavPathStack实例,用于管理页面导航路径。
      4. 定义pageMap方法
        • 使用@Builder装饰器定义了一个名为pageMap的方法,该方法根据传入的name参数决定返回哪个组件。如果name'loginPage',则返回LoginPage组件。
      5. 构建UI
        • build方法中,使用Navigation组件和Button组件构建了一个UI。
        • Navigation组件使用pathStack来管理导航路径。
        • Button组件显示一个按钮,点击后会创建一个NavPathInfo对象,并使用pathStack.pushDestination方法导航到loginPage
      6. 设置导航目标
        • 使用.navDestination(this.pageMap)设置导航的目标为pageMap方法返回的组件。
  • 方案三:使用基于navigation组件的自定义路由框架跳转。

    方案二虽然可以实现跨模块跳转的功能,但当模块间跳转需求增多,各个模块间将存在非常复杂的依赖关系,甚至会导致多个HAR/HSP间循环依赖。为了解决模块间的强耦合关系,并且提升页面加载性能。

如何在应用内共享HSP?

如需在应用内共享HSP,请将HSP共享包上传至私仓。动态共享包HSP不能直接发布在私仓内,需要先转换为.tgz包。请按以下操作编译生成*.tgz包。

  1. 将编译模式设为release。

    在这里插入图片描述

  2. 选中HSP模块的根目录,点击Build > Make Module {libraryName},启动构建。

    在这里插入图片描述

  3. 构建完成后,build目录下生成HSP包产物,其中.tgz用来上传至私仓。

在这里插入图片描述

  1. 上传到仓库,然后使用 ohpm install 命令将依赖安装到工程的oh-package.json5文件的dependencies字段中,即可查看对外共享的 HSP 方法。
Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐