All topics

QUESTIONS AND ANSWERS

Android

Why is Kotlin central to native Android development?

Android supports Kotlin, Java, and C++, but Kotlin has first-class Android Studio support and is the recommended language for modern Android development; Jetpack Compose is built on Kotlin. Its type system expresses nullability and helps catch some errors before runtime, though external data still needs validation. See Kotlin and Android.

What components make up an Android app?

The system recognizes four main component types: an Activity for UI interaction, a Service for work without visible UI, a BroadcastReceiver for responding to broadcasts, and a ContentProvider for sharing data through a controlled interface. Declare them in AndroidManifest.xml where appropriate; each has its own lifecycle, and none should be treated as durable data storage. Many modern apps use a single Activity as a container for Compose screens, though this is a pattern rather than a requirement. See Android app fundamentals and the architecture guide.

What is the difference between an explicit and an implicit Intent?

An explicit Intent names its recipient component; an implicit Intent describes an action and data so Android can find an app that can handle it, such as opening a link or sharing content. Use explicit Intents for sensitive internal services, validate received data, and carefully configure which components are exported. See Intents and intent filters and component security.

What does the Activity lifecycle mean?

The system invokes callbacks such as onCreate, onStart, onResume, onPause, onStop, and onDestroy as an Activity is created, becomes visible, or loses focus. Use each transition to initialize or pause resources that depend on it, but do not assume onDestroy will always run before the process ends. The activity lifecycle guide explains the transitions and their implications.

How do you preserve state through rotation and process death?

Rotation normally recreates the Activity; process death can remove everything held only in memory and does not guarantee a final cleanup callback. Keep screen state in a ViewModel to survive configuration changes, save small restorable state with SavedStateHandle or the saved-state mechanism, and persist user data in the data layer. See saving UI state.

Which layers does modern Android architecture recommend?

As a foundation, separate UI and data: the UI renders state and sends actions; the data layer contains business rules and exposes data through repositories. A domain layer is optional. Use unidirectional data flow and immutable state models so the origin of changes is clear and testable. See the architecture guide and its recommendations.

What is a Repository responsible for?

A Repository mediates between the UI and sources such as a network, local database, or preferences; it exposes domain data, centralizes changes, and resolves caching or synchronization. ViewModels and composables should not query each source directly, because the repository makes implementations replaceable and logic testable without coupling it to the platform. See the official data layer guide.

When is a domain layer with use cases worthwhile?

Add it when a complex rule or logic reused by multiple ViewModels benefits from a unit with a clear responsibility. It is not required in every app: a class for every simple operation adds coupling and maintenance without value. Android's guidance defines the domain layer as optional.

How does Jetpack Compose represent the UI?

Compose uses @Composable functions to describe the UI from current state. When an observable value read by a composition changes, affected parts can recompose; composables should therefore be declarative and should not mutate state as a side effect of composition. See Compose UI architecture.

What are state hoisting, remember, and rememberSaveable?

State hoisting means moving state to the common ancestor that needs to read and change it, leaving a composable to receive state and emit events; this makes it easier to reuse and test. remember retains an object while that call remains in the composition, while rememberSaveable can restore supported values after Activity recreation. Neither replaces persistent storage for business data; see state in Compose.

What does a ViewModel provide, and what should it not contain?

A ViewModel is a screen-level state holder or business-logic holder that exposes the state the UI needs and retains it through configuration changes. It does not, by itself, survive process death and should not hold references to an Activity, View, or screen-bound Context; use SavedStateHandle for small restorable values and repositories for persistent data. See ViewModel and saved state.

What does suspend do, and how do you keep the main thread responsive?

suspend lets a coroutine pause and resume without blocking a thread while it waits for a suspending operation; by itself, it does not mean the work runs in the background. Data functions should be main-safe: move I/O to Dispatchers.IO and CPU-intensive work to Dispatchers.Default, returning to the appropriate context to update the UI. See coroutines on Android.

How does structured concurrency help manage requests?

Tie each coroutine to a scope with a clear owner and lifetime, such as viewModelScope for screen work. When that scope ends, child coroutines are cancelled; propagate cancellation and errors, avoid GlobalScope for ordinary work, and use an explicit external scope only if an operation must outlive the screen. See coroutines best practices.

When should you use Flow or StateFlow, and how should the UI observe them?

A suspend function usually represents a one-shot call; Flow represents values over time, and StateFlow holds a current observable value that is useful for screen state. In Compose, collect with collectAsStateWithLifecycle so observation stops and resumes with the visible lifecycle; in Views, use lifecycle-aware collection. See unidirectional flow in Compose and UI recommendations.

How do you avoid unnecessary work during recomposition?

Measure first; split UI by responsibility, keep list-item identity stable with keys, and avoid expensive calculations in composables that may run again. Compose can skip recomposition when it knows values are stable and relevant parameters have not changed, but blind optimization or adding remember everywhere can reduce clarity without fixing the bottleneck. See Compose performance.

