Skip to main content

Android Development

This guide covers Android-specific concepts that appear in mobile engineering interviews.

Android Components

Android applications are built from four fundamental component types: Activities, Services, Broadcast Receivers, and Content Providers.

Activities

An Activity represents a single screen with a user interface. The Android system manages Activity instances through a defined lifecycle, creating and destroying them based on user navigation, configuration changes, and system memory requirements.

When a device rotates, the system destroys the current Activity instance and creates a new one. When system memory runs low, the system may destroy background Activities.

onCreate handling: The Activity's onCreate method receives a savedInstanceState Bundle that contains data saved in onSaveInstanceState. This Bundle is null on first creation and contains saved state when the Activity is recreated after destruction.

Activity Lifecycle Callbacks:

CallbackWhen Called
onCreate()Activity is first created
onStart()Activity becomes visible
onResume()Activity gains focus and becomes interactive
onPause()Another activity is taking focus
onStop()Activity is no longer visible
onDestroy()Activity is being destroyed

Configuration Changes:

  • Screen rotation triggers Activity destruction and recreation
  • onSaveInstanceState() is called before onStop()
  • Bundle size is limited to approximately 500KB-1MB
  • Use ViewModel for UI-related data that should survive configuration changes

Fragments

A Fragment represents a reusable portion of UI within an Activity. Fragments have their own lifecycle, which is coordinated with their host Activity's lifecycle.

Fragments can be detached from their Activity and reattached without being destroyed. The Fragment instance can outlive its view hierarchy.

Key lifecycle methods:

  • onCreateView: Inflate and return the Fragment's view hierarchy
  • onViewCreated: Called after onCreateView; view references are safe to use here
  • onDestroyView: Clear any view references stored as class properties to prevent memory leaks

Fragment Lifecycle:

  • onAttach()onCreate()onCreateView()onViewCreated()onStart()onResume()
  • View exists between onCreateView() and onDestroyView()
  • Fragment can be detached (onDetach()) without being destroyed

Considerations:

  • Do not store view references as class properties unless cleared in onDestroyView()
  • getActivity() can return null when Fragment is detached
  • Use viewLifecycleOwner for LiveData observations to prevent leaks

Services

A Service is a component for performing long-running operations without a user interface. Services run on the main thread by default.

Service TypeBehaviorUse Case
Started ServiceRuns until explicitly stoppedOne-time operations
Bound ServiceRuns while clients are boundClient-server communication
Foreground ServiceRequires visible notificationOngoing tasks like music playback

Key methods:

  • onStartCommand: Called when the service is started. Returns a flag indicating how the system should handle service restart (START_STICKY, START_NOT_STICKY, or START_REDELIVER_INTENT).
  • onBind: Returns an IBinder for bound service communication, or null for a started-only service.

Service Lifecycle:

  • START_STICKY: System recreates service after being killed, with null intent
  • START_NOT_STICKY: System does not recreate service
  • START_REDELIVER_INTENT: System recreates service with last intent
  • Android 8.0+ restricts background service execution; use WorkManager for deferred work

Broadcast Receivers

A BroadcastReceiver responds to system-wide broadcast announcements. Applications can register receivers for specific broadcasts either in the manifest or programmatically.

Dynamic registration: Create an anonymous BroadcastReceiver class that overrides onReceive. Register it with registerReceiver, specifying an IntentFilter for the desired actions. Unregister the receiver when it is no longer needed to prevent memory leaks.

Broadcast Types:

  • Explicit broadcasts: Target a specific component
  • Implicit broadcasts: Announce to any registered receiver
  • Android 8.0+ restricts implicit broadcasts; most require runtime registration

Intent System

Intents are messaging objects used to request actions from other app components.

Explicit Intent: Specifies the exact target component by class. Create an Intent with the context and target class, optionally add extras with putExtra, then call startActivity.

Implicit Intent: Specifies an action (such as ACTION_VIEW) for the system to resolve. Create an Intent with the action and data URI. The system finds components that can handle the intent.

Intent Filters:

Intent filters declare which implicit intents a component can handle. They are defined in the AndroidManifest.xml. An intent filter specifies: action (what the component can do), category (additional context), and data (MIME type or URI scheme).

PendingIntent:

A PendingIntent is a token that grants another application permission to execute a predefined Intent with your application's permissions. Create using factory methods like getBroadcast or getActivity. Android 12+ requires specifying FLAG_IMMUTABLE or FLAG_MUTABLE.

Use cases: Notifications, AlarmManager, AppWidgets.

Process and Memory Management

Android assigns a priority to each running process. When memory is constrained, the system terminates lower-priority processes.

Process Priority (highest to lowest):

PriorityDescription
ForegroundHosting a foreground Activity or Service
VisibleHosting a visible but not foreground Activity
ServiceRunning a started Service
CachedBackground process retained for fast restart
EmptyNo active components

Considerations:

  • The system can terminate processes without calling onDestroy()
  • Design applications to handle process death and state restoration
  • Use isFinishing() in onPause() to determine if Activity is finishing

