For the complete documentation index, see llms.txt. This page is also available as Markdown.

Interview Prep 2

Dependency Injection & Build

Q121. How do you implement dependency injection in a large-scale app, and why prefer Hilt over manual DI or Koin? Hilt is the recommended DI framework for Android, generating components at compile time via annotation processing, ensuring type safety and performance. It integrates with Android lifecycle (Activity, Fragment, ViewModel) through predefined scopes and eliminates boilerplate. Manual DI becomes unmaintainable at scale due to growing factory classes and transitive dependency graphs. Koin uses service locator pattern with runtime resolution, risking crashes from missing bindings that Hilt catches at compile time. Hilt's generated code is optimized by the compiler and works seamlessly with Jetpack ViewModels via by viewModels(). For SDKs or Kotlin Multiplatform, Koin or manual DI may be preferable due to Hilt's Android-only nature. The trade-off is build time cost from kapt/KSP, but the safety and maintainability outweigh it for large teams.

@HiltAndroidApp
class MyApp : Application()

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
    private val vm: MyViewModel by viewModels()
}

Read more: https://developer.android.com/training/dependency-injection/hilt-android

Q122. How do you write custom Gradle convention plugins to standardize build configuration across modules? Create a standalone Gradle plugin project (Kotlin DSL) applying common Android plugin configurations, dependencies, and lint settings. Define a class implementing Plugin<<Project> that configures android block defaults (compileSdk, minSdk, testInstrumentationRunner) and adds standard dependencies (JUnit, Kotlin stdlib). Apply the plugin in feature modules via plugins { id("com.example.convention.android-library") }. Use extensions to expose customizable properties (e.g., enableCompose = true). This eliminates copy-paste build scripts and ensures consistency. Version the plugin independently and publish to a private Maven repository or include in buildSrc. For large projects, split into android-application, android-library, android-feature, and android-test convention plugins. Document plugin usage in the project wiki. Update all modules atomically when build standards change.

