Tensorflow
Android
Machine Learning
Mobile AI
Model Deployment

Running a Tensorflow model on Android

ML System Design practice on Codemia

Design recommenders, ranking systems and training pipelines the way ML interviews actually ask for them, with worked solutions.

Practice ML system design

Introduction

Running a TensorFlow model on Android usually means deploying a TensorFlow Lite model, not shipping a full training artifact into the app. TensorFlow Lite is the mobile-oriented runtime designed for smaller binaries, faster startup, and device-friendly inference.

The deployment flow is simple in principle: convert the trained model to .tflite, package it in the app, load it with the Lite interpreter, and feed input tensors that match the training-time shape and preprocessing.

Convert the Model Before It Reaches Android

Model conversion happens outside the Android project, typically in Python after training:

python
1import tensorflow as tf
2
3model = tf.keras.Sequential([
4    tf.keras.layers.Input(shape=(4,)),
5    tf.keras.layers.Dense(8, activation="relu"),
6    tf.keras.layers.Dense(3, activation="softmax"),
7])
8
9converter = tf.lite.TFLiteConverter.from_keras_model(model)
10tflite_model = converter.convert()
11
12with open("model.tflite", "wb") as f:
13    f.write(tflite_model)

That .tflite file is what the Android app should ship. For production builds, you may later add quantization, but first get the plain model working correctly.

Add the Lite Runtime to the Android App

Put the model file in app/src/main/assets/, then add the TensorFlow Lite dependency:

kotlin
dependencies {
    implementation("org.tensorflow:tensorflow-lite:<version>")
}

The exact version depends on the project, but the structure stays the same: dependency plus bundled model asset.

Load the Model from Assets

A common Android pattern is to memory-map the model file and create an Interpreter from that mapping:

kotlin
1import android.content.Context
2import org.tensorflow.lite.Interpreter
3import java.io.FileInputStream
4import java.nio.MappedByteBuffer
5import java.nio.channels.FileChannel
6
7fun loadModelFile(context: Context, fileName: String): MappedByteBuffer {
8    val fileDescriptor = context.assets.openFd(fileName)
9    FileInputStream(fileDescriptor.fileDescriptor).use { inputStream ->
10        val fileChannel = inputStream.channel
11        return fileChannel.map(
12            FileChannel.MapMode.READ_ONLY,
13            fileDescriptor.startOffset,
14            fileDescriptor.declaredLength
15        )
16    }
17}
18
19fun createInterpreter(context: Context): Interpreter {
20    val modelBuffer = loadModelFile(context, "model.tflite")
21    return Interpreter(modelBuffer)
22}

This keeps model loading efficient and avoids unnecessary file copying.

Run Inference with the Correct Shapes

Inference works only if the input and output containers match the model's expected tensor shapes and data types.

kotlin
1val interpreter = createInterpreter(context)
2
3val input = arrayOf(floatArrayOf(5.1f, 3.5f, 1.4f, 0.2f))
4val output = Array(1) { FloatArray(3) }
5
6interpreter.run(input, output)
7println(output[0].contentToString())
8
9interpreter.close()

If the model expects normalized input, the Android code must normalize values the same way training did. A correct model with incorrect preprocessing still produces bad predictions.

Keep Preprocessing Consistent

This is the most common mobile inference bug. If the training pipeline resized images, scaled pixels to 0.0 through 1.0, subtracted mean values, or changed channel order, Android must do the same.

For image input, the app often has to:

  • resize to the expected width and height
  • convert pixel data to the expected numeric type
  • normalize with the same formula used during training

If one of those steps differs, the problem is usually blamed on the model when the real bug is input preparation.

Reuse the Interpreter and Keep Work Off the Main Thread

Creating an interpreter is more expensive than running one inference on an already loaded interpreter. In a real app, it is better to create it once per feature or screen lifecycle and reuse it.

Also avoid running heavy inference on the main thread. Even a modest model can create visible jank if input preparation and inference happen directly in UI callbacks.

Common Pitfalls

  • Trying to use a training-time TensorFlow model directly instead of converting to TensorFlow Lite.
  • Loading the model correctly but feeding input arrays with the wrong shape or dtype.
  • Forgetting that Android preprocessing must exactly match training preprocessing.
  • Recreating the interpreter for every prediction instead of reusing it.
  • Optimizing with delegates or quantization before the plain baseline inference path is known to work.

Summary

  • On Android, deploy TensorFlow models as TensorFlow Lite .tflite files.
  • Add the Lite runtime dependency and bundle the model in app assets.
  • Load the model with an Interpreter, usually from a memory-mapped asset.
  • Match Android-side preprocessing and tensor shapes exactly to the training pipeline.
  • Reuse the interpreter and keep inference off the main thread when possible.

Related reading
Course
Beginner
27 lessons
10 hours
System Design Fundamentals

Build a strong foundation in designing scalable, reliable distributed systems.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

ML System Design practice on Codemia

Design recommenders, ranking systems and training pipelines the way ML interviews actually ask for them, with worked solutions.

Practice ML system design

All Rights Reserved.