Android开发笔记[16]-简单使用wasmedge运行时
摘要
使用wasmedge运行时在Android实现"容器化"运行,将fibonacci计算函数打包进入wasm然后打包进入APK中.
关键信息
- Android Studio:Iguana | 2023.2.1
- Gradle:distributionUrl=https://services.gradle.org/distributions/gradle-7.2-bin.zip
- jvmTarget = '1.8'
- minSdk 23
- targetSdk 31
- compileSdk 31
- 开发语言:Kotlin,Java
- ndkVersion = '23.1.7779620'
原理简介
wasm简介
[https://juejin.cn/post/7033585275791998990]
[https://developer.mozilla.org/zh-CN/docs/WebAssembly/Concepts]
WebAssembly 是一种运行在现代网络浏览器中的新型代码,并且提供新的性能特性和效果。它设计的目的不是为了手写代码而是为诸如 C、C++ 和 Rust 等低级源语言提供一个高效的编译目标。
对于网络平台而言,这具有巨大的意义——这为客户端 app 提供了一种在网络平台以接近本地速度的方式运行多种语言编写的代码的方式;在这之前,客户端 app 是不可能做到的。
而且,你在不知道如何编写 WebAssembly 代码的情况下就可以使用它。WebAssembly 的模块可以被导入的到一个网络 app(或 Node.js)中,并且暴露出供 JavaScript 使用的 WebAssembly 函数。JavaScript 框架不但可以使用 WebAssembly 获得巨大性能优势和新特性,而且还能使得各种功能保持对网络开发者的易用性。
wasmedge简介
[https://www.bilibili.com/video/BV1A341177qn]
[https://wasmedge.org/docs/contribute/source/os/android/apk/]
[https://gitcode.com/WasmEdge/WasmEdge]
[https://wasmedge.org/docs/contribute/source/os/android/build/]
WasmEdge (之前名为 SSVM) 是为边缘计算优化的轻量级、高性能、可扩展的 WebAssembly (Wasm) 虚拟机,可用于云原生、边缘和去中心化的应用。WasmEdge 是目前市场上 最快的 Wasm 虚拟机。WasmEdge 是由 CNCF (Cloud Native Computing Foundation 云原生计算基金会)托管的官方沙箱项目。其应用场景包括 serverless apps, 嵌入式函数、微服务、智能合约和 IoT 设备。
The WasmEdge Runtime releases come with pre-built binaries for the Android OS. Why WasmEdge on Android?
- Native speed & sandbox safety for Android apps
- Support multiple dev languages — eg C, Rust, Go & JS
- Embed 3rd party functions in your android app
- Kubernetes managed android apps
For the Android 10 or greater versions, SELinux will disallow the untrusted applications' exec() system call to execute the binaries in home or /data/local/tmp folder.
The Android SELinux policy will disallow the untrusted applications to access the /data/local/tmp folder.
在本节中,将构建一个“常规”的 Android 应用程序(即,可以安装在 Android 设备上的 APK 文件)。该 APK 应用程序嵌入了一个 WasmEdge 运行时。它可以通过嵌入的 WasmEdge 调用 WebAssembly 函数。这样做的好处是,开发者可以安全地将多种不同语言(例如,Rust、JS、Grain、TinyGo 等)编写的高性能函数嵌入到 Kotlin 应用程序中。
MainActivity.kt
class MainActivity : AppCompatActivity() {
lateinit var lib: NativeLib
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val tv = findViewById<TextView>(R.id.tv_text)
lib = NativeLib(this)
Thread {
val lines = Vector<String>()
val idxArr = arrayOf(20, 25, 28, 30, 32)
for (idx: Int in idxArr) {
lines.add("running fib(${idx}) ...")
runOnUiThread {
tv.text = lines.joinToString("\n")
}
val begin = System.currentTimeMillis()
val retVal = lib.wasmFibonacci(idx)
val end = System.currentTimeMillis()
lines.removeLast()
lines.add("fib(${idx}) -> ${retVal}, ${end - begin}ms")
runOnUiThread {
tv.text = lines.joinToString("\n")
}
}
}.start()
}
}
NativeLib.kt
class NativeLib(ctx : Context) {
private external fun nativeWasmFibonacci(imageBytes : ByteArray, idx : Int ) : Int
companion object {
init {
System.loadLibrary("wasmedge_lib")
}
}
private var fibonacciWasmImageBytes : ByteArray = ctx.assets.open("fibonacci.wasm").readBytes()
fun wasmFibonacci(idx : Int) : Int{
return nativeWasmFibonacci(fibonacciWasmImageBytes, idx)
}
}
wasmedge_lib.cpp
extern "C" JNIEXPORT jint JNICALL
Java_org_wasmedge_native_1lib_NativeLib_nativeWasmFibonacci(
JNIEnv *env, jobject, jbyteArray image_bytes, jint idx) {
jsize buffer_size = env->GetArrayLength(image_bytes);
jbyte *buffer = env->GetByteArrayElements(image_bytes, nullptr);
WasmEdge_ConfigureContext *conf = WasmEdge_ConfigureCreate();
WasmEdge_ConfigureAddHostRegistration(conf, WasmEdge_HostRegistration_Wasi);
WasmEdge_VMContext *vm_ctx = WasmEdge_VMCreate(conf, nullptr);
const WasmEdge_String &func_name = WasmEdge_StringCreateByCString("fib");
std::array<WasmEdge_Value, 1> params{WasmEdge_ValueGenI32(idx)};
std::array<WasmEdge_Value, 1> ret_val{};
const WasmEdge_Result &res = WasmEdge_VMRunWasmFromBuffer(
vm_ctx, (uint8_t *)buffer, buffer_size, func_name, params.data(),
params.size(), ret_val.data(), ret_val.size());
WasmEdge_VMDelete(vm_ctx);
WasmEdge_ConfigureDelete(conf);
WasmEdge_StringDelete(func_name);
env->ReleaseByteArrayElements(image_bytes, buffer, 0);
if (!WasmEdge_ResultOK(res)) {
return -1;
}
return WasmEdge_ValueGetI32(ret_val[0]);
}
[https://github.com/WasmEdge/WasmEdge/blob/master/examples/wasm/fibonacci.wat]
factorial.wat
(module
(func $fib (export "fib") (param $n i32) (result i32)
local.get $n
i32.const 2
i32.lt_s
if
i32.const 1
return
end
local.get $n
i32.const 2
i32.sub
call $fib
local.get $n
i32.const 1
i32.sub
call $fib
i32.add
return
)
)
CMakeLists.txt
cmake_minimum_required(VERSION 3.22.1)
project("wasmedge_lib")
set(WASMEDGE_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../..)
set(WASMEDGE_BUILD_PLUGINS OFF CACHE BOOL "" FORCE)
set(WASMEDGE_BUILD_TOOLS OFF CACHE BOOL "" FORCE)
set(WASMEDGE_BUILD_SHARED_LIB ON CACHE BOOL "" FORCE)
set(WASMEDGE_USE_LLVM OFF CACHE BOOL "" FORCE)
set(WASMEDGE_FORCE_DISABLE_LTO ON CACHE BOOL "" FORCE) # fast fixed problem for `-fuse-ld=gold`, we use lld.
if (CMAKE_GENERATOR STREQUAL Ninja)
set(CMAKE_JOB_POOLS "link=2")
set(CMAKE_JOB_POOL_LINK link)
endif()
add_subdirectory(${WASMEDGE_SOURCE_DIR} WasmEdge)
include_directories(${WasmEdge_BINARY_DIR}/include/api)
add_library(
wasmedge_lib
SHARED
wasmedge_lib.cpp)
target_link_libraries(
wasmedge_lib
log
wasmedge_shared
)
实现
- 下载例程代码
# JAVA版本
git clone --recursive https://github.com/hangedfish/WasmEdge_AndroidExample.git
cd WasmEdge_AndroidExample
# 或者Kotlin版本
git clone --recursive https://github.com/WasmEdge/WasmEdge
# Android Studio打开WasmEdge/utils/android/app
- JAVA版需要配置cmake 3.18.1版本
[https://cmake.org/cmake/help/v3.18/release/3.18.html]
[https://cmake.org/files/v3.18/]
[https://github.com/Kitware/CMake/tree/v3.18.1]
放置3.18.1文件夹到/Users/workspace/Library/Android/sdk/cmake
目录,
配置sdk路径:
local.properties
sdk.dir=/Users/workspace/Library/Android/sdk
cmake.dir=/Users/workspace/Library/Android/sdk/cmake/3.18.1
-
kotlin版本需要手动放置JAVA版本的asset目录的
fibonacci.wasm
文件到asset目录; -
gradle同步&编译&运行测试
效果
JAVA版本 | Kotlin版本 |
---|---|