class AndroidLibraryConventionPlugin : Plugin<<Project> {
    override fun apply(target: Project) = with(target) {
        pluginManager.apply("com.android.library")
        pluginManager.apply("org.jetbrains.kotlin.android")
        extensions.configure<<LibraryExtension> {
            compileSdk = 34
            defaultConfig { minSdk = 24; testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" }
        }
        dependencies { add("implementation", libs.findLibrary("androidx.core.ktx").get()) }
    }
}

Read more: https://developer.android.com/build

Q123. How do you handle annotation processing migration from kapt to KSP in a large project? KSP (Kotlin Symbol Processing) is faster than kapt because it directly analyzes Kotlin AST without generating Java stubs, reducing build times by ~25%. Migrate incrementally: enable KSP for one module at a time (Room, Moshi, Hilt) while keeping kapt for others. Update Gradle plugins to KSP-compatible versions (com.google.devtools.ksp). Replace kapt with ksp in dependencies. Note that some libraries (Dagger/Hilt) require specific KSP versions; verify compatibility before migration. For libraries without KSP support, continue using kapt or replace them. Configure ksp { arg("room.schemaLocation", "$projectDir/schemas") } for Room schema export. Clean build after migration to clear stale generated code. Monitor build scan times to validate improvement. Document the migration in team wiki. KSP doesn't support Java-only processors; keep kapt for those if necessary.

Read more: https://developer.android.com/build/migrate-to-ksp


Data & Storage

Q124. How do you handle complex multi-table database queries in Room while maintaining reactive updates? Room's @Relation with @Embedded and @Transaction fetches related entities, but emits reactive updates only when the parent table changes. For true reactive multi-table joins, use @Query with explicit JOINs returning a flattened POJO or use database views (@DatabaseView) to precompute complex relationships. Add indices on foreign keys to avoid full table scans during joins. Use Flow return types for automatic emission on table changes, but note that Room invalidates queries when any referenced table changes, potentially causing excessive recompositions. For complex aggregations, use GROUP BY with @Query and map to domain models. Test query plans with EXPLAIN QUERY PLAN. Avoid @Relation for many-to-many without a join table entity. Use Transaction annotation to ensure consistency.

Read more: https://developer.android.com/training/data-storage/room

Q125. How do you migrate from SharedPreferences to DataStore, and what are the trade-offs? DataStore offers type safety (Preferences or Proto), coroutine-based async I/O, and transactional updates with strong consistency guarantees, unlike SharedPreferences' synchronous apply/commit and potential ANR on main thread. Migrate by reading all existing SharedPreferences keys in a one-shot coroutine, writing them to DataStore, then deleting the old file. Use androidx.datastore:datastore-preferences for key-value pairs or Proto DataStore for typed schemas. DataStore handles migration automatically via SharedPreferencesMigration helper. Trade-offs: DataStore is slower for very frequent writes due to coroutine overhead and file rewriting. SharedPreferences is still acceptable for simple flags with low write frequency. For multi-process access, neither is ideal—use SQLite or MMKV. DataStore lacks OnSharedPreferenceChangeListener; use Flow observation instead. Always perform writes off the main thread.

Read more: https://developer.android.com/topic/libraries/architecture/datastore

Q126. How do you implement a content provider for cross-app data sharing with fine-grained permissions? Extend ContentProvider and implement query, insert, update, delete, and getType with URI matching via UriMatcher. Define path-level permissions in the manifest using android:readPermission and android:writePermission with signature protection. Use path-permission elements for granular access control per URI pattern. Return ParcelFileDescriptor for file access rather than raw paths. Validate caller identity in each method using getCallingPackage() or Binder.getCallingUid(). Use SQLiteQueryBuilder for secure parameterized queries preventing injection. Expose only necessary columns; never return internal IDs or sensitive metadata. Implement ContentObserver notifications for data changes. Document the content URI contract and MIME types for consumers. Test with a separate consumer app to verify permission enforcement.

Read more: https://developer.android.com/guide/topics/providers/content-providers


Security & Privacy

Q127. How do you implement biometric authentication with cryptographic binding for high-security operations? Use BiometricPrompt with CryptoObject to bind biometric authentication to cryptographic operations, ensuring the key is only usable after successful biometric verification. Generate a key in Android Keystore with setUserAuthenticationRequired(true) and setInvalidatedByBiometricEnrollment(true). Wrap the Cipher, Signature, or Mac in BiometricPrompt.CryptoObject. On authentication success, use the unlocked crypto object to decrypt sensitive data or sign transactions. Handle all error codes (ERROR_LOCKOUT, ERROR_NO_BIOMETRICS) with user-friendly messaging. For fallback, allow device credentials (PIN/pattern) by setting setAllowedAuthenticators(BIOMETRIC_STRONG or DEVICE_CREDENTIAL). Never store the biometric data itself—only use Keystore-backed keys. Test on devices with and without biometric hardware. Ensure the prompt is cancellable and lifecycle-aware.

Read more: https://developer.android.com/training/sign-in/biometric-auth

Q128. How do you handle file sharing securely using FileProvider and content URIs? Define a FileProvider in the manifest with android:authorities and an XML paths configuration limiting exposed directories (e.g., <files-path name="shared" path="shared/" />). Never expose the entire internal storage. Generate content URIs via FileProvider.getUriForFile() and grant temporary permissions with Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION. For multiple files, use ClipData with Item containing the URI and grant permissions via Intent.setClipData(). Revoke permissions via Context.revokeUriPermission() when no longer needed. Validate incoming URIs in receiving apps using ContentResolver.query() to check file size and type before processing. Prevent path traversal by rejecting URIs with .. segments. Use StrictMode VmPolicy to detect unsafe file URI exposure. Test with adb shell am start sending file intents.

Read more: https://developer.android.com/training/secure-file-sharing

Q129. How do you architect user privacy controls for per-app language preferences and data deletion requests introduced in Android 13+? Implement AppLocalePicker using LocaleManager (API 33+) or AppCompatDelegate.setApplicationLocales() from AppCompat to allow users to select a language different from the system default. Store the selected locale in AppCompatDelegate which persists across sessions using androidx.core:core-1.9.0+. Provide an in-app language selector that updates the locale and recreates the activity. For older devices, use a custom ContextWrapper attaching a new Configuration with the selected locale to the base context. Ensure all string resources are properly externalized with translations. Test by changing the app locale and verifying layout direction (RTL) changes for Arabic/Hebrew. Document that LocaleManager requires app targeting API 33+. Handle locale changes in attachBaseContext() for Activity and Application context. Use autoStoreLocales = true in Application metadata to persist without manual storage.

Read more: https://developer.android.com/guide/topics/resources/app-languages


UI & Compose

Q130. How do you implement edge-to-edge UI while handling system bars and keyboard insets correctly? Use WindowCompat.setDecorFitsSystemWindows(window, false) to enable edge-to-edge display. Consume insets via ViewCompat.setOnApplyWindowInsetsListener or in Compose via WindowInsets and Modifier.padding with safeDrawing or safeContent. For Compose, use Scaffold with contentWindowInsets or manual PaddingValues derived from WindowInsets.statusBars and navigationBars. Handle IME (keyboard) animations using WindowInsets.ime with Modifier.imePadding() or AnimatedInsets on Android 11+. Respect display cutouts with WindowInsets.displayCutout. Ensure touchable elements don't overlap system gestures by adding Modifier.systemGesturesPadding(). Test on devices with notches, gesture navigation, and three-button navigation. Use InsetsController to control light/dark system bar icons. Material3 components like TopAppBar and BottomNavigationBar automatically handle insets when used within Scaffold.

Read more: https://developer.android.com/develop/ui/views/layout/edge-to-edge

Q131. How do you implement dynamic theming with Material3 and wallpaper-derived color schemes? Use dynamicColorScheme() from androidx.compose.material3:material3 on Android 12+ to generate color schemes from the user's wallpaper via android.graphics.color APIs. Fallback to predefined light/dark schemes on older devices. Use isSystemInDarkTheme() to respect system preference, overridden by in-app toggle stored in DataStore. Apply MaterialTheme with the dynamic scheme at the app root. Use tonal palettes (primaryContainer, onPrimaryContainer) for accessible contrast. For Views, use Theme.Material3.DynamicColors and apply DynamicColors.applyToActivitiesIfAvailable(). Test with different wallpapers and under dark mode. Ensure custom brand colors harmonize with dynamic palettes using ColorScheme.harmonized(). Document that dynamic theming requires targetSdk 31+ and device support. Provide an opt-out in settings for users preferring consistent app branding.

Read more: https://developer.android.com/develop/ui/views/theming/dynamic-colors

Q132. How do you implement predictive back gesture support in an app targeting Android 15+? Enable predictive back by setting android:enableOnBackInvokedCallback="true" in the manifest and using OnBackInvokedDispatcher instead of overriding onBackPressed(). Register callbacks with priority levels to handle hierarchical back navigation (e.g., close drawer before exiting app). In Compose, use PredictiveBackHandler from androidx.activity:activity-compose to receive back progress events and animate UI accordingly (e.g., scaling down the screen or revealing the home screen behind). Provide visual continuity by interpolating from Float progress values (0.0 to 1.0) during the gesture. Test with gesture navigation enabled on Android 14+ devices and emulators. For custom transitions, use androidx.transition with predictive back support. Ensure that disabled users can still navigate via accessibility services when animations are reduced. Document the back stack behavior for each screen in navigation graphs.

Read more: https://developer.android.com/guide/navigation/custom-back/predictive-back-gesture

Q133. How do you implement and test custom Views with complex drawing and touch handling? Extend View and override onMeasure to define exact dimensions based on MeasureSpec constraints. Override onDraw using Canvas and Paint for custom rendering; cache Path and Bitmap objects in onSizeChanged to avoid allocation during draw calls. Handle touch events in onTouchEvent using MotionEvent coordinates, supporting both single and multi-touch via getPointerId. Use VelocityTracker for gesture speed calculations. Enable hardware acceleration hints with setLayerType(View.LAYER_TYPE_HARDWARE, null) for complex animations. Test custom views using androidx.test with PixelCopy for screenshot verification and manual touch injection via MotionEvent.obtain(). Use View.isInEditMode to provide preview data in Android Studio layout editor. Profile custom drawing with GPU rendering profile bars.

Read more: https://developer.android.com/training/custom-views


Media & Background

Q134. How do you architect media playback for a podcast app supporting background playback, downloads, and casting? Use Jetpack Media3 with ExoPlayer as the player engine, wrapped in a MediaSessionService for background playback and platform integration. Implement MediaController in the UI for transport controls and metadata display. Use DownloadManager for offline episodes with a DownloadService foreground notification. Support Google Cast via CastPlayer from Media3 extensions. Structure the app with a single MediaBrowserService that exposes the content hierarchy to Android Auto, Wear, and Assistant. Use PlayerNotificationManager for persistent media notifications with playback controls. Handle audio focus via AudioFocusRequest and noisy intent for headphone disconnects. Store playback progress in DataStore and resume seamlessly. Use WorkManager for periodic library sync and download cleanup.

Read more: https://developer.android.com/media

Q135. How do you handle audio ducking and focus management when multiple apps request audio playback? Request audio focus with AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK) when your app plays short audio (notifications, navigation instructions) that should duck others rather than pause them. For long-form media, use AUDIOFOCUS_GAIN and pause on focus loss. Implement OnAudioFocusChangeListener to handle AUDIOFOCUS_LOSS_TRANSIENT (pause temporarily) and AUDIOFOCUS_LOSS (pause indefinitely). Use AudioAttributes with CONTENT_TYPE_SPEECH or CONTENT_TYPE_MUSIC to help the system prioritize. In Media3, ExoPlayer handles audio focus automatically when configured with AudioAttributes. Test ducking behavior by playing music in Spotify and launching your app. Ensure volume changes are smooth using VolumeShaper. Respect the NotificationManager.INTERRUPTION_FILTER_PRIORITY Do Not Disturb state.

