鸿蒙HarmonyOS:Web组件网页白屏检测

之前介绍了HarmonyOS的Web组件的简单使用和离线包方案,本篇文章主要说的是Web组件的白屏检测的方案

鸿蒙HarmonyOS:Web组件初体验与离线包方案探索

APP白屏问题简介

Web白屏问题通常是在客户端上经常遇到的一种常见问题,尤其是对于Android来说,多个版本的碎片化导致用户有多种不同版本的chrome内核,那么对于HarmonyOS来说也是一样的,一般来说Web白屏有以下几种原因

  • Web组件配置问题,对于APP来说,这种可能性很小,主要是开发阶段遇到,比如没有开启网络权限、JavaScript未启用、缓存问题等
  • 网络问题 ,如果设备网络条件不好,尝试加载网络内容时可能导致白屏,这个可以考虑离线包方案解决可以参考之前那篇文章 鸿蒙HarmonyOS:Web组件初体验与离线包方案探索
  • SSL证书问题 如果WebView加载的内容涉及到HTTPS,SSL证书验证失败可能导致页面无法加载。
  • HTML、JavaScript语法兼容问题,由于前端这几年发展迅速,导致语法变更快,很多框架比如vue、react等在构建的时候,ES语法不兼容在客户端上经常见到,这种大多都是兼容性问题,比如只在某些低版本机型可见,此类问题比较难以感知,只有用户反馈过来才能去响应

总体来说Web白屏问题是没发根治的,只能通过合理的线上监控,去发现解决一些疑难的白屏问题,因此对于客户端APP来说白屏检测基本上是必有功能,既可以去做白屏监控,也能通过检测来统计用户前段加载页面时所需要的白屏时间,为Web秒开优化做有效数据验证。

白屏检测方案

在Android和iOS端白屏检测方案比较成熟,我们参考字节的方案来简单讲一下判断白屏的先觉条件把

字节方案原文如下: ...我们把 WebView 截图的图片进行缩小到原图的 1/6,遍历检测图片的像素点,当非白色的像素点大于 5% 的时候我们就认为是非白屏的情况,可以相对高效检测准确得出详情页是否发生了白屏。

参考 www.toutiao.com/article/687...

首先先对Web组件进行截图,但如果按照图片整个大小来遍历像素点,这耗费的时间和性能在客户端上时不太现实的,我们一般把图片缩小,然后在对图片进行抽样检测例,抽样一般都是按照小区域来划分,判断这些小区域的点位的白屏信息,来判断页面的白屏情况,采样点越细,判断的越精确,但同时性能损耗页越大,采样方案 如下图所示

HarmonyOS Web组件白屏检测

话不多说,我们开干,

1. 首先,我们先对web组件进行截图,

js 复制代码
//给Web 组件设置id 
Web({ src: this.url, controller: this.webviewController })  
  .layoutWeight(1)  
  .width("100%")  
  .id("web-view")
  ...
  ...
  ...
//调用 componentSnapshot 对Web截图
componentSnapshot.get("web-view", (error: Error, pixmap: image.PixelMap) => {  
  pixmap.getImageInfo().then((info) => {  
    console.info("image info: width=" + info.size.width + ",height=" + info.size.height + ",density=" + info.density);  
  })  
})

2. 定义白屏采样参数

js 复制代码
declare class CheckParam {
  //白屏采样图片
  imageMap: image.PixelMap
  //采样图片压缩倍数
  compressRatio: number;
  //白屏采样阈值,单位:百分比
  threshold: number;
  //白屏采样比例,单位:百分比
  density: number;
  //采样区域大小
  regionSize: number;
  //采样对比颜色
  color: number;
}

参数解释

  • imageMap,采样图片
  • compressRatio,采样图片压缩倍数
  • threshold,白屏采样阈值,单位:百分比
  • density,白屏采样比例,单位:百分比
  • regionSize,采样区域大小
  • color,采样白屏对比颜色,一般为白色,如果是黑色背景就是黑色,按照Web组件的背景色来判1断

3. 白屏检测具体实现

首先定义白屏检测的异步方法,返回是 Promise

js 复制代码
function checkWhiteScreen(param: CheckParam): Promise<boolean> {
...
}

按照param.compressRatio 的,来压缩图片的大小再通过getImageInfo解析图片的具体信息

scss 复制代码
param.imageMap.scale(1 / param.compressRatio, 1 / param.compressRatio)  
  .then(() => {  
    return param.imageMap.getImageInfo();  
  })

根据上一步得到的info信息来把图片按照采样区域生成ArrayBuffer数组

