Memelm is an Android application that runs the MiniCPM multimodal language model fully on-device. It leverages llama.cpp under the hood via a JNI bridge, downloads model weights on demand, and wires everything together into a single integrated app — no cloud backend required.
memelm-android/
├── app/ # Entry point — integrates all modules into the running application
├── memelm/ # Core inference engine — MiniCPM via llama.cpp + JNI
├── modelpull/ # Model downloader — fetches GGUF weights and mmproj via HTTP
└── constant/ # Shared configuration — URLs, keys, and build-time constants
The modules are intentionally separated by responsibility. constant feeds into both memelm and modelpull. The app module sits at the top of the dependency graph and assembles everything.
Per-Friday/5/June/2026.
github-attachment.mp4
This is the core library module responsible for loading and running the MiniCPM multimodal model entirely on the device.
Responsibilities:
- Bridges into llama.cpp via the Java Native Interface (JNI), exposing a Kotlin/Java API over the native C++ inference engine.
- Loads the main GGUF model file and the associated mmproj (multimodal projector) file for vision-language tasks.
- Manages the llama.cpp context lifecycle. Including initialization, sampling, token generation, and teardown.
- Handles tokenization and decoding for both text and image inputs.
- Exposes a clean, suspendable API so inference can be called from coroutines without blocking the main thread.
Key internals:
| Layer | Technology |
|---|---|
| Inference runtime | llama.cpp (C++) |
| Android integration | JNI (System.loadLibrary) |
| Native build | CMake / Android NDK |
| API surface | Kotlin (suspend functions / Flow) |
Note:
- The native
.solibraries are compiled for ABIarm64-v8aonly and bundled inside this module's AAR.- Sample specification from official llama.cpp at
LIB_REF_INFORMATION.md
This module handles everything related to fetching model files from a remote source before the inference engine can run.
Responsibilities:
- Downloads the GGUF model weights file (the main language model).
- Downloads the mmproj (multimodal projector) weights file required for image understanding.
- Reports real-time download progress so the UI can display a progress bar or percentage.
- Persists downloaded files to the app's internal storage in a predictable, versioned path.
Key internals:
| Concern | Technology |
|---|---|
| HTTP client | OkHttp |
| REST abstraction | Retrofit |
| Progress tracking | Response Retrofit to Worker Manager |
This RoomDB persistance module only to maintain the conversation memory to support the chat environment. Naive RAG is only due by query + best_matching_record = augmented_input.
Responsibilities:
- Main place to save conversation history across app.
- Past messages retrieval and processing to get augmented_input.
- Have conversation list so the user can just select conversation they want.
- Ability to save image locally and save the path as copy Uri.
- The sentence save as given llm style which markdown, so the display use markdown style renderer
Key internals:
| Concern | Technology |
|---|---|
| RoomDB | Room |
| Markdown | Halilibo |
A configuration-only module that acts as the single source of truth for all compile-time and runtime constants shared across the other modules.
Design principle: This module depends on nothing and is depended on by everything. Keeping constants here prevents duplication and makes global changes a one-file edit.
The top-level Android application module that wires all other modules together into the final installable APK.
Responsibilities:
- Integrate all modules based on their specific functionality.
- Composes the UI layer and manage state given by ViewModel.
- Listen to User specific event like cancellation or network error.
- Manages Android permissions, foreground service for long-running downloads, and lifecycle-aware cleanup of native resources.
- Android Studio Hedgehog or later
- Android NDK 27.2.12479018
- CMake 3.31.6
make sure to pull the llama.cpp submodule:
git submodule update --init --recursive
&&
sudo apt update && sudo apt install ninja-buildDon't forget to set the ANDROID_NDK environment variable to your NDK path
NDK_PATH=/home/dani/Android/Sdk/ndk/27.2.12479018
If you find following error about Vulkan SDK missing
cp vulkan.hpp \
"$ANDROID_NDK/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/include/vulkan/"
#OR
cd /tmp
wget https://github.com/KhronosGroup/Vulkan-Headers/archive/refs/tags/v1.3.275.tar.gz
tar xf v1.3.275.tar.gz
VULKAN_DIR="$NDK_PATH/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/include/vulkan"
# Copy everything from the Vulkan-Headers include/vulkan folder
cp -r Vulkan-Headers-1.3.275/include/vulkan/* "$VULKAN_DIR/"
Or if you find another error about SPIR-V header missing temporarily
cd /tmp
git clone --depth 1 https://github.com/KhronosGroup/SPIRV-Headers.git
NDK_PATH=/home/dani/Android/Sdk/ndk/27.2.12479018
cp -r SPIRV-Headers/include/spirv \
"$NDK_PATH/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/include/"// app/build.gradle
dependencies {
implementation(project(":memelm"))
implementation(project(":modelpull"))
implementation(project(":constant"))
}
// memelm/build.gradle
dependencies {
implementation(project(":constant"))
}
// modelpull/build.gradle
dependencies {
implementation(project(":constant"))
}
// constant/build.gradle
Distributed under the MIT License. See LICENSE for details.
llama.cpp is licensed under the MIT License. MiniCPM model weights are subject to their respective upstream license.
