1. 自我介绍
看表达能力、沟通能力
面试记录:
2. 进一步挖掘
2.1. 现状
目前是在职还是离职,如果离职,从上一家公司离职的原因
2.2. 项目经验
如果自我介绍工作项目经验讲的不够清楚,可以根据简历上的信息再进一步了解
面试记录:
3. 鸿蒙技术问题
3.1. ArkTS
3.1.1. 基础
3.1.1.1. 基本数据类型有哪些
|------|-----------|----------------------------------------------------------------------------------------|
| | 数据类型 | 概述 |
| 基本类型 | number | 数值 |
| 基本类型 | boolean | 布尔 |
| 基本类型 | string | 字符串 |
| | array | |
| | Record | |
| | Object | Object类型是所有引用类型的基类型。任何值,包括基本类型的值(它们会被自动装箱),都可以直接被赋给Object类型的变量。 |
| | object | object类型则用于表示除非基本类型外的类型。 |
| | enum | 枚举 |
| | Union | union类型,即联合类型,是由多个类型组合成的引用类型。联合类型包含了变量可能的所有类型。 typeAnimal = Cat | Dog | Frog | number; |
| | undefined | 一个未定义或不存在的值 |
| | null | 空 |
| | object | 对象 |
| | Symbol | 独一无二的值 |
| | bigint | 任意大的整数 |
| | any | 任意 |
| | unknown | 未知类型 |
| | void | 没有任何返回值的类型 |
| | never | 永远不存在的类型 |
| | NaN | parseInt一个非数字字符串,不会走到catch,会直接返回一个NaN |
不用讲全,可以引导讲一下常用的用法和注意事项
延伸问题
- 如Map不能转json,要用Record
- 枚举比较运算符需要用"===",否则不会返回true
- if条件中,数值类型0会返回false
3.1.1.2. let 和 const区别
|----|----------------------|---------|
| 区别 | let | const |
| 类型 | 声明变量 | 声明常量 |
| 赋值 | 可以被多次赋值,最后一次赋值覆盖前面的值 | 只能被赋值1次 |
3.2. 并发
为了提升应用的响应速度与帧率,避免耗时任务对UI主线程的影响,ArkTS提供了异步并发和多线程并发两种处理策略。
- 异步并发是指异步代码在执行到一定程度后会被暂停,以便在未来某个时间点继续执行,这种情况下,同一时间只有一段代码在执行。ArkTS通过Promise和async/await提供异步并发能力,适用于单次I/O任务的开发场景。详细请参见使用异步并发能力。
- 多线程并发允许在同一时间段内同时执行多段代码。在UI主线程继续响应用户操作和更新UI的同时,后台线程也能执行耗时操作,从而避免应用出现卡顿。ArkTS通过TaskPool和Worker提供多线程并发能力,适用于耗时任务等并发场景。详细请参见多线程并发概述。
3.2.1. 异步并发
Promise async/await
使用场景
用法简单描述
对于异步并发的理解:不开启新线程
3.2.2. 多线程并发
3.2.2.1. TaskPoll和Worker的区别?
TaskPool和Worker均支持多线程并发能力。由于TaskPool的工作线程会绑定系统的调度优先级,并且支持负载均衡(自动扩缩容),而Worker需要开发者自行创建,存在创建耗时以及不支持设置调度优先级,故在性能方面使用TaskPool会优于Worker,因此大多数场景推荐使用TaskPool。
TaskPool偏向独立任务维度,该任务在线程中执行,无需关注线程的生命周期,超长任务(大于3分钟且非长时任务)会被系统自动回收;而Worker偏向线程的维度,支持长时间占据线程执行,需要主动管理线程生命周期。
延伸问题
TaskPool线程间通信,假如传递的是一个对象,这个对象的父类属性和方法在另外一个线程能调用到吗?
答案:调用不到,因为线程间传递对象是,把对象序列化了,序列化再反序列化后,父类的属性和方法就丢失了。
3.2.3. 数据持久化
HarmonyOS标准系统支持典型的存储数据形态,包括用户首选项、键值型数据库、关系型数据库。
开发者可以根据如下功能介绍,选择合适的数据形态以满足自己应用数据的持久化需要。
- 用户首选项(Preferences):通常用于保存应用的配置信息。数据通过文本的形式保存在设备中,应用使用过程中会将文本中的数据全量加载到内存中,所以访问速度快、效率高,但不适合需要存储大量数据的场景。
- 键值型数据库(KV-Store):一种非关系型数据库,其数据以"键值"对的形式进行组织、索引和存储,其中"键"作为唯一标识符。适合很少数据关系和业务关系的业务数据存储,同时因其在分布式场景中降低了解决数据库版本兼容问题的复杂度,和数据同步过程中冲突解决的复杂度而被广泛使用。相比于关系型数据库,更容易做到跨设备跨版本兼容。
- 关系型数据库(RelationalStore):一种关系型数据库,以行和列的形式存储数据,广泛用于应用中的关系型数据的处理,包括一系列的增、删、改、查等接口,开发者也可以运行自己定义的SQL语句来满足复杂业务场景的需要。
3.3. ArkUI
3.3.1. ArkUI框架有哪些特点?
ArkUI框架是鸿蒙(HarmonyOS)中的一个重要组件框架,具有以下几个特点:
- 组件树结构 : ArkUI框架通过布局组件和基础组件构建界面描述树(组件树),其中基础组件为叶子节点,布局组件为中间节点 。当用户进行交互时,会触发界面修改,通过重新渲染组件树来实现应用界面更新 。
- 数据与UI更新过程 : ArkUI框架的数据处理过程和UI更新过程是分开进行的。数据处理过程中,主要是对状态数据进行更新,并通过标脏过程确定布局最小影响范围,减少不必要的重新布局 。UI更新过程包括组件标脏、布局、测量和渲染等阶段 。
- 布局组件 : ArkUI框架提供了多种布局组件,如Row、Column、Stack、Flex、List、Grid、RelativeContainer等。开发者可以根据场景选择合适的布局组件,以优化性能 。例如,线性布局(Row、Column)适用于横向或纵向排列组件,而弹性布局(Flex)适用于需要弹性排列的场景。
- 性能优化 : ArkUI框架在性能优化方面做了很多工作。例如,通过减少不必要的组件嵌套和节点数量,降低布局测算的复杂度,从而提升性能。开发者可以使用DevEco Studio提供的工具(如Profiler和ArkUI Inspector)来查看性能瓶颈和组件树结构,进一步优化应用性能。
- 状态管理 : ArkUI框架支持状态管理最佳实践,通过有效的状态管理减少无效的UI更新操作,提升性能。例如,在状态变量变化导致UI更新时,只更新部分组件,而不是重新渲染整个界面。
3.3.2. 生命周期
3.3.2.1. UIAbility生命周期、启动流程
3.3.2.2. Page和自定义组件生命周期
有@Entry装饰器的@component组件的生命周期
- onPageShow:页面每次显示时触发一次,包括路由过程、应用进入前台等场景。
- onPageHide:页面每次隐藏时触发一次,包括路由过程、应用进入后台等场景。
- onBackPress:当用户点击返回按钮时触发。
有@Entry装饰器和无@Entry装饰器@Component组件都有的生命周期
- aboutToAppear :组件即将出现时回调该接口,具体时机为在创建自定义组件的新实例后,在执行其
build()
函数之前执行。 - onDidBuild :API12新增,组件
build()
函数执行完成之后回调该接口,不建议在onDidBuild
函数中更改状态变量、使用animateTo
等功能,这会导致不稳定的UI表现。 - aboutToDisappear :
aboutToDisappear
函数在自定义组件析构销毁之前执行。不允许在aboutToDisappear函数中改变状态变量,特别是@Link变量的修改会导致应用程序行为不稳定。
说一下顺序
页面又刷新,会走aboutToAppear吗
组件尺寸变化回调
abouttodisappear不适合异步,阻碍回收
3.3.3. 手势优先级的运用
两个手势冲突,怎么处理
3.3.4. ArkUI状态管理
V1
@State @Prop @Link @ObjectLink
@Provide/@Consume
@Builder 和 @BuilderParam
@Watch 监视数据的变化(第一次不会触发)
@State @Prop @Link哪个性能最差
V2
@ObserveV2 @Trace
3.3.4.1. 如何进行全局状态管理?
- @Provide+@Consume装饰器 适用场景:适用于整个组件树而言"全局"的状态共享,且该状态改动不频繁的场景。 工作原理:通过在最顶层组件中使用
@Provide
装饰器提供状态,其他需要共享状态的组件通过@Consume
装饰器获取该状态 。 优点:减少了状态传递的层级,提升了代码的可维护性和可拓展性。 注意事项:确保状态的生命周期与组件树的生命周期一致,避免不必要的UI刷新。 - AppStorage 适用场景:适用于整个应用而言"全局"的变量或应用的主线程内多个
UIAbility
实例间的状态共享。 工作原理:AppStorage
与应用的进程绑定,由UI框架在应用程序启动时创建,当应用进程终止,AppStorage
被回收。 优点:适用于需要在整个应用中共享状态的场景。 注意事项:确保状态的生命周期与应用进程一致,避免在应用退出后仍有状态存在。 - LocalStorage 适用场景:适用于单个Ability而言"全局"的变量,主要用于不同页面间的状态共享。 工作原理:
LocalStorage
的生命周期由应用程序决定,当应用释放最后一个指向LocalStorage
的引用时,LocalStorage
被垃圾回收。 优点:适用于需要在单个UIAbility中不同页面间共享状态的场景。 注意事项:确保状态的生命周期与应用程序的生命周期一致,避免在应用退出后仍有状态存在。
3.3.4.2. LocalStorage在应用重启后数据会消失吗?
会,因为LocalStorage 是一种用于页面或组件级别的数据存储方式,它允许开发者在页面或组件的生命周期内存储和检索数据。LocalStorage 的数据存储在内存中,因此它的读写速度相对较快。但是,当应用重启后,LocalStorage 中的数据会丢失。
3.3.4.3. @Provider和@Consumer vs @Provide和@Consume的区别
|----------|------------------------------------------------------|-------------------------------------------------------|
| | @Provider和@Consumer | @Provide和@Consume |
| 版本历史 | V2 | V1 |
| 是否本地初始化 | 允许本地初始化,当找不到@Provider 的时候使用本地默认值 | 禁止本地初始化,当找不到对应的@Provide 时候,会抛出异常。 |
| 支持类型 | 支持 function。 | 不支持 function。 |
| 观察能力 | 仅能观察自身赋值变化,如果要观察嵌套场景,配合@Trace 一起使用 | 观察第一层变化,如果要观察嵌套场景,配合@Observed 和@ObjectLink 一起使用。 |
| 命名 | alias 和属性名alias 是唯一匹配的 key,如果缺省 alias,则默认属性名为 alias。 | alias 和属性名都为 key,优先匹配 alias,匹配不到可以匹配属性名。 |
| 从父组件初始化 | 允许 | 禁止 |
| 支持重载默认开启 | 即@Provider 可以重名,@Consumer 向上查找最近的@Provider。 | 默认关闭,即在组件树上不允许有同名@Provide。如果需要重载,则需要配置 allowOverride。 |
3.3.5. ForEach和LazyForEach的区别?
ForEach和LazyForEach都是用于渲染列表的装饰器,它们的区别在于:
ForEach:渲染列表时,会将列表中的每一项都渲染一次,适用于列表项数量较少的情况。
LazyForEach:渲染列表时,只渲染当前可见的列表项,适用于列表项数量较多的情况。
3.3.6. ArkUI动画
3.3.7. 上拉加载和下拉刷新如何实现?
上拉加载: 使用pullToRefresh组件可以实现上拉加载更多数据的效果 。该组件支持懒加载,可以通过设置LazyForEach来实现数据的按需加载 。 在使用pullToRefresh组件时,需要将列表组件、绑定的数据对象和scroller对象包含进去,并添加上滑方法。
下拉刷新: 可以使用Refresh组件来实现下拉刷新功能 。刷新逻辑可以在onRefreshing回调方法中执行。 当列表滑动到底部时,可以触发onReachEnd事件回调,用于加载更多数据 。 这些组件和方法可以帮助开发者在鸿蒙(HarmonyOS)中实现流畅的上拉加载和下拉刷新功能,提升用户体验。
4. 进阶问题
4.1. 鸿蒙动态UI加载
4.2. 如何优化应用的性能?
- 内存管理:合理分配和释放内存,避免内存泄漏。(按需分配(懒加载),及时释放)
-
- 如避免循环依赖
- 组件内生命周期的大内存对象在aboutToDisappear手动销毁释放
- 播放器资源如果不需要在屏幕渲染了,及时释放
- 后台优化:合理使用后台服务和定时任务,避免不必要的后台运行。
- UI渲染优化
-
- 使用轻量级的UI组件,减少布局复杂度,优化渲染性能。
- 图片压缩
- 图片/视频资源预加载
- 资源优化
-
- 压缩图片和媒体资源,减少应用的体积和加载时间。
- 耗时的绘制换成图片
- List等列表组件
-
- LazyForEach,合理使用cachedCount
- 复用
- 耗时任务放到异步线程(不卡主线程)
4.3. H5如何与HarmonyOS应用(webView)进行通信?官方文档
应用侧可以通过runJavaScript()方法异步调用前端页面的JavaScript相关函数,并通过Promise方式返回脚本执行的结果。runJavaScript需要在loadUrl完成后,比如onPageEnd中调用。
使用Web组件将应用侧代码注册到前端页面中,注册完成之后,前端页面中使用注册的对象名称就可以调用应用侧的函数,实现在前端页面中调用应用侧方法。注册应用侧代码有两种方式,一种在Web组件初始化调用,使用javaScriptProxy()接口。另外一种在Web组件初始化完成后调用,使用registerJavaScriptProxy()接口。
4.4. 三层架构是什么?
官方文档 三层架构为了"一次开发,多端部署",项目结构采用三层架构 三层工程结构如下:
- commons(公共能力层):用于存放公共基础能力集合(如工具库、公共配置等)。commons层可编译成一个或多个HAR包或HSP包,只可以被products和features依赖,不可以反向依赖。
- features(基础特性层):开发页面、组件(HAR包或HSP包)。
- products(产品定制层):定义phone\pad两个ability,引用 features 的包和 commons 的包完成应用功能
4.5. commonEventManager公共事件订阅踩坑
错误用法:subscribeEventHandler方法里,使用到this是为undefined
this.subscriber = await commonEventManager.createSubscriber(this.subscriberInfo)
commonEventManager.subscribe(this.subscriber, this.subscribeEventHandler)
正确用法:加上.bind(this)
this.subscriber = await commonEventManager.createSubscriber(this.subscriberInfo)
commonEventManager.subscribe(this.subscriber, this.subscribeEventHandler.bind(this))
原因:bind(this)来强行指定方法体的this为当前对象
5. 编程能力
5.1. 算法
任意说个算法
5.1.1. 开发模式MVC、MVVM等的理解
5.1.1.1. 如何提高代码质量
- 变量命名清晰,尽量不缩写不简写
- 方法行数、类行数控制,一个方法干一件事,一个类做一种事
- 模块之间解耦合,互相之间通信通过回调、代理给到另外的模块处理
- 单元测试、冒烟自测
6. 业务理解以及解决问题的能力
- 项目有哪些印象深刻难题,怎么解决的
- 有哪些亮点功能,大概讲一下
7. 面试结果
建议层级
面试外包对应的层级
w3对应s4,w4对应s5,老师给一个推荐层级
候选人优势
候选人不足
其他
面试结果