合理使用IPC通信

应用使用操作系统提供的IPC(Inter-Process Communication,进程间通信)机制进行跨进程通信是通用场景, 当前系统基于Binder驱动封装了一套IPC机制,提供了应用和系统服务间的跨进程通信能力。

IPC机制方便了进程之间的交互和通信,但是,不合理的使用IPC通信会对应用性能造成影响。

在应用主线程中进行IPC通信,消息的发送和接收需要等待对方进程的响应,这会对应用主线程造成阻塞。如果操作耗时较长或者频率较高,产生的时延会引起页面卡顿、丢帧。

进行IPC通信时需要进程上下文切换,从一个进程或线程切换到另一个进程或线程,会造成CPU时间片的浪费,从而降低应用性能。

为了保持数据的一致性和正确性,需要使用进程同步和互斥机制,在进程之间进行协调。每个进程在对资源操作前都尝试加锁,操作结束解锁,产生了锁的获取和释放等额外开销。

所以,在一些对性能要求高的场景:多人在线游戏、视频编辑、实时通信和视频等,IPC通信是一个重要优化点。

优化思路

在应用开发过程中,合理使用IPC通信是确保应用性能和用户体验的关键因素。然而,不合理的IPC通信可能会导致性能问题。因此,可以从以下几个方面进行优化。

本地缓存数据:合理使用本地缓存,将常用的数据存储在本地,而不是每次都通过IPC请求。这可以减少不必要的通信次数。确保缓存数据的有效期和更新策略是合理的,以防止缓存数据过期或不一致。

批处理请求:将多个IPC请求合并成一个批处理请求,从而减少通信的次数。这对于频繁的小数据请求尤其有用。例如,在获取多个设置项时,可以一次性请求所有设置项而不是分别请求每个设置项。

异步处理:对于不需要立即响应的IPC请求,可以将它们设置为异步处理,以免阻塞主线程。例如,后台数据同步可以在后台线程中进行,而不会影响用户界面的响应。

减少不必要的数据传输:确保只传输应用所需的数据,避免传输不必要的信息。

场景示例 1

当前应用程序模块需要进行耗时计算,由应用程序进程和执行计算任务进程间进行IPC通信。

问题:前台发送计算任务请求后,一直处于获取计算结果状态,界面卡死无法进行其他操作。

分析 :通过耗时分析工具Time Profiler观察到,在页面跳转过程中,ArkTS Callstack泳道存在大量的函数调用栈,总耗时达3.4s,阻塞了UI绘制渲染。

提出方案

1.可以先读取应用中的缓存数据,如果已经存在相同任务的计算结果,那么就不依赖于IPC通信进行获取数据。

2.单次大数据耗时IPC通信,改为多次小数据异步IPC通信。

方案实施

通过读取缓存目录下的缓存数据,进行数据对比

typescript 复制代码
// 获取缓存数据
getCacheData(key: string) {
 try {
  let context = getContext(this) as common.UIAbilityContext;
  let cacheFilePath = context.cacheDir + '/ComputeResult.txt'; // 应用沙箱缓存目录
  let computeResult = JSON.parse(fs.readTextSync(cacheFilePath));
  // 是否存在已有耗时任务
  if (computeResult[key] === undefined) {
   return undefined
  }
  return computeResult[key]
 } catch (e) {
  console.error('getCacheData failed' + JSON.parse(e));
 }
}

通过获取到缓存数据结果,决定是否发送IPC通信请求

kotlin 复制代码
if (this.getCacheData(this.taskId)) {
 // 获取缓存数据
 this.result = this.getCacheData(this.taskId);
} else {
 // 写入数据
 data.writeStringArray(sendData);
 // 发送IPC通信请求
 await ReceivedData.sendMessageRequest(REQUEST_CODE, data, reply, option);
}

大数据IPC通信改为小数据批量IPC通信