js 复制代码
then(info => {
    let width = info.size.width;
    let height = info.size.height;
    let size = param.regionSize;
    // 根据采样比例计算采样图片 X 轴的采样点数量
    let pointNumX = Math.floor(width * param.density / 100 / size);
    // 根据采样比例计算采样图片 Y 轴的采样点数量
    let pointNumY = Math.floor(height * param.density / 100 / size);
    // 根据采样点数量计算采样图片 X 轴的采样步长
    let xStep = Math.floor(width / pointNumX);
    // 根据采样点数量计算采样图片 Y 轴的采样步长
    let yStep = Math.floor(height / pointNumY);
    console.info("采样点数量:pointNumX =" + pointNumX + "pointNumY=" + pointNumY + "total=" + pointNumX * pointNumY);
    console.info("采样步长:" + xStep + "," + yStep);

    let promiseArr = [] as Promise<ArrayBuffer>[];
    for (let i = 0; i < pointNumX; i++) {
      for (let j = 0; j < pointNumY; j++) {
        let x = i * xStep;
        let y = j * yStep;
        //buffer大小,取值为:height * width *4
        const buffer = new ArrayBuffer(size * size * 4);
        promiseArr.push(param.imageMap.readPixels({ pixels: buffer,
          offset: 0,
          stride: size * 4,
          region: { x: x, y: y, size: { width: size, height: size } } }).then(() => {
          return buffer
        }));
      }
    }
    return Promise.all(promiseArr);
})

遍历数组然后判断每一个像素点的信息统计后判断白屏率

js 复制代码
then(buffers=>{  
  let whitePointNum = 0;  
    let cr = param.color >> 16 & 0xff;  
    let cg = param.color >> 8 & 0xff;  
    let cb = param.color & 0xff;  
    let ca = param.color >> 24 & 0xff;  
  for (let i = 0; i < buffers.length; i++) {  
    let buffer = buffers[i];  
    let dataView = new DataView(buffer);  
    for (let j = 0; j < buffer.byteLength; j += 4) {  
      let r = dataView.getUint8(j);  
      let g = dataView.getUint8(j + 1);  
      let b = dataView.getUint8(j + 2);  
      let a = dataView.getUint8(j + 3);  
      if (r === cr && g === cg && b === cb && a === ca) {  
        whitePointNum++;  
      }  
    }  }  
    let whitePointPercent = Math.floor(whitePointNum / (buffers.length * param.regionSize * param.regionSize) * 100);
  console.info("白屏检测耗时:" + (Date.now() - time) + "ms");  
  if (whitePointPercent >= param.threshold) {  
    console.info("白屏" + whitePointPercent + "%");  
    return true;  
  } else {  
    console.info("非白屏" + whitePointPercent + "%");  
    return false;  
  }  
})

完整代码如下

js 复制代码
//白屏采样参数定义
import image from '@ohos.multimedia.image';

declare class CheckParam {
  //白屏采样图片
  imageMap: image.PixelMap
  //采样图片压缩倍数
  compressRatio: number;
  //白屏采样阈值,单位:百分比
  threshold: number;
  //白屏采样比例,单位:百分比
  density: number;
  //采样区域大小
  regionSize: number;
  //采样对比颜色
  color: number;
}

function checkWhiteScreen(param: CheckParam): Promise<boolean> {
  const time = Date.now();
  //压缩图片
  return param.imageMap.scale(1 / param.compressRatio, 1 / param.compressRatio)
    .then(() => {
      return param.imageMap.getImageInfo();
    })
    .then(info => {
      let width = info.size.width;
      let height = info.size.height;
      let size = param.regionSize;
      // 根据采样比例计算采样图片 X 轴的采样点数量
      let pointNumX = Math.floor(width * param.density / 100 / size);
      // 根据采样比例计算采样图片 Y 轴的采样点数量
      let pointNumY = Math.floor(height * param.density / 100 / size);
      // 根据采样点数量计算采样图片 X 轴的采样步长
      let xStep = Math.floor(width / pointNumX);
      // 根据采样点数量计算采样图片 Y 轴的采样步长
      let yStep = Math.floor(height / pointNumY);
      console.info("采样点数量:pointNumX =" + pointNumX + "pointNumY=" + pointNumY + "total=" + pointNumX * pointNumY);
      console.info("采样步长:" + xStep + "," + yStep);

      let promiseArr = [] as Promise<ArrayBuffer>[];
      for (let i = 0; i < pointNumX; i++) {
        for (let j = 0; j < pointNumY; j++) {
          let x = i * xStep;
          let y = j * yStep;
          //buffer大小,取值为:height * width *4
          const buffer = new ArrayBuffer(size * size * 4);
          promiseArr.push(param.imageMap.readPixels({ pixels: buffer,
            offset: 0,
            stride: size * 4,
            region: { x: x, y: y, size: { width: size, height: size } } }).then(() => {
            return buffer
          }));
        }
      }

      return Promise.all(promiseArr);
    })
    .then(buffers => {
      let whitePointNum = 0;
      let cr = param.color >> 16 & 0xff;
      let cg = param.color >> 8 & 0xff;
      let cb = param.color & 0xff;
      let ca = param.color >> 24 & 0xff;
      for (let i = 0; i < buffers.length; i++) {
        let buffer = buffers[i];
        let dataView = new DataView(buffer);
        for (let j = 0; j < buffer.byteLength; j += 4) {
          let r = dataView.getUint8(j);
          let g = dataView.getUint8(j + 1);
          let b = dataView.getUint8(j + 2);
          let a = dataView.getUint8(j + 3);
          if (r === cr && g === cg && b === cb && a === ca) {
            whitePointNum++;
          }
        }
      }
      let whitePointPercent = Math.floor(whitePointNum / (buffers.length * param.regionSize * param.regionSize) * 100);
      console.info("白屏检测耗时:" + (Date.now() - time) + "ms");
      if (whitePointPercent >= param.threshold) {
        console.info("白屏" + whitePointPercent + "%");
        return true;
      } else {
        console.info("非白屏" + whitePointPercent + "%");
        return false;
      }
    })
}

