llama.cppAndroidKotlinintegrationon-device AIVulkan
llama.cpp Android 集成:Kotlin 与 Vulkan 指南
将 llama.cpp 集成到 Android 应用,使用 Kotlin。涵盖 JNI 绑定、Vulkan GPU 加速、模型加载、内存管理与设备测试。
Edward Xi Yang
llama.cpp 在 Android 设备上使用 CPU 多线程和 Vulkan GPU 加速运行 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 支持取决于设备:
- 骁龙 8 Gen 2+:完整 Vulkan 计算支持,性能最佳
- Tensor G3/G4:良好的 Vulkan 支持
- 骁龙 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
}