How do you design for different window sizes and devices?

Adapt the UI to the available window space, which can change because of rotation, multi-window mode, folding, or resizing, rather than relying only on the device model. Use window size classes to choose between one- or multi-pane layouts and navigation patterns; test compact, medium, and expanded sizes. APIs and breakpoints can vary by version; see adaptive apps.

How do you make a Compose screen accessible?

Use standard components where possible because they already expose semantics; for custom UI, define role, label, state, and actions with semantics. Test that screen readers can describe controls, that navigation order is logical, and that enlarged text and touch targets remain usable. See Compose accessibility and semantics.

When should you use Room, and how should its schema evolve?

Room adds a typed layer over SQLite with entities, DAOs, and queries checked at compile time; it is useful for structured local data, caching, and offline use. Every schema change in a published app needs a migration that preserves user data, and queries must not block the main thread. See the current Room guide.

How do you design an offline-first feature?

Keep a local source canonical for UI reads; the Repository synchronizes changes with the network when appropriate and exposes freshness or error state. Define what happens to writes made offline and how conflicts are resolved, because optimistic synchronization does not mean the server will always accept a change. See the official guide to offline-first apps.

How do you make network requests without blocking the UI or leaking information?

Perform network operations off the main thread, handle timeouts, HTTP errors, cancellation, and loading states, and use HTTPS with normal certificate validation. Model responses and errors in the data layer; network availability does not guarantee that a particular endpoint is reachable. See coroutines for I/O and network security.

When should you use WorkManager instead of a screen coroutine?

Use a coroutine tied to the ViewModel for work that matters only while the screen is active. For deferrable work that must persist if the app closes or the device restarts—for example, syncing an upload when a network becomes available—consider WorkManager and declare its constraints; the system schedules it when those conditions permit. See persistent work scheduling.

How should you handle permissions and minimize access to user data?

Request a permission in context, when the user starts a feature that needs it, explain why, and offer an alternative if they deny it. Ask for only the minimum access; for selecting photos or videos, the Photo Picker can avoid requesting broad library access. Exact permissions and behavior depend on Android version; see the permissions guide and Photo Picker.

Which practices protect Android data and components?

Store private data in internal storage unless it must be shared; use HTTPS, minimize permissions, and avoid exporting components unnecessarily. Intents that start internal services should be explicit, and exposed surfaces should validate input and enforce appropriate permissions. Review the official security checklist and security best practices.

What is the difference between local and instrumented tests?

Local tests run on the JVM and are fast for pure logic or classes with substituted dependencies; instrumented tests run on a device or emulator and validate framework APIs or real integration. Keep most logic outside Activities and Android components so it can be tested without starting a device. See what to test on Android and instrumented tests.

How do you test a Compose screen and its accessible states?

Compose tests find nodes by text, labels, or semantics properties, perform actions, and assert the displayed state. Test loading, empty, success, and error states as well as critical interactions; the same semantics that make the UI testable also help accessibility tools. See Compose testing and semantics for tests.

How do you investigate an Android performance problem?

Choose a metric and reproducible journey, measure startup, scrolling, and memory in a build close to production, and use traces to find work that exceeds the frame budget. Macrobenchmark measures larger journeys; Baseline Profiles can speed critical paths from first use, and R8 optimizes release builds. Compare before and after on representative devices; tool requirements depend on AGP and Jetpack versions. See performance measurement, Macrobenchmark, and Baseline Profiles.

Which tools help debug a failure?

Logcat shows app and system messages, exceptions, and stack traces; the Android Studio Debugger can pause at breakpoints and inspect variables. For Compose UI, inspect the composition and semantics trees; log enough context to investigate failures without printing tokens or sensitive data. See Logcat and debugging in Android Studio.

What do build type, product flavor, and build variant mean?

A build type defines settings such as debug or release; a product flavor defines a product or environment variant; Gradle combines them into a build variant with its own code, resources, and configuration. Use them to separate staging and production identifiers and endpoints without manually editing an artifact after compilation. See build variants; syntax can vary across Android Gradle Plugin versions.

What should you check before distributing a release?

Test the optimized artifact on supported devices and API levels, configure signing without putting private keys in the repository, and review permissions, manifest, size, and state restoration. Google Play releases commonly use an Android App Bundle, and Play App Signing separates the upload key from the key that signs APKs for users; verify the current process for your distribution channel. See app signing and uploading bundles.

In an interview, how would you choose Compose or Views for an existing app?

Consider team experience, stability of existing code, library integration, and migration cost; Compose is the recommended modern UI toolkit, but migration can be gradual and interoperate with Views. Decide by screen or module using measurable criteria, test coverage, and accessibility rather than rewriting the whole app solely to adopt a technology. See Android UI architecture and Compose interoperability.