
llama.cpp en Android: guia de integracion con Kotlin
Guia paso a paso para integrar llama.cpp en una app Android con Kotlin. Bindings JNI, aceleracion GPU con Vulkan, carga de modelos y gestion de memoria en el espectro de dispositivos Android.
llama.cpp ejecuta modelos de lenguaje GGUF en dispositivos Android usando multi-threading de CPU y aceleracion GPU con Vulkan. El proyecto llama.android proporciona bindings Kotlin pre-construidos a traves de JNI, haciendo la integracion directa para desarrolladores Kotlin.
Esta guia cubre el camino completo de integracion desde la configuracion del proyecto hasta el despliegue en produccion.
Opciones de integracion
Opcion 1: Libreria llama.android (Recomendado)
El repositorio de llama.cpp incluye llama.android, una libreria Android pre-construida con bindings Kotlin. Este es el camino mas rapido a IA en el dispositivo funcional.
Agregala a tu proyecto:
- Clona o descarga el modulo llama.android del repositorio de llama.cpp
- Incluyelo como modulo en tu proyecto Android
- Agrega la dependencia en el
build.gradle.ktsde tu app
dependencies {
implementation(project(":llama"))
}
Opcion 2: Compilar desde fuente
Para mas control, compila llama.cpp como libreria nativa usando el Android NDK:
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
Esto produce libllama.so que incluyes en tu directorio jniLibs.
Opcion 3: AAR pre-compilado
Algunos proyectos de la comunidad publican llama.cpp como un AAR (Android Archive) que puedes incluir como dependencia Maven. Busca versiones recientes y mantenidas.
Configuracion del proyecto
Requisitos minimos
- Android API 26+ (Android 8.0)
- Arquitectura objetivo ARM64 (arm64-v8a)
- NDK r25+ para compilar codigo nativo
- 4GB+ RAM en dispositivo objetivo (para modelos 1B)
Configuracion de build
// app/build.gradle.kts
android {
defaultConfig {
ndk {
abiFilters += "arm64-v8a" // Solo ARM 64-bit
}
}
}
Permisos
No se requieren permisos especiales para inferencia. Para descarga de modelos:
<uses-permission android:name="android.permission.INTERNET" />
Cargando un modelo
class LlamaEngine(private val context: Context) {
private var model: Long = 0 // Puntero nativo
private var ctx: Long = 0 // Puntero de contexto nativo
suspend fun loadModel(modelPath: String) = withContext(Dispatchers.Default) {
// Cargar modelo con aceleracion GPU
model = LlamaNative.loadModel(
modelPath = modelPath,
nGpuLayers = 99, // Descargar todas las capas a Vulkan
)
require(model != 0L) { "Fallo al cargar modelo" }
// Crear contexto de inferencia
ctx = LlamaNative.createContext(
model = model,
nCtx = 2048, // Ventana de contexto
nThreads = 4, // Hilos CPU
nBatch = 512, // Tamano de lote
)
require(ctx != 0L) { "Fallo al crear contexto" }
}
fun unload() {
if (ctx != 0L) {
LlamaNative.freeContext(ctx)
ctx = 0
}
if (model != 0L) {
LlamaNative.freeModel(model)
model = 0
}
}
}
Aceleracion GPU con Vulkan
Vulkan es la API de computo GPU de Android. llama.cpp la usa para acelerar operaciones matriciales durante la inferencia. Habilitala configurando nGpuLayers al conteo de capas del modelo.
El soporte de Vulkan depende del dispositivo:
- Snapdragon 8 Gen 2+: Soporte completo de computo Vulkan, mejor rendimiento
- Tensor G3/G4: Buen soporte de Vulkan
- Snapdragon 7 Gen 3+: Vulkan soportado, aceleracion moderada
- Dispositivos antiguos/economicos: Pueden carecer de computo Vulkan. Recurre a CPU automaticamente.
Verifica la disponibilidad de Vulkan en tiempo de ejecucion:
fun isVulkanAvailable(): Boolean {
return try {
val vk = android.hardware.HardwareBuffer::class.java
android.os.Build.VERSION.SDK_INT >= 26
// Mas robusto: intentar crear una instancia Vulkan
} catch (e: Exception) {
false
}
}
Generando texto
class LlamaEngine(private val context: Context) {
// ... loadModel y unload de arriba
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)
// Despachar al hilo principal para actualizacion de UI
withContext(Dispatchers.Main) {
onToken(token)
}
}
result.toString()
}
}
Integracion con 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()
}
}
UI con Jetpack Compose
@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)) {
// Area de respuesta
Text(
text = response,
modifier = Modifier.weight(1f).verticalScroll(rememberScrollState())
)
// Area de entrada
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("Enviar")
}
}
}
}
Gestion de memoria
La gestion de memoria de Android es agresiva. El sistema elimina procesos en segundo plano para liberar RAM y puede terminar tu app bajo presion de memoria.
Verificacion de memoria disponible
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()
// Reservar 500MB para app y SO
return available > modelSizeMb + 500
}
Gestion del ciclo de vida
class AiService : LifecycleObserver {
private var engine: LlamaEngine? = null
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
fun onResume() {
// Considerar cargar modelo si la pantalla de IA esta activa
}
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
fun onPause() {
// Descargar modelo para liberar memoria
engine?.unload()
}
}
Manejador onTrimMemory
override fun onTrimMemory(level: Int) {
super.onTrimMemory(level)
if (level >= ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW) {
engine?.unload()
}
}
Entrega del modelo
Asset Delivery (Play Feature Delivery)
Para modelos mayores de 150MB, usa Play Asset Delivery para evitar el limite de tamano de 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") // o "fast-follow" o "on-demand"
}
}
Descarga post-instalacion
Descarga el modelo despues de la instalacion:
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
// Actualizar UI de progreso
}
}
}
true
} ?: false
}
}
Usa WorkManager para descargas en segundo plano que sobrevivan reinicios de la app.
Pruebas en diferentes dispositivos
El espectro de dispositivos Android es amplio. Prueba en:
- Flagship (SD 8 Gen 3, 12GB): Verifica rendimiento en mejor caso
- Gama media (SD 7 Gen 3, 6-8GB): Verifica que el modelo 1B se ejecuta fluido
- Economico (SD 6 Gen 3, 4GB): Verifica respaldo gracioso si el modelo no puede cargar
- Flagship antiguo (SD 8 Gen 1, 8GB): Verifica que flagships de 2 anos funcionan
Usa Firebase Test Lab o BrowserStack para pruebas de cobertura de dispositivos sin poseer cada dispositivo.
Checklist de produccion
- El modelo carga y genera correctamente en el dispositivo de especificacion minima
- La aceleracion Vulkan se detecta y usa cuando esta disponible
- El respaldo a CPU funciona cuando Vulkan no esta disponible
- La verificacion de memoria previene la carga en dispositivos con poca RAM
- El modelo se descarga en onPause/onTrimMemory
- El progreso de descarga se muestra correctamente para entrega post-instalacion
- La integridad del modelo se verifica despues de la descarga (SHA256)
- La generacion puede ser cancelada por el usuario
- La app funciona normalmente cuando el modelo no esta disponible
El modelo GGUF es lo que determina la calidad. Un modelo fine-tuned con tus datos de dominio (via Ertas o similar) producira respuestas especificas para el proposito de tu app. La integracion con llama.cpp es identica independientemente del modelo que uses.
Ship AI that runs on your users' devices.
Early bird pricing starts at $14.50/mo — locked in for life. Plans for builders and agencies.
Keep reading

AI in Android Apps: ML Kit, Cloud APIs, and On-Device LLMs Compared
Three paths to AI in your Android app. Google ML Kit for common tasks, cloud APIs for full LLM capability, and on-device models via llama.cpp for cost and privacy. A practical comparison for Kotlin developers.

LLM Benchmarks on Android: Snapdragon, Tensor, and Exynos Compared
Real benchmark data for running LLMs on Android via llama.cpp. Token speeds across Snapdragon 8 Gen 2/3, Tensor G3/G4, Exynos 2400, and mid-range chipsets with practical deployment guidance.

llama.cpp on iOS: A Swift Integration Guide
Step-by-step guide to integrating llama.cpp into an iOS app. Project setup, Metal GPU acceleration, model loading, token streaming, and memory management for production deployment.