Read more: https://developer.android.com/guide/topics/media

Q136. How do you implement NFC Host Card Emulation (HCE) for contactless payments or access control? Declare an HceService extending HostApduService in the manifest with android:exported="true" and an intent filter for android.nfc.cardemulation.action.HOST_APDU_SERVICE. Provide an XML apduservice configuration defining AID (Application ID) filters. Override processCommandApdu() to handle SELECT and other APDU commands, returning response APDUs as ByteArray. Maintain a secure element or Keystore-backed keys for cryptographic operations within the service. Use sendResponseApdu() for synchronous responses. Handle onDeactivated() to clean up sessions. Ensure the service is bound to a foreground activity or notification for security. Test with real NFC readers and the NFC Forum test suite. Comply with EMVCo specifications for payment apps. Use TapAndPay APIs if integrating with Google Wallet rather than building custom HCE.

Read more: https://developer.android.com/guide/topics/connectivity/nfc/hce


Performance & Profiling

Q137. How do you use Macrobenchmark and JankStats to detect and fix UI jank in production? Macrobenchmark measures cold startup, warm startup, scrolling, and animation frame times using FrameTimingMetric and StartupTimingMetric on real devices without debug overhead. Write benchmark classes extending MacrobenchmarkRule to repeatedly execute user journeys and report frame durations. JankStats (androidx.metrics) is a production library that reports frame metrics from real user sessions via JankStats.createAndTrack(window, executor) { frameData -> analytics.log(frameData) }. It detects frames exceeding the target refresh rate deadline and reports stack traces when available. Combine both: use Macrobenchmark in CI for regression detection and JankStats in production for real-world anomaly detection. Fix jank by identifying slow layout passes via Systrace, reducing overdraw, and moving heavy work off the main thread. Target 90th percentile frame times rather than averages.

