Skip to main content

litert-runtime

Configures and implements LiteRT (.tflite) on-device inference on Android in Kotlin using the CompiledModel API on CPU and GPU. Use this skill for any Android LiteRT or .tflite task, including Gradle setup, image classification or vision/audio/embedding helpers, TensorBuffer reuse, Bitmap preprocessing, AndroidViewModel GPU-to-CPU fallback with warm-up on a single-thread dispatcher, migrating from legacy org.tensorflow.lite.Interpreter or GpuDelegate, and latency benchmarking. Don't use for running generative text LLMs with .litertlm files or for Python model conversion.

来源信息

仓库
android/skills
最近来源活动
2026年10月14日 11:22
检测到的 SKILL.md 语言
英语
星标
7,842
分支
520

安装方式

默认使用会先检查来源的 Prompt;你也可以切换为直接命令,或下载本地副本。

检查来源文件

决定是否安装前,请先阅读 SKILL.md,以及 SkillsMP 当前展示的配套文件。

文件资源管理器
3 个文件

正在显示 SKILL.md

SKILL.md
来源说明 · 只读预览
name
litert-runtime
description
Configures and implements LiteRT (.tflite) on-device inference on Android in Kotlin using the CompiledModel API on CPU and GPU. Use this skill for any Android LiteRT or .tflite task, including Gradle setup, image classification or vision/audio/embedding helpers, TensorBuffer reuse, Bitmap preprocessing, AndroidViewModel GPU-to-CPU fallback with warm-up on a single-thread dispatcher, migrating from legacy org.tensorflow.lite.Interpreter or GpuDelegate, and latency benchmarking. Don't use for running generative text LLMs with .litertlm files or for Python model conversion.
license
Complete terms in LICENSE.txt
metadata
{"author":"Google LLC","last-updated":"2026-10-13","keywords":["LiteRT","CompiledModel","tflite","Android app","GPU","TensorBuffer"]}
This skill provides step-by-step guidance for building an Android app that runs a `.tflite` model with the [LiteRT CompiledModel API](https://ai.google.dev/edge/litert/android) (`com.google.ai.edge.litert:litert` 2.x; [source repository](https://github.com/google-ai-edge/litert/blob/main/README.md)) on the CPU or the GPU. Don't use the deprecated `org.tensorflow.lite.Interpreter` API. For open text language models using `.litertlm` files, suggest the `litert-lm` skill. ## Prerequisites - A Kotlin Android project (`minSdk` must be `24` or higher, as declared by the LiteRT 2.3.0 AARs). - The dependencies in the app-level `build.gradle.kts` file from Google Maven: `implementation("com.google.ai.edge.litert:litert:2.3.0")` and `implementation("com.google.ai.edge.litert:litert-gpu:2.3.0")`. From 2.3.0 the GPU accelerator is the second artifact: with `litert` alone the app builds, and `CompiledModel.create` with `Accelerator.GPU` throws `LiteRtException` because the APK has no GPU accelerator library (Logcat logs `GPU accelerator could not be loaded and registered`). No manifest entry is needed: `litert-api`, which `litert` depends on, declares the OpenCL driver libraries (`uses-native-library`) in its own manifest. - A verified `.tflite` model and its input contract (input dimensions, mean, standard deviation, channel order, and tensor layout). For models with a verified recipe, inspect the [LiteRT model recipes](https://github.com/google-ai-edge/litert-samples/blob/main/models/README.md). ## Critical mandates - **Single-thread confinement** : One single-thread executor or confined coroutine dispatcher must own the `CompiledModel` instance. Creation, inference, and closing must execute on that same background thread, never on the main thread. - **Buffer lifecycle** : Allocate `TensorBuffer` instances once using `createInputBuffers` and `createOutputBuffers`, reuse them across inferences, and close all input and output buffers before closing the `CompiledModel`. - **Synchronous timing** : `model.run` enqueues work on the GPU and returns before GPU execution completes. Always measure `model.run` and output buffer readback (`outputs[0].readFloat`) together. - **Warm-up and exception safety** : Execute one warm-up inference inside the helper constructor's `init` block so first-run GPU shader compilation doesn't block the first user interaction, and call `close` in `catch` before rethrowing so neither `CompiledModel` nor `TensorBuffer` handles leak if buffer allocation or warm-up throws. ## Detailed steps ### Step 1: Configure Gradle and place the model file Search the project for `libs.versions.toml`, `gradle.properties`, `app/build.gradle.kts`, and any existing `org.tensorflow.lite` imports before editing build files. Configure `minSdk` and add the LiteRT runtime and GPU accelerator dependencies in `app/build.gradle.kts`: android { defaultConfig { minSdk = 24 } } dependencies { implementation("com.google.ai.edge.litert:litert:2.3.0") implementation("com.google.ai.edge.litert:litert-gpu:2.3.0") } Place bundled models at `app/src/main/assets/model.tflite` (AGP stores `.tflite` files uncompressed by default). Deliver a model that exceeds the base APK size limit through [Play for On-Device AI](references/android/google/play/on-device-ai.md) as an AI pack, or download it into `context.filesDir` (`bundletool --local-testing` installs AI packs locally without Google Play). Read an install-time AI pack through `AssetManager`. Load a fast-follow or on-demand AI pack or a downloaded file from its on-disk `filePath` using `CompiledModel.create(filePath, CompiledModel.Options(accelerator), environment)` in place of the asset call in Step 2. Obtain the pack's directory from the AI Delivery library (`com.google.android.play:ai-delivery`) using `AiPackManagerFactory.getInstance(context).getPackLocation(name)?.assetsPath()`, which returns `null` until the pack has finished downloading. With 2.3.0 nothing is added to `gradle.properties`: `litert` 2.3.0 declares the namespace `com.google.ai.edge.litert.impl` and its dependency `litert-api` 2.3.0 declares `com.google.ai.edge.litert`, so AGP 9.x builds the project. With 2.2.0 or 2.1.6 both libraries declare `com.google.ai.edge.litert`, and AGP 9.x stops at `processDebugMainManifest` (AGP 8.x reports it as a warning and builds); for those two versions add `android.uniquePackageNames=false` to `gradle.properties`, and remove it when moving to 2.3.0 because it also hides the same clash between any other two libraries. Excluding `litert-api` instead doesn't work: `CompiledModel` and `Accelerator` are in `litert-api`, and the build then fails at `compileDebugKotlin`. ### Step 2: Implement the inference helper class Isolate `CompiledModel` and its `TensorBuffer` instances in a dedicated `AutoCloseable` helper class free of Android UI types: import android.content.Context import com.google.ai.edge.litert.Accelerator import com.google.ai.edge.litert.CompiledModel import com.google.ai.edge.litert.Environment import com.google.ai.edge.litert.TensorBuffer const val INPUT_SIZE = 224 * 224 * 3 private val environment by lazy { Environment.create() } class Classifier(context: Context, accelerator: Accelerator) : AutoCloseable { private val model = CompiledModel.create( context.assets, "model.tflite", CompiledModel.Options(accelerator), environment ) private val inputs = mutableListOf<TensorBuffer>() private val outputs = mutableListOf<TensorBuffer>() init { try { inputs += model.createInputBuffers() outputs += model.createOutputBuffers() infer(FloatArray(INPUT_SIZE)) } catch (e: Exception) { close() throw e } } fun infer(input: FloatArray): FloatArray { inputs[0].writeFloat(input) model.run(inputs, outputs) return outputs[0].readFloat() } override fun close() { (inputs + outputs).forEach { it.close() } model.close() } } `Accelerator.CPU` requires nothing from the device. `Accelerator.GPU` compiles the graph for the GPU; when the GPU can't execute the model, the constructor throws `LiteRtException` (from `create` for an operator the GPU doesn't support, and on the Android emulator from `create` or from buffer allocation). A missing asset or a file that isn't a valid model also throws `LiteRtException` from `create`. The buffers are created once with the model, reused for every inference, and closed before the model. One `Environment` serves every model in the process and stays open, so LiteRT loads the GPU accelerator library once rather than on every `create` call. The constructor ends with one warm-up inference (the first GPU run includes shader compilation) and closes the model and its buffers if any step in `init` throws. `INPUT_SIZE` is the model's input element count: `writeFloat` throws on a longer array and writes a shorter one without an error, so a wrong size produces an incorrect result. For `int8` or `uint8` tensors, use `writeInt8` and `readInt8` on `TensorBuffer` instead of `writeFloat` and `readFloat`. ### Step 3: Wire the ViewModel and Compose screen Drive the helper from a single-thread executor inside an `AndroidViewModel`, fall back to `Accelerator.CPU` when GPU compilation fails, and expose immutable state to the Compose UI: import android.app.Application import androidx.lifecycle.AndroidViewModel import com.google.ai.edge.litert.Accelerator import com.google.ai.edge.litert.LiteRtException import java.util.concurrent.Executors import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.asCoroutineDispatcher import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch data class UiState( val ready: Boolean = false, val accelerator: Accelerator = Accelerator.CPU, val result: List<Float>? = null, val error: String? = null ) class MainViewModel(app: Application) : AndroidViewModel(app) { private val executor = Executors.newSingleThreadExecutor() private val scope = CoroutineScope( SupervisorJob() + executor.asCoroutineDispatcher() ) private var classifier: Classifier? = null private val _uiState = MutableStateFlow(UiState()) val uiState: StateFlow<UiState> = _uiState.asStateFlow() fun load(preferred: Accelerator = Accelerator.GPU) { scope.launch { if (classifier != null) return@launch val appContext = getApplication<Application>() val selected = try { classifier = Classifier(appContext, preferred) preferred } catch (e: LiteRtException) { if (preferred != Accelerator.CPU) { try { classifier = Classifier(appContext, Accelerator.CPU) Accelerator.CPU } catch (cpuError: LiteRtException) { _uiState.value = UiState(error = cpuError.message) return@launch } } else { _uiState.value = UiState(error = e.message) return@launch } } _uiState.value = UiState(ready = true, accelerator = selected) } } fun classify(input: FloatArray) { scope.launch { try { classifier?.infer(input)?.let { result -> _uiState.update { it.copy(result = result.toList(), error = null) } } } catch (e: LiteRtException) { _uiState.update { it.copy(error = e.message) } } } } override fun onCleared() { scope.launch { classifier?.close() }.invokeOnCompletion { scope.cancel() } executor.shutdown() } } One single-thread executor owns the model: creation, inference, and closing execute only on it, never on the main thread. The `ViewModel` keeps its own scope because `viewModelScope` is cancelled before `onCleared` runs, so a close launched there wouldn't run; `onCleared` cancels `scope` once `close` completes. `load` is a no-op when a model is already loaded across configuration changes; a failed `load` leaves `classifier` `null`. `classify` reports any `LiteRtException` (in package `com.google.ai.edge.litert`) in `error` and keeps the model. The following Compose screen takes the picture (`bitmap`) from the photo picker or CameraX and runs `preprocess` in the click handler (a few milliseconds for 224x224; move `preprocess` onto the executor for images exceeding 224x224). `viewModel` and `collectAsStateWithLifecycle` come from `androidx.lifecycle:lifecycle-viewmodel-compose` and `androidx.lifecycle:lifecycle-runtime-compose` (2.10.0): import android.graphics.Bitmap import androidx.compose.foundation.layout.Column import androidx.compose.material3.Button import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.viewmodel.compose.viewModel import com.google.ai.edge.litert.Accelerator @Composable fun MainScreen( bitmap: Bitmap, viewModel: MainViewModel = viewModel() ) { val state by viewModel.uiState.collectAsStateWithLifecycle() LaunchedEffect(Unit) { viewModel.load(Accelerator.GPU) } Column { state.error?.let { Text(it) } Button( onClick = { viewModel.classify(preprocess(bitmap)) }, enabled = state.ready ) { Text("Run (${state.accelerator})") } state.result?.let { scores -> Text("Top class: ${scores.indices.maxBy { i -> scores[i] }}") } } } ### Step 4: Preprocess inputs to match the model contract Convert the `Bitmap` into the model's input with `preprocess`. Match the model's exact input dimensions, normalization mean and standard deviation, channel order, and memory layout (this example uses NHWC, RGB, scaled to `-1.0..1.0`). Mismatched `mean` or `std` values produce corrupted predictions that look like a broken model: import android.graphics.Bitmap fun preprocess( bitmap: Bitmap, size: Int = 224, mean: Float = 127.5f, std: Float = 127.5f ): FloatArray { val scaled = checkNotNull( Bitmap.createScaledBitmap(bitmap, size, size, true) .copy(Bitmap.Config.ARGB_8888, false) ) val pixels = IntArray(size * size).also { scaled.getPixels(it, 0, size, 0, 0, size, size) } val out = FloatArray(size * size * 3) pixels.forEachIndexed { i, p -> out[i * 3] = ((p shr 16 and 0xFF) - mean) / std out[i * 3 + 1] = ((p shr 8 and 0xFF) - mean) / std out[i * 3 + 2] = ((p and 0xFF) - mean) / std } return out } The scaled bitmap is copied to `Bitmap.Config.ARGB_8888` because `ImageDecoder` returns `Bitmap.Config.HARDWARE` bitmaps by default on Android 9 and higher (including from a photo picker `Uri`), scaling preserves that config, and `getPixels` can't read `HARDWARE` bitmaps; the copy is of the 224x224 scaled bitmap rather than the full image. Scaling doesn't preserve the aspect ratio; crop first if the model requires center cropping. An audio or embedding model replaces `preprocess` with its own input encoding and keeps the inference class. ### Step 5: Verify output on a physical device Run the app on a physical device that runs Android from Android Studio. Compare the app's output with the model's reference output for one fixed input on `Accelerator.CPU` first and then on `Accelerator.GPU`; when verifying model outputs on a physical device, you must follow [device verification](references/verify.md). ## Migrating from legacy TFLite Interpreter When upgrading an existing project from `org.tensorflow.lite.Interpreter`, replace legacy constructs with their `CompiledModel` equivalents: - **Gradle dependencies** : Remove all `org.tensorflow:tensorflow-lite*` artifacts (`org.tensorflow:tensorflow-lite`, `org.tensorflow:tensorflow-lite-gpu`, `org.tensorflow:tensorflow-lite-support`, and `org.tensorflow:tensorflow-lite-metadata`). Replace them with `com.google.ai.edge.litert:litert:2.3.0` and `com.google.ai.edge.litert:litert-gpu:2.3.0`. If you still need Support or Metadata utilities (such as `ImageProcessor`, `TensorImage`, or `MetadataExtractor`), use `com.google.ai.edge.litert:litert-support:2.3.0` (or `litert-support-api:2.3.0` alongside `litert:2.3.0`) and `com.google.ai.edge.litert:litert-metadata:2.3.0`, and rename package imports from `org.tensorflow.lite.support.*` to `com.google.ai.edge.litert.support.*` per the [LiteRT Support migration guide](https://github.com/google-ai-edge/LiteRT/blob/main/litert/support_lib/MIGRATION_GUIDE.md). - **Model loading** : Replace `FileUtil.loadMappedFile` and `Interpreter` with `CompiledModel.create(context.assets, name, options, environment)`. - **GPU delegation** : Replace `GpuDelegate` on `Interpreter.Options` with `CompiledModel.Options(Accelerator.GPU)` and the `com.google.ai.edge.litert:litert-gpu:2.3.0` dependency. - **Execution** : Replace `interpreter.run` on `ByteBuffer` inputs with `inputs[0].writeFloat(input)`, `model.run(inputs, outputs)`, and `outputs[0].readFloat`. - **Label loading** : Replace `FileUtil.loadLabels` with `bufferedReader` and `readLines` on `context.assets.open(name)` (or `com.google.ai.edge.litert.support.common.FileUtil.loadLabels` if using `litert-support:2.3.0`). ## Antipatterns - **Never** create, run, or close `CompiledModel` on the Android main thread. - **Never** allocate new `TensorBuffer` instances on every inference call; failing to close per-frame `TensorBuffer` objects leaks C++ heap memory. - **Never** benchmark `model.run` without including output buffer readback (`outputs[0].readFloat`). - **Never** mix `org.tensorflow:tensorflow-lite*` artifacts (such as `org.tensorflow:tensorflow-lite-support`) with `com.google.ai.edge.litert:*` artifacts; both ship the `org.tensorflow.lite.*` classes and cause duplicate class build failures.
在 GitHub 查看
这个 SKILL.md 很大,SkillsMP 这里只预览前一段内容。 在 GitHub 查看