On this page
In a machine-coding or take-home round, a working feature is the minimum. What earns a strong signal is clean structure, correct state handling, error and edge cases, and a few meaningful tests, all explained clearly.
What interviewers evaluate
Problem 1: Search with debounce and cancellation
Build a search ViewModel that waits 300 ms after typing stops, ignores blank queries, cancels outdated searches, and exposes loading, results and error states.
sealed interface SearchState {
data object Idle : SearchState
data object Loading : SearchState
data class Results(val items: List<String>) : SearchState
data class Error(val message: String) : SearchState
}
@OptIn(FlowPreview::class, ExperimentalCoroutinesApi::class)
class SearchViewModel(private val api: suspend (String) -> List<String>) : ViewModel() {
private val query = MutableStateFlow("")
val state: StateFlow<SearchState> = query
.map(String::trim)
.debounce(300)
.distinctUntilChanged()
.flatMapLatest { q ->
if (q.isEmpty()) flowOf(SearchState.Idle)
else flow<SearchState> {
emit(SearchState.Loading)
emit(SearchState.Results(api(q)))
}.catch { emit(SearchState.Error("Search failed")) }
}
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), SearchState.Idle)
fun onQueryChange(q: String) { query.value = q }
}Discuss: why flatMapLatest (cancels stale requests), why distinctUntilChanged, and how you'd test it with virtual time.
Problem 2: LRU cache
Implement a fixed-capacity least-recently-used cache with O(1) get and put.
class LruCache<K, V>(private val capacity: Int) {
init { require(capacity > 0) }
// accessOrder = true: iteration order is least โ most recently accessed
private val map = object : LinkedHashMap<K, V>(capacity, 0.75f, true) {
override fun removeEldestEntry(eldest: MutableMap.MutableEntry<K, V>?) = size > capacity
}
@Synchronized fun get(key: K): V? = map[key]
@Synchronized fun put(key: K, value: V) { map[key] = value }
@Synchronized fun size(): Int = map.size
}Discuss: thread safety, why LinkedHashMap with access order works, and Android's built-in LruCache with size-of for bitmaps.
Problem 3: Rate limiter (token bucket)
class TokenBucket(
private val capacity: Int,
private val refillPerSecond: Double,
private val now: () -> Long = System::nanoTime,
) {
private var tokens = capacity.toDouble()
private var last = now()
@Synchronized
fun tryAcquire(): Boolean {
val t = now()
tokens = minOf(capacity.toDouble(), tokens + (t - last) / 1e9 * refillPerSecond)
last = t
return if (tokens >= 1) { tokens -= 1; true } else false
}
}
// Use: limit AI or API calls per user action burst; inject `now` for deterministic tests.Problem 4: Paginated list with error and retry (feature task)
Show a list from a paged API, load more near the end, show a footer spinner or retry, and keep state across rotation. Approach: Paging 3 if allowed (explain the choice), otherwise a manual pager:
data class ListState(
val items: List<Item> = emptyList(),
val nextPage: Int? = 1,
val loadingMore: Boolean = false,
val error: String? = null,
)
fun loadMore() {
val s = _state.value
if (s.loadingMore || s.nextPage == null) return // guard duplicate and end-of-list loads
_state.update { it.copy(loadingMore = true, error = null) }
viewModelScope.launch {
_state.update { cur ->
try {
val page = api.page(s.nextPage)
cur.copy(items = cur.items + page.items, nextPage = page.next, loadingMore = false)
} catch (e: IOException) {
cur.copy(loadingMore = false, error = "Couldn't load more")
}
}
}
}Note: calling a suspend function inside update { } isn't allowed, because the lambda isn't suspending. In the interview, fetch first, then update. Spotting this kind of detail is exactly what interviewers watch for.
viewModelScope.launch {
val result = runCatching { api.page(s.nextPage) }
_state.update { cur ->
result.fold(
onSuccess = { page -> cur.copy(items = cur.items + page.items, nextPage = page.next, loadingMore = false) },
onFailure = { cur.copy(loadingMore = false, error = "Couldn't load more") },
)
}
}Problem 5: Group and summarise transactions
data class Txn(val category: String, val amountPaise: Long, val date: LocalDate)
/** Top N categories by spend in a month, with totals. */
fun topCategories(txns: List<Txn>, month: YearMonth, n: Int): List<Pair<String, Long>> =
txns.asSequence()
.filter { YearMonth.from(it.date) == month }
.groupingBy { it.category }
.fold(0L) { acc, t -> acc + t.amountPaise }
.entries
.sortedByDescending { it.value }
.take(n)
.map { it.key to it.value }Tips for the round
- Clarify requirements and constraints before coding.
- Start with the data model and state, then logic, then UI.
- Handle loading, empty and error states, and rotation.
- Add one or two focused tests if time allows.
- Summarise trade-offs and next steps at the end.
Frequently asked questions
Should I use libraries in a take-home?
Use standard ones (Retrofit, Room, Compose), unless told otherwise, and explain why. Avoid exotic dependencies.
DS&A or Android tasks?
Companies differ. Product companies often include both. Practise common algorithm patterns in Kotlin as well as feature tasks.
Conclusion
Android coding rounds reward correct, well-structured, state-aware code with edge cases, a few tests and clear communication. Practise realistic features (debounced search, pagination, caching) and classic Kotlin collection problems.
Next step: prepare for legacy-code questions with Java to Kotlin Migration Interview Questions.
Official references
๐ฑ Apps built by Yunok Tech
Real Android apps on Google Play, free to download.
Walzi4K wallpapers for lock and home screenGet it โธ
NotiVaultNotification history: recover dismissed notificationsGet it โธ
Ready to build real Android applications?
Interviews reward developers who can explain why, not just how. Build the Kotlin, Compose and architecture depth that lets you discuss real trade-offs with confidence.
Move beyond theory and strengthen your Android development skills with Yunoktech's Android course. Learn core concepts, practical implementation, modern Android development techniques, and the reasoning behind building maintainable applications.
Explore the Android course and take your next step as an Android developer.