Read more: https://developer.android.com/topic/performance/benchmarking

Q138. How do you use the ProfileInstaller library to ship Baseline Profiles without waiting for Play Store cloud profiling? ProfileInstaller (androidx.profileinstaller) embeds a Baseline Profile in assets/dexopt/baseline.prof and installs it on first launch or app update, bypassing the delay for Play Store cloud profiles. It uses ProfileInstallerInitializer via App Startup library to trigger installation. Ensure your AAB includes the profile asset generated by the Baseline Profile Gradle plugin. ProfileInstaller works on API 24+ but is most effective on API 31+ with ART updates. Verify installation via logcat filtering for ProfileInstaller. Combine with Macrobenchmark to measure startup improvement immediately after install rather than waiting days. If the profile is invalid or ART rejects it, ProfileInstaller logs warnings without crashing. Use androidx.benchmark:benchmark-macro-junit4 to generate profiles via the BaselineProfileRule. Document profile generation in CI so profiles are updated with each release.

Read more: https://developer.android.com/topic/performance/baselineprofiles

Q139. How do you handle strict mode policies in development to catch main thread violations early? Enable StrictMode.ThreadPolicy to detect disk reads, disk writes, and network calls on the main thread with penaltyDeathOnNetwork() or penaltyLog(). Enable StrictMode.VmPolicy to detect leaked SQLiteCursor, Activity, and Closeable objects, plus untagged sockets. Configure policies in Application.onCreate() wrapped in if (BuildConfig.DEBUG). Use penaltyListener to send violations to Crashlytics or custom logging in internal builds. For coroutines, StrictMode helps identify accidental Dispatchers.Main usage for I/O. Fix violations by moving operations to Dispatchers.IO, using AsyncLayoutInflater, or deferring initialization. Don't enable penaltyDeath() in production; use logging only. Use StrictMode.noteSlowCall() to annotate expected slow operations on the main thread. Document expected exceptions (e.g., Firebase init) with StrictMode.allowThreadDiskReads() temporarily.