ViewModel

ViewModel stores and manages UI-related data across configuration changes. It does not survive process death.

State management: Use StateFlow or LiveData to expose UI state. Private mutable state (MutableStateFlow) is updated internally; public read-only state (StateFlow) is observed by the UI.

Surviving process death: Use SavedStateHandle to persist data across process death. SavedStateHandle provides a key-value store that is automatically saved and restored.

ViewModel Lifecycle:

  • Created when the associated Activity or Fragment is first created
  • Retained across configuration changes
  • onCleared() is called when the owner Activity finishes (not during configuration changes)
  • Do not store Activity, Fragment, or View references in ViewModel

Kotlin Coroutines

Coroutines provide a framework for asynchronous programming on Android.

Dispatchers

DispatcherThread PoolUse Case
Dispatchers.MainMain/UI threadUI updates
Dispatchers.IOShared poolNetwork and disk I/O
Dispatchers.DefaultShared poolCPU-intensive computation

Structured Concurrency

Launch coroutines within a scope tied to a lifecycle to ensure proper cancellation.

viewModelScope: Use the ViewModel's built-in scope for coroutines. Launch operations on the main thread, then switch to IO dispatcher for data fetching using withContext. Update state on the main thread after data is loaded.

Coroutine Builders:

  • launch: Starts a coroutine that does not return a result
  • async: Starts a coroutine that returns a Deferred result

Exception Handling:

  • try-catch: Wrap risky operations in try-catch within the coroutine
  • CoroutineExceptionHandler: Create a handler that logs or processes exceptions, and pass it to the launch builder

Cancellation:

  • Child coroutine failure cancels parent and siblings by default
  • Use supervisorScope to prevent sibling cancellation

Jetpack Libraries

Room Database

Room provides an abstraction layer over SQLite.

Entity: Annotate a data class with @Entity. Use @PrimaryKey on the unique identifier field.

DAO (Data Access Object): Define an interface with @Dao annotation. Use @Query for custom queries (returns Flow for reactive updates), @Insert, @Update, and @Delete for standard operations. Mark functions as suspend for coroutine support.

Database: Create an abstract class extending RoomDatabase with @Database annotation. Specify entities and version number. Declare abstract methods returning DAO instances.

Features:

  • Compile-time SQL verification
  • @Transaction annotation for atomic operations
  • Migrations required for schema changes

WorkManager

WorkManager handles deferrable, guaranteed background work.

Worker implementation: Extend CoroutineWorker and override doWork. Perform the background operation and return Result.success(), Result.failure(), or Result.retry() based on the outcome.

Scheduling work: Create a work request using OneTimeWorkRequestBuilder or PeriodicWorkRequestBuilder. Set constraints (such as requiring network connectivity) using the Constraints.Builder. Enqueue the request using WorkManager.getInstance().enqueue().

Work Types:

  • OneTimeWorkRequest: Executes once
  • PeriodicWorkRequest: Repeats at intervals (minimum 15 minutes)

Navigation component manages navigation between destinations.

Navigation with Safe Args: Use the generated Directions class to create type-safe navigation actions. Call findNavController().navigate() with the action and arguments.

Features:

  • Safe Args plugin generates type-safe argument classes
  • Automatic back stack management
  • Deep link support

Build System

Build Variants

Configuration: In the app's build.gradle, define buildTypes (debug, release) and productFlavors (such as free, paid). Build types specify release configurations like minification. Product flavors specify different versions of the app. The combination generates all variant combinations (for example: freeDebug, freeRelease, paidDebug, paidRelease).

Code Shrinking (R8)

R8 performs code shrinking, optimization, and obfuscation for release builds.

Configuration:

  • minifyEnabled true enables shrinking
  • Classes accessed via reflection require keep rules
  • ProGuard rules file specifies exceptions

Common Interview Topics

DEX Method Limit: DEX files have a limit of approximately 65,536 methods. Applications exceeding this limit require multidex, which is enabled by default when using AndroidX.

Kotlin Scope Functions:

FunctionContext ObjectReturn Value
letitLambda result
runthisLambda result
withthisLambda result
applythisContext object
alsoitContext object

Sealed Classes: Sealed classes restrict inheritance to the same file, enabling exhaustive when expressions. Define a sealed class with nested subclasses (object for singleton states, data class for states with data). When using a when expression on a sealed class, the compiler ensures all cases are handled, making the else branch unnecessary.

Property Initialization:

  • by lazy: Initialized on first access, thread-safe, val only
  • lateinit: Must be initialized before use, var only, no primitive types

Parcelable vs Serializable:

  • Parcelable: Android-specific, faster, requires implementation. Use the @Parcelize annotation on a data class implementing Parcelable to auto-generate implementation.
  • Serializable: Java standard, slower, no implementation required

SharedPreferences:

  • commit(): Synchronous write, returns boolean
  • apply(): Asynchronous write, no return value