function logPicInfo(imageMap: image.PixelMap) {
  imageMap.getImageInfo().then((info) => {
    console.info("image info: width=" + info.size.width + ",height=" + info.size.height + ",density=" + info.density);
  })
}

export {
  checkWhiteScreen
}

效果展示

待优化

采样调优

对于白屏采样来说,有着多种方式,本文的采样将页面均匀采样,精度可能准确些但是性能上有很大的提升空间,

比如减少采样点的十字形采样:

也可以对采样点的颜色判断优化成区域判断,比如在 250~255 之间的都是白色

多种方式一起判断

前端白屏像素点采集统计只是其中的一种方法,白屏判断可以多种方式结合,像素点采集为其中一种权重,可以和前端结合,js检测根节点是否渲染,检测窗口高度等多种方式综合判断

无法精准匹配,无法适应复杂业务场景

像素采集方式还是基于统计能力,前端有骨架屏、错误页、复杂背景色等会影响检测结果,像素分析不具有通用性。

后续希望拓展一下端智能的方式,通过 AI 算法来去做精准的白屏统计

总结

在本文中,我们深入探讨了HarmonyOS中Web组件的白屏问题及其检测方案。

文章重点介绍了一种基于字节的白屏检测方案,通过对WebView截图、压缩和采样像素点的方式,遍历检测小区域的非白色像素点数量,从而判断是否出现白屏问题。方案考虑了压缩比、采样阈值、采样点数、采样区域大小和颜色等参数,使得检测更加灵活和精准。

在HarmonyOS中的具体实现中,使用了PixelMap和相关的图像处理API,通过异步方法和Promise链式调用的方式,实现了对Web组件的白屏检测。通过该方案,开发者能够在应用中及时发现白屏问题,提高应用的稳定性和用户体验。

但是面对复杂的前端业务场景,像素采样的方式还是比较单薄,后续希望拓展段智能的方式,让 AI 能力扩展到HarmonyOS。

总体而言,白屏问题作为移动应用中常见的用户体验难题,需要综合考虑多个因素,结合合适的检测方案。本文所介绍的HarmonyOS Web组件白屏检测方案提供了一种实用的思路和代码实现,对于开发者优化应用性能、提升用户体验具有积极意义。

鸣谢

感谢ChatGPT 对本文的写作帮助

感谢Github Copilot 对本文代码帮助

参考《今日头条品质优化 - 图文详情页秒开实践》

相关推荐
好开心3312 分钟前
axios的使用
开发语言·前端·javascript·前端框架·html
Domain-zhuo21 分钟前
Git常用命令
前端·git·gitee·github·gitea·gitcode
菜根Sec1 小时前
XSS跨站脚本攻击漏洞练习
前端·xss
m0_748257181 小时前
Spring Boot FileUpLoad and Interceptor(文件上传和拦截器,Web入门知识)
前端·spring boot·后端
桃园码工1 小时前
15_HTML5 表单属性 --[HTML5 API 学习之旅]
前端·html5·表单属性
百万蹄蹄向前冲2 小时前
2024不一样的VUE3期末考查
前端·javascript·程序员
Anlici2 小时前
three.js建立3D模型展示地球+高亮
前端·数据可视化·canvas
轻口味2 小时前
【每日学点鸿蒙知识】AVCodec、SmartPerf工具、web组件加载、监听键盘的显示隐藏、Asset Store Kit
前端·华为·harmonyos
alikami2 小时前
【若依】用 post 请求传 json 格式的数据下载文件
前端·javascript·json
吃杠碰小鸡3 小时前
lodash常用函数
前端·javascript