Read more: https://developer.android.com/reference/android/os/StrictMode


System Integration & Distribution

Q140. How do you leverage Android App Bundles and Dynamic Delivery to reduce install size and enable on-demand features? Build as Android App Bundle (AAB) instead of APK; Google Play generates optimized APKs per device configuration (ABI, density, language) reducing install size by ~20%. Use Dynamic Feature Modules (com.android.dynamic-feature) for large or rarely used features (AR, heavy media, advanced editing). Request on-demand installation via SplitInstallManager.startInstall() with a loading UI and progress listener. Use install-time delivery for critical features and on-demand for optional ones. Implement deferred uninstall via SplitInstallManager.deferredUninstall() to reclaim space. Handle module uninstallation gracefully by checking SplitInstallHelper.updateAppContext(). Monitor install success rates and cancellation via Play Console. For instant experiences, use dist:instant="true". Ensure the base module contains all core functionality; dynamic modules must not be required for app launch.

Read more: https://developer.android.com/guide/app-bundle

Q141. How do you handle in-app updates using Play In-App Updates API? Use AppUpdateManager to check for available updates via appUpdateManager.appUpdateInfo. For critical updates, trigger an immediate update flow that blocks UI until completed using startUpdateFlowForResult() with AppUpdateType.IMMEDIATE. For flexible updates, show a snackbar prompting the user to download in the background using AppUpdateType.FLEXIBLE; monitor progress via InstallStateUpdatedListener and complete installation with completeUpdate() when the app is backgrounded. Always check updateAvailability() and isUpdateTypeAllowed() before launching flows. Handle user denial gracefully—don't force updates unless security-critical. Test with internal app sharing which supports in-app update testing. Use version code checks to ensure the update is actually newer. Monitor update adoption rates via Play Console. Wrap the API in a repository for testability.

Read more: https://developer.android.com/guide/playcore/in-app-updates

