在 Android 上用上原生的 xxHash,性能直接拉满

哈希计算在日常开发中太常见了------数据校验、缓存 key 生成、文件去重,哪哪都离不开。提到哈希,大家第一反应可能是 MD5 或者 SHA,但如果你只需要快速生成一个指纹,不需要密码学安全性,那 xxHash 绝对是更好的选择。

问题是,Android 上好用的 xxHash 库不太好找。纯 Java 实现的性能跟 C 原版差了好几倍,而现有的 JNI 封装要么年久失修,要么不支持 Android 15 的 16KB 页面大小对齐。

所以我自己造了个轮子。

不说废话

fast-xxhash-android 直接编译 xxHash 的 C 源码,通过 JNI 暴露给上层,支持 XXH32、XXH64、XXH3-64、XXH3-128 四种算法,覆盖 armeabi-v7a、arm64-v8a、x86、x86_64 四个架构。

引入一行搞定:

kotlin 复制代码
implementation("io.github.limuyang2:xxhash:<version>")

用起来也简单:

kotlin 复制代码
// Kotlin 扩展函数,直接调
val hash = "Hello, World!".xxh64()

// Java 也行
long hash = XXHash.xxh64(data, 0);

为什么不用纯 Java 实现?

xxHash 官方给出的速度数据是这样的:

算法 速度
XXH3-64 31.5 GB/s
XXH64 19.3 GB/s
XXH32 9.8 GB/s
MD5 0.65 GB/s
SHA-256 0.42 GB/s

XXH3 比 MD5 快了将近 50 倍

但这个数据是 C 原版的。换成纯 Java 实现,性能会打折扣------JVM 的数组访问有边界检查,SIMD 指令也没法直接用,大块数据计算的时候差距更明显。尤其 Android 设备性能参差不齐,中低端机上这个差距会被放大。

直接编译 C 源码走 JNI 是最靠谱的方案,调用开销在微秒级别,可以忽略不计。

16KB Page Size 的事

Android 15 开始支持 16KB 页面大小,部分新设备(比如 Pixel 9 系列)已经默认开启了。如果你的 native 库没有做 16KB 对齐,在这些设备上可能会出问题。

这个问题很多老库都没适配,本库已经处理好了,SO 文件构建时就做了对齐,不用担心兼容性。

怎么用

Kotlin

StringByteArrayByteBuffer 都提供了扩展函数:

kotlin 复制代码
import io.github.limuyang2.xxhash.lib.*

// 字符串直接算
val h64 = "Hello, World!".xxh64()

// 带 seed
val h64s = "Hello, World!".xxh64(seed = 42)

// ByteArray 也行
val data = "some data".toByteArray()
data.xxh32()
data.xxh3As128()  // 返回 LongArray[2],128 位

// 只算数组的一部分,不用拷贝
data.xxh32(offset = 7, length = 6)

// ByteBuffer 也可以,不会动 position
val buffer = ByteBuffer.wrap(data)
buffer.xxh64()

Java

Java 就用静态方法,@JvmStatic 注解都加好了:

java 复制代码
import io.github.limuyang2.xxhash.lib.XXHash;

byte[] data = "Hello, World!".getBytes();

long h32 = XXHash.xxh32(data, 0);
long h64 = XXHash.xxh64(data, 0);
long h3 = XXHash.xxh3_64bits(data);
long[] h128 = XXHash.xxh3_128bits(data);

// 数组切片
long hash = XXHash.xxh32Bytes(full, 7, 6, 0);

支持的算法

算法 说明 返回值
XXH32 经典 32 位哈希,兼容性好 Long
XXH64 64 位哈希,速度和分布更优 Long
XXH3-64 最新一代,速度最快 Long
XXH3-128 128 位哈希,碰撞概率极低 LongArray2

日常用 xxh64() 就够了,追求极致速度选 xxh3As64(),对碰撞敏感的场景用 xxh3As128()

实现细节

不多说,核心就三层:

  1. C 层 --- 直接用的 xxHash v0.8.3 官方源码,-O3 编译,静态链接进 libmuxxhash.so
  2. JNI 层 --- 薄薄一层桥接代码,拿到 jbyteArray 指针直接丢给 xxHash 算完返回
  3. Kotlin 层 --- object 单例 + @JvmStatic + 扩展函数

没有多余的依赖,SO 文件也很小。

最后

感兴趣的可以看看源码,实现很简洁:

有问题欢迎提 issue。

相关推荐
齊家治國平天下2 小时前
AAOS 电源管理深度解析:休眠/唤醒/功耗优化
android·车载系统·aaos·aosp·电源管理·休眠唤醒·carpowermanager
Xzaveir2 小时前
企业号码展示不可观测怎么办:状态机、拨测事件与异常回放
android·人工智能
2501_916007472 小时前
iOS和macOS应用程序性能分析和优化工具使用综合指南
android·macos·ios·小程序·uni-app·iphone·webview
黄林晴3 小时前
Nearby Connections 重大变更:不再自动开启 Wi-Fi / 蓝牙,适配指南
android
恋猫de小郭3 小时前
Flutter 全新真 3D 实现,用 flutter_scene 能开发一个「我的世界」
android·前端·flutter
HLC++16 小时前
Linux的进程间通信
android·linux·服务器
爱笑鱼20 小时前
Binder(二):AIDL 生成的 Proxy、Stub 和 Parcel 到底在做什么?
android
爱笑鱼20 小时前
Binder(一):一次方法调用,究竟怎样跨进程执行?
android
壮哥_icon21 小时前
【Android 系统开发】使用 BAT 脚本高效自动化管理 /system/priv-app/ 系统应用(安装与卸载)
android·运维·自动化
用户69371750013841 天前
Claude Code终端日志Token占用实测:一个过滤器砍掉60%-90%
android·前端·后端