llama.cpp Android 整合:Kotlin 與 Vulkan 指南
將 llama.cpp 整合到 Android 應用程式,使用 Kotlin。涵蓋 JNI 繫結、Vulkan GPU 加速、模型載入、記憶體管理與裝置測試。
llama.cpp 使用 CPU 多執行緒和 Vulkan GPU 加速在 Android 裝置上運行 GGUF 語言模型。llama.android 專案透過 JNI 提供預建置的 Kotlin 繫 結,使 Kotlin 開發者能夠輕鬆整合。
本指南涵蓋從專案設定到正式環境部署的完整整合路徑。
整合選項
選項 1:llama.android 函式庫(推薦)
llama.cpp 儲存庫包含 llama.android,一個帶有 Kotlin 繫結的預建置 Android 函式庫。這是實現裝置端 AI 最快的途徑。
將其新增到您的專案:
- 從 llama.cpp 儲存庫克隆或下載 llama.android 模組
- 將其作為模組包含在您的 Android 專案中
- 在應用程式的
build.gradle.kts中新增相依性
dependencies {
implementation(project(":llama"))
}選項 2:從原始碼建構
如需更多控制,使用 Android NDK 將 llama.cpp 建構為原生函式庫:
mkdir build-android && cd build-android
cmake .. \
-DCMAKE_TOOLCHAIN_FILE=$NDK_PATH/build/cmake/android.toolchain.cmake \
-DANDROID_ABI=arm64-v8a \
-DANDROID_PLATFORM=android-26 \
-DLLAMA_VULKAN=ON \
-DBUILD_SHARED_LIBS=ON
cmake --build . --config Release這會產生 libllama.so,您需將其包含在 jniLibs 目錄中。
選項 3:預建置 AAR
部分社群專案將 llama.cpp 發佈為 AAR(Android Archive),您可以作為 Maven 相依性包含。請檢查最近且維護中的版本。
專案設定
最低需求
- Android API 26+(Android 8.0)
- ARM64(arm64-v8a)目標架構
- NDK r25+ 用於建構原生程式碼
- 目標裝置需 4GB+ RAM(用於 1B 模型)
建構組態
// app/build.gradle.kts
android {
defaultConfig {
ndk {
abiFilters += "arm64-v8a" // 僅 64 位元 ARM
}
}
}權限
推論不需要特殊權限。模型下載需要:
<uses-permission android:name="android.permission.INTERNET" />載入模型
class LlamaEngine(private val context: Context) {
private var model: Long = 0 // 原生指標
private var ctx: Long = 0 // 原生上下文指標
suspend fun loadModel(modelPath: String) = withContext(Dispatchers.Default) {
// 使用 GPU 加速載入模型
model = LlamaNative.loadModel(
modelPath = modelPath,
nGpuLayers = 99, // 將所有層卸載到 Vulkan
)
require(model != 0L) { "Failed to load model" }
// 建立推論上下文
ctx = LlamaNative.createContext(
model = model,
nCtx = 2048, // 上下文視窗
nThreads = 4, // CPU 執行緒
nBatch = 512, // 批次大小
)
require(ctx != 0L) { "Failed to create context" }
}
fun unload() {
if (ctx != 0L) {
LlamaNative.freeContext(ctx)
ctx = 0
}
if (model != 0L) {
LlamaNative.freeModel(model)
model = 0
}
}
}Vulkan GPU 加速
Vulkan 是 Android 的 GPU 運算 API。llama.cpp 使用它來加速推論期間的矩陣運算。將 nGpuLayers 設為模型的層數即可啟用。
Vulkan 支援取決於裝置:
- Snapdragon 8 Gen 2+:完整 Vulkan 運算支援,最佳效能
- Tensor G3/G4:良好的 Vulkan 支援
- Snapdragon 7 Gen 3+:支援 Vulkan ,中等加速
- 較舊/入門裝置:可能缺乏 Vulkan 運算支援。自動降級為 CPU。
執行時期檢查 Vulkan 可用性:
fun isVulkanAvailable(): Boolean {
return try {
val vk = android.hardware.HardwareBuffer::class.java
android.os.Build.VERSION.SDK_INT >= 26
// 更穩健的方式:嘗試建立 Vulkan 實例
} catch (e: Exception) {
false
}
}生成文字
class LlamaEngine(private val context: Context) {
// ... 上方的 loadModel 和 unload
suspend fun generate(
prompt: String,
maxTokens: Int = 256,
temperature: Float = 0.7f,
onToken: (String) -> Unit = {}
): String = withContext(Dispatchers.Default) {
val result = StringBuilder()
LlamaNative.generate(
context = ctx,
prompt = prompt,
maxTokens = maxTokens,
temperature = temperature,
) { token ->
result.append(token)
// 分派到主執行緒進行 UI 更新
withContext(Dispatchers.Main) {
onToken(token)
}
}
result.toString()
}
}ViewModel 整合
class AiViewModel(application: Application) : AndroidViewModel(application) {
private val engine = LlamaEngine(application)
private val _response = MutableStateFlow("")
val response: StateFlow<String> = _response
private val _isGenerating = MutableStateFlow(false)
val isGenerating: StateFlow<Boolean> = _isGenerating
fun loadModel(path: String) {
viewModelScope.launch {
engine.loadModel(path)
}
}
fun generate(prompt: String) {
viewModelScope.launch {
_isGenerating.value = true
_response.value = ""
engine.generate(prompt, maxTokens = 256) { token ->
_response.value += token
}
_isGenerating.value = false
}
}
override fun onCleared() {
engine.unload()
super.onCleared()
}
}Jetpack Compose UI
@Composable
fun AiChatScreen(viewModel: AiViewModel = viewModel()) {
val response by viewModel.response.collectAsState()
val isGenerating by viewModel.isGenerating.collectAsState()
var input by remember { mutableStateOf("") }
Column(modifier = Modifier.fillMaxSize().padding(16.dp)) {
// 回應區域
Text(
text = response,
modifier = Modifier.weight(1f).verticalScroll(rememberScrollState())
)
// 輸入區域
Row(modifier = Modifier.fillMaxWidth()) {
TextField(
value = input,
onValueChange = { input = it },
modifier = Modifier.weight(1f),
enabled = !isGenerating
)
Button(
onClick = {
viewModel.generate(input)
input = ""
},
enabled = !isGenerating
) {
Text("Send")
}
}
}
}記憶體管理
Android 的記憶體管理相當積極。系統會終止背景程序以釋放 RAM,並在記憶體壓力下可能終止您的應用程式。
可用記憶體檢查
fun getAvailableMemoryMb(): Long {
val memInfo = ActivityManager.MemoryInfo()
val activityManager = getSystemService(ACTIVITY_SERVICE) as ActivityManager
activityManager.getMemoryInfo(memInfo)
return memInfo.availMem / (1024 * 1024)
}
fun canLoadModel(modelSizeMb: Long): Boolean {
val available = getAvailableMemoryMb()
// 為應用程式和作業系統保留 500MB
return available > modelSizeMb + 500
}生命週期管理
class AiService : LifecycleObserver {
private var engine: LlamaEngine? = null
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
fun onResume() {
// 如果 AI 畫面處於活躍狀態,考慮載入模型
}
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
fun onPause() {
// 卸載模型以釋放記憶體
engine?.unload()
}
}onTrimMemory 處理
override fun onTrimMemory(level: Int) {
super.onTrimMemory(level)
if (level >= ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW) {
engine?.unload()
}
}模型交付
資源交付(Play Feature Delivery)
對於超過 150MB 的模型,使用 Play Asset Delivery 以避免 APK 大小限制:
// build.gradle.kts
assetPacks += ":model_pack"
// model_pack/build.gradle.kts
plugins {
id("com.android.asset-pack")
}
assetPack {
packName.set("model_pack")
dynamicDelivery {
deliveryType.set("install-time") // 或 "fast-follow" 或 "on-demand"
}
}安裝後下載
安裝後下載模型:
suspend fun downloadModel(url: String, destination: File): Boolean {
return withContext(Dispatchers.IO) {
val client = OkHttpClient()
val request = Request.Builder().url(url).build()
val response = client.newCall(request).execute()
response.body?.let { body ->
val totalBytes = body.contentLength()
destination.outputStream().use { output ->
body.byteStream().use { input ->
val buffer = ByteArray(8192)
var bytesRead: Long = 0
var read: Int
while (input.read(buffer).also { read = it } != -1) {
output.write(buffer, 0, read)
bytesRead += read
val progress = bytesRead.toFloat() / totalBytes
// 更新進度 UI
}
}
}
true
} ?: false
}
}使用 WorkManager 進行能在應用程式重啟後繼續的背景下載。
跨裝置測試
Android 裝置光譜非常廣泛。在以下裝置上測試:
- 旗艦機(SD 8 Gen 3,12GB):驗證最佳效能
- 中階機(SD 7 Gen 3,6-8GB):驗證 1B 模型流暢運行
- 入門機(SD 6 Gen 3,4GB):驗證模型無法載入時的優雅降級
- 舊旗艦機(SD 8 Gen 1,8GB):驗證 2 年前的旗艦機可正常運作
使用 Firebase Test Lab 或 BrowserStack 進行裝置覆蓋測試,無需擁有每一台裝置。
正式環境檢查清單
- 模型在最低規格裝置上正確載入和生成
- 可用時偵測並使用 Vulkan 加速
- Vulkan 不可用時 CPU 降級正常運作
- 記憶 體檢查防止在低 RAM 裝置上載入
- 在 onPause/onTrimMemory 時卸載模型
- 安裝後交付的下載進度正確顯示
- 下載後驗證模型完整性(SHA256)
- 使用者可以取消生成
- 模型不可用時應用程式正常運作
GGUF 模型決定了品質。在您的領域資料上微調的模型(透過 Ertas 或類似平台)將產生針對應用程式目的的特定回應。無論使用哪個模型,llama.cpp 的整合方式都是相同的。
Ship AI that runs on your users' devices.
Free plan with 30 credits/mo, no card required. Paid plans from $10/mo USD.
Keep reading
Android 應用程式的 AI:ML Kit、雲端 API 與裝置端 LLM 比較
Android 應用程式的三條 AI 路徑。Google ML Kit 用於常見任務,雲端 API 提供完整 LLM 能力,裝置端模型透過 llama.cpp 實現成本與隱私。為 Kotlin 開發者提供的實用比較。
llama.cpp 在 iOS 上的應用:Swift 整合指南
將 llama.cpp 整合到 iOS 應用程式的分步指南。專案設定、Metal GPU 加速、模型載入、token 串流和記憶體管理,適用於正式環境部署。
Android LLM 效能基準:Snapdragon 對比 Tensor
使用 llama.cpp 實測 1B 與 3B 模型在 Snapdragon、Tensor、Exynos 和 Dimensity 手機上的 token 速度,以及每個層級所需的 RAM。