Q142. How do you implement and manage a crash reporting and logging strategy across multiple environments? Use Firebase Crashlytics for production crash reporting with custom keys (Crashlytics.setCustomKey("user_tier", tier)) and non-fatal logging (Crashlytics.recordException(e)). Use Timber for debug logging with a custom tree that forwards to Crashlytics only in release builds. Maintain separate Firebase projects for dev, staging, and prod to isolate crash data. Use BuildConfig.DEBUG to switch log levels and tree planting. For sensitive data, implement a RedactingTree that scrubs PII before sending to remote logs. Use BreadCrumb logging to trace user actions leading to crashes. In CI, symbolicate native crashes with ndk upload paths. Monitor crash-free user rate and prioritize top crashes by affected user count. Integrate with alerting (PagerDuty/Slack) for crash rate spikes. Regularly audit and resolve ignored/non-fatal exceptions to prevent noise.

Read more: https://developer.android.com/studio/debug


Wearables, Native & Advanced

Q143. How do you architect a Wear OS app that complements a mobile app without duplicating logic? Use the same domain and data layers via Kotlin Multiplatform or shared library modules, exposing business logic through a Wear-specific presentation layer. Communicate with the phone via Wearable Data Layer API (DataClient, MessageClient, CapabilityClient) for syncing small data payloads, or use Bluetooth/WiFi direct for larger transfers. Implement complications (ComplicationDataSourceService) and tiles (TileService) for glanceable surfaces that fetch data from a local cache synced from the phone. Use HealthServices (PassiveMonitoringClient) for fitness data instead of polling sensors. Keep the Wear app functional offline with local Room database caching. Use WatchFace APIs for custom watch faces if applicable. Test on physical Wear devices and emulators with round/square configurations. Minimize network calls—batch syncs via WorkManager constrained to charging. Ensure the Wear UI follows Material design for Wear with large touch targets.

Read more: https://developer.android.com/training/wearables

Q144. How do you manage native memory and JNI boundaries when integrating C++ libraries? Use try/finally or Cleaner (Java 9+) to ensure native resources are released. Allocate native memory via ByteBuffer.allocateDirect() for zero-copy transfer between Java and C++, or use MemoryFile for shared memory scenarios. Pass primitive arrays and DirectByteBuffer to JNI rather than object graphs to minimize marshalling overhead. Keep JNI calls coarse-grained—batch operations rather than calling native methods in tight loops. Use System.loadLibrary() in a static block, handling UnsatisfiedLinkError gracefully for missing ABIs. Profile native heap with Android Studio Native Memory Profiler. Ensure thread safety by attaching native threads to the JVM via AttachCurrentThread. Use WeakGlobalRef sparingly to avoid GC issues. Package only required ABIs (arm64-v8a, armeabi-v7a) to reduce APK size. Document JNI function signatures with javah or manual prototypes.

Read more: https://developer.android.com/training/articles/perf-jni

Q145. How do you architect a photo editor with undo/redo, filters, and non-destructive editing using GPU acceleration? Use GLSurfaceView or TextureView with custom GLRenderer for real-time GPU filter previews via OpenGL ES shaders. Maintain an edit history stack as a list of EditOperation sealed classes (Crop, Filter, Rotate) applied sequentially. For non-destructive editing, store the original bitmap and the operation stack; render the final image by replaying operations on the GPU rather than mutating a single bitmap. Use RenderScript (deprecated) or GPUImage / custom shaders for filters. Implement undo by removing the last operation from the stack and re-rendering. Use SurfaceTexture for camera preview integration. Export the final image asynchronously using CoroutineWorker with Dispatchers.Default. Cache intermediate textures to avoid recomputing the full stack on every preview frame. Test memory usage with large images (12MP+) to avoid OOM.

Read more: https://developer.android.com/guide/topics/graphics/opengl


Testing, Lint & Quality

Q146. How do you write custom Lint rules to enforce architectural boundaries in a multi-module project? Create an IssueRegistry extending LintRegistry and registering custom Detector implementations. Implement a Detector with UastScanner to inspect Kotlin/Java AST nodes, checking for illegal imports (e.g., android.content.Context in ViewModels) or cross-module dependencies. Define Issue metadata with severity (Severity.ERROR), category, and explanation. Package the rules in a dedicated Java/Kotlin module applied via lintChecks dependency. Run custom lint alongside Android Lint via ./gradlew lint. For module boundary enforcement, detect import statements referencing forbidden packages or use PsiJavaFile analysis. Use Context.getProject() to identify module names and enforce that feature:a doesn't import feature:b:impl. Document rules with quickfix suggestions. Update the registry when adding new detectors. Test detectors with LintDetectorTest for unit-level validation.

