一、概念溯源:ArrayBuffer与Uint8Array的本质

在鸿蒙(HarmonyOS NEXT)应用开发中,二进制数据的处理是绕不开的基础能力。无论是网络通信、文件读写、图片处理还是加解密,都会与ArrayBufferUint8Array打交道。

1.1 ArrayBuffer:原始的内存块

ArrayBuffer代表一块固定长度的原始二进制数据缓冲区。可以把它想象成一块未经开垦的内存区域,它只负责占有一块连续的内存空间,但不提供任何手段来直接读写其中的数据

  • 特点:固定长度,创建后无法直接增减大小;本身不可读写,必须通过“视图”来操作。

  • 创建方式

typescript

// 创建一个长度为8字节的ArrayBuffer
const buffer: ArrayBuffer = new ArrayBuffer(8);
console.log(buffer.byteLength); // 输出: 8

在鸿蒙系统中,ArrayBuffer内部包含一块Native内存,其JS对象壳被分配在虚拟机本地堆(LocalHeap)上。作为一种可转移对象ArrayBuffer在线程间传递时无需拷贝,能有效减少内存占用、提升性能。

1.2 Uint8Array:操作内存的视图

Uint8Array是一种类型化数组(TypedArray),它充当ArrayBuffer的“视图”,将缓冲区的数据解析为8位无符号整数数组。

  • 本质:它并不拥有自己的数据,而是引用底层的ArrayBuffer。每个元素对应1个字节(8位),取值范围为0~255。

  • 常见用途:处理文件数据、图像像素、网络协议包、证书二进制数据等。

typescript

// 从已有的ArrayBuffer创建Uint8Array视图
const buffer = new ArrayBuffer(8);
const uint8Array = new Uint8Array(buffer);
// 此时uint8Array引用了buffer,修改uint8Array[0]会直接影响buffer

理解两者的关系:ArrayBuffer是“内存条”,Uint8Array是“读懂内存条上字节的工具”


二、核心转换:互转的两种场景

2.1 ArrayBuffer → Uint8Array

ArrayBuffer转化为Uint8Array获取数据视图的过程,可以通过构造方法直接实现:

typescript

/**
 * ArrayBuffer转Uint8Array
 * @param buffer 原始二进制缓冲区
 * @returns 对应的Uint8Array视图
 */
function arrayBufferToUint8Array(buffer: ArrayBuffer): Uint8Array {
    // 方法1:直接构造(共享内存)
    return new Uint8Array(buffer);
    
    // 方法2:若需独立副本(不共享内存),使用slice
    // return new Uint8Array(buffer.slice(0));
}

注意事项:通过new Uint8Array(buffer)创建的视图与buffer共享同一块内存,修改视图中的数据会同步影响原ArrayBuffer

2.2 Uint8Array → ArrayBuffer

Uint8Array内部维护着对底层ArrayBuffer的引用,通过.buffer属性即可获取:

typescript

/**
 * Uint8Array转ArrayBuffer
 * @param array Uint8Array实例
 * @returns 对应的ArrayBuffer
 */
function uint8ArrayToArrayBuffer(array: Uint8Array): ArrayBuffer {
    // 方法1:直接访问buffer属性(共享内存)
    return array.buffer as ArrayBuffer;
    
    // 方法2:若需独立副本(不共享内存),需手动复制
    // const newBuffer = new ArrayBuffer(array.length);
    // const view = new Uint8Array(newBuffer);
    // view.set(array);
    // return newBuffer;
}

注意事项array.buffer返回的ArrayBuffer与原Uint8Array共享内存。如需独立副本,需手动创建新的ArrayBuffer并用set()复制数据。

2.3 工具类封装(可直接复用)

typescript

export class BinaryUtil {
    /**
     * ArrayBuffer转Uint8Array
     */
    public static arrayBufferToUint8Array(buffer: ArrayBuffer): Uint8Array {
        return new Uint8Array(buffer);
    }

    /**
     * Uint8Array转ArrayBuffer
     */
    public static uint8ArrayToArrayBuffer(array: Uint8Array): ArrayBuffer {
        return array.buffer as ArrayBuffer;
    }

    /**
     * 复制一份独立的Uint8Array(不共享内存)
     */
    public static cloneUint8Array(src: Uint8Array): Uint8Array {
        const buffer = new ArrayBuffer(src.length);
        const dst = new Uint8Array(buffer);
        dst.set(src);
        return dst;
    }

    /**
     * 复制一份独立的ArrayBuffer(不共享内存)
     */
    public static cloneArrayBuffer(src: ArrayBuffer): ArrayBuffer {
        const dst = new ArrayBuffer(src.byteLength);
        const srcView = new Uint8Array(src);
        const dstView = new Uint8Array(dst);
        dstView.set(srcView);
        return dst;
    }
}

三、进阶实战:常见业务场景

3.1 字符串与二进制互转(文件读写、网络请求)

在鸿蒙中处理文本文件或网络数据时,常需在字符串和二进制之间转换。核心利用util模块的TextEncoderTextDecoder

typescript

import { util } from '@kit.ArkTS';

// 字符串 → ArrayBuffer(编码)
function stringToArrayBuffer(str: string): ArrayBuffer {
    const encoder = new util.TextEncoder();
    const uint8Array = encoder.encodeInto(str); // 返回Uint8Array
    return uint8Array.buffer as ArrayBuffer;
}

