Comprehensive list of scenario-based technical questions for a Senior Android Engineer interview, covering various aspects of Android development and software engineering practices:
Scenario 1: Rewriting a Legacy App
Question: How would you approach rewriting a legacy Android app to improve scalability and maintainability?
Answer:
I would adopt Clean Architecture to structure the app into independent layers:
- Domain Layer for business logic
- Data Layer for repository and API interactions
- Presentation Layer for UI using MVVM
I would migrate the app incrementally to avoid disruptions, starting with small modules and ensuring extensive unit tests for the rewritten sections. Dependency Injection (e.g., Hilt) would manage dependencies, and new features would be developed using the improved architecture while the legacy sections are refactored.
Scenario 2: Navigation Crash
Question: Users report crashes when navigating between fragments. How would you fix this?
Answer:
I would investigate the crash logs to pinpoint lifecycle issues, such as using retained fragments incorrectly. Transitioning to the Android Navigation Component resolves most navigation-related lifecycle issues, ensuring safe fragment transactions. Adding null checks and default fallbacks would prevent null pointer crashes. Tools like Firebase Crashlytics help monitor the fix in production.
Scenario 3: API Integration
Question: How do you manage third-party API integrations to make the app robust and future-proof?
Answer:
I’d encapsulate the API logic in a Repository Layer, isolating the app from changes to the API. Libraries like Retrofit handle networking, while Result wrappers ensure error states are managed gracefully. To future-proof, I’d use an abstraction layer so that switching APIs later won’t require massive codebase changes.
Scenario 4: RecyclerView Performance
Question: The RecyclerView in your app lags with large datasets. How would you optimize it?
Answer:
I’d optimize the ViewHolder
pattern, avoiding redundant inflation of views. Leveraging ListAdapter with DiffUtil
improves performance for dynamic datasets. For heavy image loads, libraries like Glide or Picasso are used with proper caching enabled. If the dataset is very large, Paging 3 ensures efficient, paginated loading.
Scenario 5: Unresponsive API Calls
Question: Users complain about unresponsive API calls. How do you handle this?
Answer:
To address this, I’d offload API calls to Coroutines on Dispatchers.IO
to ensure they don’t block the main thread. Adding timeouts and retry mechanisms in OkHttp Interceptors prevents unresponsiveness. Showing a loading state keeps users informed, and local caching reduces API dependency for repeated data.
Scenario 6: Secure Token Storage
Question: How do you securely store API tokens in an Android app?
Answer:
For API 23+, I’d use EncryptedSharedPreferences or the Android Keystore System to store tokens securely. To prevent exposure during transit, all API requests are made over HTTPS, and sensitive tokens are refreshed periodically using OAuth mechanisms.
Scenario 7: Testing Flaky UI Tests
Question: How do you address flaky UI tests in your CI pipeline?
Answer:
I’d identify the root causes, such as timing issues or non-deterministic behaviors. Using IdlingResources ensures UI tests wait for background tasks to complete. Tests are rewritten for determinism by mocking external dependencies. For UI animations, disabling animations during testing improves reliability.
Scenario 8: Multi-Environment Support
Question: How do you configure an Android app for multiple environments (dev, staging, prod)?
Answer:
I’d use Gradle build flavors to define separate configurations for each environment. Each flavor would have its own properties file for environment-specific settings like base URLs or API keys. This setup ensures clean separation and easy switching during development and deployment.
Scenario 9: Junior Developer Code Review
Question: A junior developer’s pull request contains suboptimal code. How do you address this?
Answer:
I’d provide constructive feedback by highlighting specific issues, such as code inefficiencies or deviation from standards. Suggestions would be supported with documentation or examples. To improve learning, I’d schedule a pair programming session to guide them in implementing the changes.
Scenario 10: Periodic Background Jobs
Question: How do you fetch data periodically in an Android app without impacting performance?
Answer:
I’d use WorkManager with periodic work constraints. Configurations like network type and battery usage constraints ensure efficiency. Exponential backoff handles retries on failure, and app state is preserved using Data objects passed to the worker.
Scenario 11: Slow Build Times
Question: Your team is experiencing slow build times. How would you address this issue?
Answer:
I’d analyze the build process using Gradle’s build scan tools to identify bottlenecks, such as excessive task configurations or dependencies. Recommendations include:
- Dependency resolution: Use API instead of implementation for unnecessary module dependencies.
- Parallel execution: Enable parallel execution in Gradle.
- Build caching: Configure remote caching to speed up builds in CI environments.
- Modularization: Break the app into smaller modules, improving build times for incremental changes.
Scenario 12: App State Restoration
Question: How would you handle app state restoration after process death?
Answer:
I’d use ViewModel and SavedStateHandle to persist critical UI state. For larger datasets or app-wide data, Room Database or SharedPreferences would store non-sensitive data. The onSaveInstanceState method ensures transient states (like scroll positions) are restored using Bundle
.
Scenario 13: Dependency Updates
Question: A library you depend on has released a critical update. How do you handle updating dependencies?
Answer:
I’d analyze the release notes and change logs to understand potential impacts. Before updating, I’d test the new version in a separate branch or a feature flag. CI pipelines would run full regression tests to ensure no functionality breaks. Gradle’s dependency locking helps manage controlled updates in the team.
Scenario 14: Multi-Module Navigation
Question: How would you implement navigation in a multi-module app?
Answer:
I’d use the Navigation Component with dynamic feature modules. Deep links are utilized for cross-module navigation. Navigation logic remains within each module, and shared arguments are passed using SafeArgs
. Dynamic feature module delivery ensures lightweight APKs for specific features.
Scenario 15: Offline-First Architecture
Question: How would you design an offline-first app?
Answer:
I’d use a Room Database as the single source of truth, with APIs syncing data in the background using WorkManager. Conflict resolution strategies (e.g., timestamp-based syncing) ensure consistency. Data updates are observed via LiveData or Flow, providing a seamless offline and online experience.
Scenario 16: Memory Leaks in Fragments
Question: How would you debug and resolve memory leaks in fragments?
Answer:
I’d use tools like LeakCanary to detect leaks. Common issues include retaining fragment references in ViewModels or adapters. Avoiding anonymous inner classes and clearing listeners in onDestroyView
prevents leaks. Switching to Fragment KTX utilities simplifies fragment lifecycle handling.
Scenario 17: Expanding App to Tablets
Question: How would you optimize your app to support tablet devices?
Answer:
I’d implement responsive layouts using resource qualifiers (layout-w600dp
, etc.). ConstraintLayout or Compose's BoxWithConstraints
helps create flexible designs. Tablets would leverage split-screen functionality and dual-pane layouts for a richer experience. I’d test on actual devices or simulators with varying aspect ratios.
Scenario 18: Network State Handling
Question: How would you handle intermittent network connectivity in your app?
Answer:
Using ConnectivityManager, I’d monitor network status and notify users of connectivity changes. For intermittent connections, I’d use WorkManager with a NetworkType.CONNECTED
constraint for retries. Local caching with Room or offline-first design ensures minimal disruption for users.
Scenario 19: ProGuard Rules Issue
Question: After enabling ProGuard, some features break in release builds. How do you troubleshoot this?
Answer:
I’d check ProGuard logs for obfuscation rules causing issues. Adding keep
rules for critical classes or libraries resolves the problem. For example, libraries like Gson need rules to retain serialized class names. Testing the release APK in a staging environment prevents such errors from reaching production.
Scenario 20: Gradle Dependency Conflicts
Question: How do you handle dependency version conflicts in a multi-module project?
Answer:
I’d resolve dependency conflicts by enforcing versions in the root build.gradle
using the constraints
block. Gradle’s dependency resolution tools or ./gradlew dependencies
help identify conflicts. Upgrading to a shared compatible version or excluding transitive dependencies ensures compatibility.
Scenario 21: Gradual Migration to Jetpack Compose
Question: How would you migrate an app with traditional XML layouts to Jetpack Compose without disrupting ongoing development?
Answer:
I’d use ComposeView to integrate Jetpack Compose into existing XML-based screens. This allows for gradual migration by converting one feature or screen at a time. The strategy ensures compatibility between Compose and View-based UIs. Migration priorities would be screens with simple layouts or those undergoing redesigns. Thorough testing would validate seamless UI interactions during the transition.
Scenario 22: Managing App Performance During Animation
Question: How do you optimize animations to maintain 60 FPS in an Android app?
Answer:
I’d use Jetpack Compose animations or Android’s Animator API, ensuring minimal main-thread blocking. Optimizations include precomputing values, reducing overdraw with proper layering, and using lightweight vector assets instead of large bitmaps. Profiling tools like GPU Profiler or Layout Inspector help identify performance bottlenecks. Asynchronous animations using Coroutines also minimize UI thread load.
Scenario 23: Handling Sensitive Data in Logs
Question: How do you ensure sensitive user data doesn’t appear in logs during debugging?
Answer:
I’d avoid logging sensitive data altogether by following best practices like:
- Tag masking for potentially sensitive information.
- Utilizing
Log.d()
only in debug builds usingBuildConfig.DEBUG
. - Leveraging tools like Timber for conditional logging and ensuring
proguard-rules.pro
strips out all logs in release builds.
Scenario 24: App Crash on Specific Devices
Question: Your app works fine on most devices but crashes on a few specific models. How would you debug this?
Answer:
I’d start by analyzing device-specific crash logs through Firebase Crashlytics or Play Console’s Vitals. Common issues include hardware limitations, API compatibility, or vendor-specific customizations. I’d test on emulators or real devices mimicking those models and apply device-specific feature toggles or fallback logic to ensure stability.
Scenario 25: Adopting KMM (Kotlin Multiplatform Mobile)
Question: How would you decide if Kotlin Multiplatform Mobile (KMM) is suitable for your project?
Answer:
I’d evaluate if the app’s business logic can be shared across platforms. Projects with extensive API integrations or business rules benefit most from KMM. I’d ensure the team is comfortable with Kotlin and modular architecture. UI layers would remain native (Jetpack Compose for Android and SwiftUI for iOS), while the shared code handles networking, data parsing, and logic.
Scenario 26: Debugging High Battery Usage
Question: Users report your app drains their battery quickly. How would you address this?
Answer:
Using Android Studio’s Energy Profiler, I’d monitor wake locks, background jobs, and location services. Optimizations include batching background tasks with WorkManager, reducing GPS usage frequency with fused location providers, and leveraging low-power modes for periodic operations. Removing unnecessary foreground services helps reduce power consumption.
Scenario 27: Scaling an App for Millions of Users
Question: How would you scale an Android app to handle millions of active users?
Answer:
I’d optimize the app for minimal network usage using effective caching strategies (e.g., Room or DiskLruCache). Efficient API pagination and batched updates would reduce server strain. Metrics and crash monitoring tools (e.g., Firebase Analytics, Sentry) provide insights into performance and usage patterns. Incremental rollout strategies prevent widespread issues.
Scenario 28: Addressing App Uninstall Feedback
Question: How do you act on feedback that users uninstall your app due to poor onboarding?
Answer:
I’d streamline the onboarding process by reducing friction with concise, visually engaging walkthroughs. Adding in-app prompts for permissions only when necessary improves the first experience. A/B testing onboarding flows and analyzing retention metrics help refine the process.
Scenario 29: Maintaining Backward Compatibility
Question: How do you support legacy Android devices while using modern features?
Answer:
I’d use Jetpack Libraries to provide backward-compatible implementations of modern features. Features exclusive to higher API levels are guarded with Build.VERSION.SDK_INT
checks. Custom implementations or polyfills provide fallbacks for unsupported features on older devices.
Scenario 30: Real-Time Feature Flags
Question: How would you implement feature flags for dynamic control over app behavior?
Answer:
I’d integrate a feature flag service like Firebase Remote Config or a custom implementation using a REST API and local caching. Flags are dynamically fetched at app startup and stored in local preferences. The code paths for new features remain toggleable, allowing easy enable/disable scenarios without requiring an app update.
Scenario 31: Integrating Payments
Question: How do you integrate in-app purchases securely?
Answer:
I’d use Google Play Billing API for secure purchase handling. Verifying purchases with the server ensures authenticity. Sensitive business logic is implemented server-side, and the app only displays validated purchase states. Subscription models follow best practices for automatic renewal handling and cancellation prompts.
Scenario 32: Codebase Documentation
Question: Your team is struggling with understanding parts of the codebase. How do you address this?
Answer:
I’d introduce KDoc comments in the code for method and class documentation. A centralized wiki system like Confluence or GitHub Pages provides detailed guides and architecture diagrams. Regular knowledge-sharing sessions ensure all team members are aligned.
Scenario 33: Leading a Team through Technical Debt
Question: Your team is facing significant technical debt due to quick fixes in the past. How would you prioritize and address this challenge?
Answer:
I’d start by identifying critical areas of technical debt using static analysis tools (e.g., SonarQube) and gathering feedback from developers. I’d prioritize issues that impact performance, security, or maintainability. During sprint planning, I would allocate time for refactoring while ensuring that new features are still being delivered. Introducing Code Reviews with an emphasis on long-term maintainability helps avoid further accumulation of technical debt. Additionally, I’d encourage the team to gradually improve the codebase with regular cleanup tasks and foster a culture of continuous refactoring.
Scenario 34: Managing Conflicting Opinions in the Team
Question: During a sprint planning session, two developers strongly disagree on the architecture approach for a feature. How would you handle this conflict?
Answer:
I’d listen to both developers and ensure they explain their reasoning clearly, focusing on the pros and cons of each approach. I’d encourage data-driven discussions (e.g., performance benchmarks, previous project examples). If needed, I’d bring in a third-party expert (e.g., senior developer or architect) to help make an informed decision. If the disagreement persists, I’d consider running a proof-of-concept (POC) to evaluate both approaches in real-world conditions. This fosters a collaborative environment while helping the team make the best technical decision for the feature.
Scenario 35: Onboarding a New Developer to an Existing Codebase
Question: How would you onboard a new developer who is unfamiliar with your app’s architecture and codebase?
Answer:
I’d provide the new developer with a structured onboarding guide, including:
- Codebase Overview: Detailed documentation on the architecture, technologies used, and the project structure.
- Key Concepts: Introduce design patterns like MVVM, dependency injection (e.g., Hilt), and CI/CD pipelines.
- Hands-On Tasks: Assign simple tasks (e.g., bug fixes or small feature enhancements) to help them get familiar with the codebase and development workflow.
- Mentorship: Pair the developer with a more experienced team member for code reviews and guidance.
Regular check-ins ensure they feel supported throughout the process.
Scenario 36: Automated Tests in a CI/CD Pipeline
Question: How would you ensure that all tests are passing before deploying an app in a CI/CD pipeline?
Answer:
I’d configure the CI/CD pipeline (e.g., Jenkins, GitHub Actions) to run automated tests for every commit or pull request. This includes unit tests (via JUnit), UI tests (via Espresso or Compose Test), and integration tests. Code coverage tools (e.g., JaCoCo) would ensure comprehensive testing. Additionally, I’d enable static analysis tools like Lint and SonarQube to check for code quality issues. The pipeline would fail if any tests fail, preventing broken code from reaching production.
Scenario 37: Dealing with Deployment Failures
Question: You deploy a new version of the app, but users report issues. How do you handle this situation?
Answer:
I’d immediately roll back the deployment if the issues are critical, using Google Play Console’s staged rollout feature to limit exposure. Then, I’d review crash reports from Firebase Crashlytics and check analytics for affected users. Once the root cause is identified, I’d prioritize a hotfix, test it in a staging environment, and then redeploy. Post-mortem analysis would help understand what went wrong and ensure better testing or monitoring practices in the future. Communication with users about the issue and its resolution is essential for trust.
Scenario 38: Testing with Multiple Android Versions
Question: How would you ensure that your app works across various Android versions and devices?
Answer:
I’d set up automated tests targeting multiple Android versions using Firebase Test Lab or Sauce Labs to test on real devices. I’d include API version checks within the app to handle specific features and permissions for older Android versions. Additionally, I’d manually test on critical versions (e.g., Android 10, 11, 12, etc.) and leverage tools like Android Device Monitor to simulate devices with different screen sizes, resolutions, and OS versions.
Scenario 39: Continuous Integration (CI) Configuration
Question: You need to set up a new CI/CD pipeline for an Android project. What tools and steps would you use?
Answer:
I’d choose Jenkins, GitHub Actions, or CircleCI as CI tools. The pipeline steps would include:
- Clone Repository: Pull the latest code from the version control system (e.g., GitHub, GitLab).
- Build: Use Gradle to build the APK.
- Static Analysis: Run Lint and SonarQube checks for code quality.
- Run Tests: Execute unit tests (JUnit), UI tests (Espresso), and instrumented tests.
- Publish: Upload the build to Google Play Console or Firebase App Distribution for testing.
- Deploy: Deploy to production only after passing all tests and reviews.
This setup automates the process and ensures consistency across environments.
Scenario 40: Code Freeze Before Release
Question: How do you manage a code freeze before a major release?
Answer:
Before the code freeze, I’d ensure all features for the release are completed and tested. The freeze date is communicated well in advance. During the freeze, only critical bug fixes and high-priority tasks are allowed. I’d schedule QA testing, run regression tests, and address any remaining issues promptly. A branch protection strategy is enforced, ensuring no new features or changes are merged during the freeze. The focus is on stabilizing the app for release, ensuring that no new bugs are introduced.
Scenario 41: Performance Testing and Optimization
Question: How would you approach performance testing and optimization for an Android app?
Answer:
I’d start by identifying critical areas that affect performance, such as startup time, network requests, memory usage, and UI responsiveness. Tools like Android Profiler, Systrace, and Firebase Performance Monitoring help pinpoint issues. For optimization:
- Lazy loading reduces memory consumption and startup time.
- Background tasks (e.g., using WorkManager) are optimized to avoid battery drain.
- RecyclerView optimizations like ViewHolder patterns and DiffUtil for list updates.
- Image loading is done efficiently using libraries like Glide or Picasso to handle caching and resizing.
- Threading and Coroutines ensure smooth UI interactions by avoiding main-thread blocking.
Scenario 42: Handling App Crashes in Production
Question: How do you respond if your app crashes in production and affects a significant portion of users?
Answer:
I’d first check Firebase Crashlytics or Play Console crash reports to identify the crash’s root cause. If the issue is critical, I’d initiate a hotfix and ensure it passes all necessary tests before redeploying. Rollbacks or staged rollouts minimize user impact while fixing the issue. In parallel, I’d work on a longer-term solution to prevent similar issues, such as adding more test coverage, improving error handling, or adding more specific logging. Clear communication with users is also essential during this process.
Would you like even more specific scenarios or focus on any other technical areas? Let me know in comments below !