ini 复制代码
if (this.getCacheData(this.taskId)) {
 this.result = this.getCacheData(this.taskId);
} else {
 let chunkSize = 10;
 for (let i = 0;i < sendData.length; i += chunkSize) {
  let chunk = sendData.slice(i, i + chunkSize);
  // 写入数据
  data.writeStringArray(chunk);
  await ReceivedData.sendMessageRequest(REQUEST_CODE, data, reply, option);
 }
}

运行效果

应用读取缓存后,运行时效果如下泳道图所示:

ArkTS Callstack泳道中,可以看出当前的进程中没有大量的computed耗时任务,而是直接调用getCacheData获取缓存数据,减少了应用IPC通信次数。

拆分大数据后,运行时效果如下泳道图所示:

ArkTS Callstack泳道中,当前应用进程的computed耗时任务由3.4s变为227ms,性能优化明显。

场景示例 2

问题应用包含Page: A和Page B两个页面,Page B的主体视图是Tabs组件,Tabs组件内包含首页、WiFi列表页等数个页签,默认展示首页页签。从Page A跳转到Page B时,页面加载时间较长。

分析 :通过耗时分析工具Time Profiler对页面跳转过程进行录制并分析,观察ArkTS Callstack泳道发现该过程频繁调用getScanInfoList方法,产生阻塞。

提出方案 :通过查阅接口文档,getScanInfoList功能为获取当前扫描到的热点列表,是同步接口,会阻塞主线程。考虑到Page B的首页页签并不展示热点列表,只有在用户切换到WiFi页签时才需要展示,该扫描操作在页面跳转过程中是不必要的。因此,将该功能放在切换WiFi列表页签的处理逻辑中。

方案实施

将Page B中aboutToAppear生命周期函数中获取热点列表的方法ipcTask移除。在Page B的Tabs组件WiFi页签中自定义组件WiFiItem,用于展示WiFi列表,将该ipcTask移动到WiFiItem组件的aboutToAppear生命周期函数中。

ini 复制代码
aboutToAppear() {
 this.ipcTask();
}

ipcTask() {
 let count = 0;
 while (count < 100) {
  let data = wifiManager.getScanInfoList();
  count++;
 }
}

调整位置后,Page A到Page B的跳转过程耗时明显缩短,提升了用户体验。

总结

合理使用IPC通信对于优化应用程序性能至关重要。通过场景分析、逻辑优化和合理的权衡,可以确保IPC通信不成为应用性能的瓶颈,从而提供更出色的用户体验。

参考资料

· [1] @ohos.rpc (RPC通信) (openharmony.cn)

· [2] IPC与RPC通信概述 (openharmony.cn)

· [3] 性能分析工具CPU Profiler (openharmony.cn)

· [4] @ohos.wifiManager (WLAN)(推荐) (openharmony.cn)

相关推荐
马剑威(威哥爱编程)2 分钟前
在HarmonyOS NEXT 开发中,如何指定一个号码,拉起系统拨号页面
华为·harmonyos·arkts
GeniuswongAir1 小时前
Flutter极速接入IM聊天功能并支持鸿蒙
flutter·华为·harmonyos
90后的晨仔5 小时前
鸿蒙ArkUI框架中的状态管理
harmonyos
别说我什么都不会21 小时前
OpenHarmony 5.0(API 12)关系型数据库relationalStore 新增本地数据变化监听接口介绍
api·harmonyos
MardaWang1 天前
HarmonyOS 5.0.4(16) 版本正式发布,支持wearable类型的设备!
华为·harmonyos
余多多_zZ1 天前
鸿蒙学习手册(HarmonyOSNext_API16)_应用开发UI设计:Swiper
学习·ui·华为·harmonyos·鸿蒙系统
斯~内克1 天前
鸿蒙网络通信全解析:从网络状态订阅到高效请求实践
网络·php·harmonyos
别说我什么都不会1 天前
OpenHarmony 5.0Release 开发的在线音乐应用卡片
app·harmonyos
威哥爱编程1 天前
在HarmonyOS NEXT 开发中,如何指定一个号码,拉起系统拨号页面
华为·harmonyos·arkts