// ArrayBuffer → 字符串(解码)
function arrayBufferToString(buffer: ArrayBuffer, encoding: string = 'utf-8'): string {
    const decoder = util.TextDecoder.create(encoding, { ignoreBOM: true });
    const uint8Array = new Uint8Array(buffer);
    return decoder.decodeWithStream(uint8Array, { stream: false });
}

典型场景:读取GBK编码的网络数据时,先将响应数据作为ArrayBuffer获取,再通过TextDecoder指定'gbk'解码为可读字符串。

3.2 Base64与二进制互转(图片传输、存储)

鸿蒙@kit.ArkTS提供了Base64Helper,配合二进制互转实现Base64编解码:

typescript

import { util } from '@kit.ArkTS';

const base64Helper = new util.Base64Helper();

// ArrayBuffer → Base64
function arrayBufferToBase64(buffer: ArrayBuffer): string {
    const uint8Array = new Uint8Array(buffer);
    return base64Helper.encodeToStringSync(uint8Array);
}

// Base64 → ArrayBuffer
function base64ToArrayBuffer(base64Str: string): ArrayBuffer {
    const uint8Array: Uint8Array = base64Helper.decodeSync(base64Str);
    return uint8Array.buffer as ArrayBuffer;
}

3.3 图片Uri/文件转ArrayBuffer

从图片URI或文件路径读取二进制数据,可用于上传或图像处理:

typescript

import { fileIo as fs } from '@kit.CoreFileKit';

function uriToArrayBuffer(uri: string): ArrayBuffer {
    const file = fs.openSync(uri, fs.OpenMode.READ_ONLY);
    // 先获取文件大小,更精确
    const stat = fs.statSync(file.fd);
    const buffer = new ArrayBuffer(stat.size);
    fs.readSync(file.fd, buffer);
    fs.closeSync(file.fd);
    return buffer;
}

3.4 PixelMap与ArrayBuffer互转(图像处理)

处理图像像素数据时,可在PixelMapArrayBuffer间转换:

typescript

import { image } from '@kit.ImageKit';

// PixelMap → ArrayBuffer
function pixelMapToArrayBuffer(pm: image.PixelMap): ArrayBuffer {
    const buffer = new ArrayBuffer(pm.getPixelBytesNumber());
    pm.readPixelsToBufferSync(buffer);
    return buffer;
}

// ArrayBuffer → PixelMap(需结合ImageSource)
function arrayBufferToPixelMap(buffer: ArrayBuffer): image.PixelMap {
    const imageSource = image.createImageSource(buffer);
    return imageSource.createPixelMapSync({
        desiredPixelFormat: image.PixelMapFormat.RGBA_8888
    });
}

3.5 与Native层交互(ArkTS → C++)

在鸿蒙跨语言调用中,Native层收到的是Uint8Array类型参数,通过.buffer获取ArrayBuffer后可传递给C++层处理:

typescript

// ArkTS侧
function passToNative(data: Uint8Array): void {
    const buffer = data.buffer; // 获取ArrayBuffer
    // 调用Native接口,传递buffer
    nativeModule.processData(buffer);
}

四、深度注意:内存共享与独立副本

这是最容易踩的坑——转换时内存是否共享

转换方式 内存关系 修改是否互相影响
new Uint8Array(buffer) 共享 ✅ 是
uint8Array.buffer 共享 ✅ 是
buffer.slice(0) 再构造 独立 ❌ 否
手动new ArrayBuffer + set() 独立 ❌ 否

何时需要独立副本

  • 序列化/反序列化传输时,不希望数据被意外篡改

  • 跨线程传递时,原数据可能被回收或修改

  • 需要对数据进行修改,但不想影响原始缓冲区

创建独立副本的标准写法

typescript

// 从Uint8Array创建独立ArrayBuffer
function createIndependentBuffer(src: Uint8Array): ArrayBuffer {
    const dst = new ArrayBuffer(src.length);
    const dstView = new Uint8Array(dst);
    dstView.set(src); // 复制数据
    return dst;
}

五、总结速查表

需求 代码示例
ArrayBuffer → Uint8Array(共享) new Uint8Array(buffer)
Uint8Array → ArrayBuffer(共享) uint8Array.buffer
ArrayBuffer → Uint8Array(独立) new Uint8Array(buffer.slice(0))
Uint8Array → ArrayBuffer(独立) const b = new ArrayBuffer(a.length); new Uint8Array(b).set(a);
字符串 → ArrayBuffer new util.TextEncoder().encodeInto(str).buffer
ArrayBuffer → 字符串 util.TextDecoder.create('utf-8').decodeWithStream(new Uint8Array(buffer))
Base64 → ArrayBuffer new util.Base64Helper().decodeSync(str).buffer
ArrayBuffer → Base64 new util.Base64Helper().encodeToStringSync(new Uint8Array(buffer))

理解ArrayBufferUint8Array的关系是鸿蒙二进制编程的基石。简单转换用.buffernew Uint8Array()即可,涉及数据独立性时需手动复制。结合util模块的编解码能力,可覆盖绝大多数日常开发场景。

Logo

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

更多推荐