Read more: https://developer.android.com/studio/write/lint

Q147. How do you implement and test accessibility services integration beyond basic TalkBack support? Implement AccessibilityService for custom hardware or assistive technology integration, overriding onAccessibilityEvent() to react to window changes and onInterrupt() for cleanup. Use AccessibilityNodeInfo to programmatically interact with UI elements from the service. For app accessibility, ensure custom views implement ExploreByTouchHelper or override onInitializeAccessibilityNodeInfo() to expose semantics. Test with Accessibility Scanner, TalkBack, and Switch Access. Use uiautomator for automated accessibility testing querying nodes by content description. Verify focus traversal order with keyboard navigation (Tab/Shift+Tab). Support AccessibilityActions like ACTION_CLICK and ACTION_SCROLL_FORWARD. Handle AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED efficiently to avoid infinite loops. Document minimum accessibility requirements in PR checklists. For Braille displays, ensure text content is fully exposed via contentDescription or view text.

Read more: https://developer.android.com/guide/topics/ui/accessibility

Q148. How do you handle complex notification interactions including direct reply, progress, and grouped summaries? Use NotificationCompat.Builder with addAction containing a RemoteInput for direct reply actions, processing the input via a BroadcastReceiver or Service. For progress notifications, use setProgress(max, current, false) and update via NotificationManager.notify() with the same ID. Group related notifications using setGroup() and provide a summary notification with setGroupSummary(true) and InboxStyle or BigTextStyle. Use notification channels with appropriate importance levels and sound/vibration settings. Handle notification dismissal via DeleteIntent. Use BubbleMetadata for conversations on Android 11+. For media, use MediaStyle with MediaSession token. Test notification behavior under Do Not Disturb and with notification permission (Android 13+). Use NotificationManager.areNotificationsEnabled() to check status before posting.

Read more: https://developer.android.com/develop/ui/views/notifications


Startup & Optimization

Q149. How do you use the App Startup library to optimize initialization order and reduce cold start time? Use InitializationProvider as a content provider that automatically discovers Initializer<T> implementations via manifest metadata, eliminating manual Application.onCreate bloat. Implement Initializer<T> for each library (WorkManager, Timber, Firebase) with dependencies() declaring prerequisites. The library initializes components in topological order based on the dependency graph. For lazy initialization, don't list the initializer in manifest; call AppInitializer.getInstance(context).initializeComponent(MyInitializer::class.java) manually when needed. Move non-critical initialization to background threads within Initializer.create(). Combine with ProfileInstaller to ship Baseline Profiles. Measure startup improvement via Macrobenchmark. Avoid using ContentProvider directly for libraries that support Startup library integration. The overhead is minimal compared to manual initialization, and dependency ordering is explicit rather than implicit.

Read more: https://developer.android.com/topic/libraries/app-startup

Q150. How do you architect large-scale A/B testing of UI and behavior in an Android app? Use a server-driven feature flag system (Firebase Remote Config, LaunchDarkly, or custom) to assign users to experiment buckets deterministically based on stable user IDs or device hashes. Define experiments in a data class Experiment(val name: String, val variant: String). Evaluate variants in ViewModels or UseCases, never in UI directly, to keep logic testable. Use Analytics to log exposure events (experiment_exposed) for analysis. Ensure assignment is consistent across sessions by caching the variant locally. For UI experiments, use Compose's conditional rendering or View-based visibility toggles. For backend-dependent experiments, include the variant in API headers so the backend can coordinate behavior. Run experiments for statistically significant durations (2 weeks minimum). Use holdout groups (0% vs 100%) to measure long-term impact. Document experiment hypotheses and success metrics before launch.

Read more: https://developer.android.com/topic/architecture

Last updated