摘要
使用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版本 |
---|---|