Kotlin or Java for a New Android App
A practical decision framework for choosing the language of a new Android project — based on team, codebase and lifetime of the app.
The honest answer is that both work. But "both work" is not a decision, so here is how we think about it when a client asks.
Kotlin is the default for new work
Since Google announced Kotlin-first development, the platform's direction has been clear: new APIs, samples and tooling target Kotlin. Coroutines make asynchronous work — network calls, database access — dramatically simpler than the callback and thread patterns Java requires.
suspend fun loadPatient(id: String): Patient = withContext(Dispatchers.IO) {
api.getPatient(id)
}
Null safety alone removes an entire category of crashes. In Java, every reference is a potential NullPointerException; in Kotlin, the type system tells you which values can be null and forces you to handle them.
When Java still makes sense
- An existing large Java codebase. A rewrite for the sake of language preference is expensive and risky. Kotlin interoperates with Java, so new modules can be Kotlin while legacy code stays Java.
- A team that only knows Java. Tooling is not the bottleneck; people are. A team writing idiomatic Kotlin beats a team forcing Kotlin.
- Very long-lived apps with old toolchains. Some enterprise environments lag on Gradle and library versions.
What we actually recommend
For a new Android application in 2026, start with Kotlin, use Jetpack libraries, and keep the architecture boring: a single-activity app, ViewModel for state, Room for local storage, Retrofit or Ktor for the API, and coroutines for concurrency.
class PatientViewModel(private val repo: PatientRepository) : ViewModel() {
private val _state = MutableStateFlow(PatientUiState())
val state: StateFlow<PatientUiState> = _state.asStateFlow()
fun load(id: String) = viewModelScope.launch {
_state.update { it.copy(loading = true) }
runCatching { repo.getPatient(id) }
.onSuccess { patient -> _state.update { it.copy(loading = false, patient = patient) } }
.onFailure { error -> _state.update { it.copy(loading = false, error = error.message) } }
}
}
That structure survives staff changes, survives Android version changes, and is straightforward to test.
The part that matters more than the language
Neither Kotlin nor Java will save a badly designed app. Decide the offline strategy, the sync model and the API contract before you write the first screen — those decisions are far more expensive to change later than the language you typed them in.
If you are building a pharmacy counter app that must keep working when the network drops, that design problem is the real project. The language is just how you express the answer.