鸿蒙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 对本文代码帮助

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

相关推荐
y先森3 小时前
CSS3中的伸缩盒模型(弹性盒子、弹性布局)之伸缩容器、伸缩项目、主轴方向、主轴换行方式、复合属性flex-flow
前端·css·css3
前端Hardy3 小时前
纯HTML&CSS实现3D旋转地球
前端·javascript·css·3d·html
susu10830189113 小时前
vue3中父div设置display flex,2个子div重叠
前端·javascript·vue.js
IT女孩儿4 小时前
CSS查缺补漏(补充上一条)
前端·css
吃杠碰小鸡5 小时前
commitlint校验git提交信息
前端
虾球xz6 小时前
游戏引擎学习第20天
前端·学习·游戏引擎
我爱李星璇6 小时前
HTML常用表格与标签
前端·html
疯狂的沙粒6 小时前
如何在Vue项目中应用TypeScript?应该注意那些点?
前端·vue.js·typescript
小镇程序员6 小时前
vue2 src_Todolist全局总线事件版本
前端·javascript·vue.js
野槐6 小时前
前端图像处理(一)
前端