Android Interview Guide: Master the Fundamentals for 2025


Android Core Interview Prep (Kotlin + Jetpack Compose) – 50 Essential Questions & Answers

Android Interview Core Guide

Q1. Explain Activity & Fragment lifecycle. How do you handle process death & configuration changes?

Activity Lifecycle:

  • onCreate() → Initialize UI, bind ViewModel, restore saved state.
  • onStart() → Activity becomes visible but not interactive.
  • onResume() → Foreground, interactive.
  • onPause() → Partially visible (release sensors/camera).
  • onStop() → Not visible (release heavy resources).
  • onDestroy() → Cleanup.

Fragment Lifecycle (extra states):

  • onAttach() → Fragment attached to Activity.
  • onCreateView() → Inflate layout.
  • onViewCreated() → Bind views.
  • onDestroyView() → Cleanup ViewBinding (to avoid memory leaks).

Handling Config Changes:

  • Use ViewModel for data across rotations.
  • Use SavedStateHandle or onSaveInstanceState for transient data.
  • Persist critical data in Room DB/SharedPreferences.
  • Avoid android:configChanges unless absolutely required.

Handling Process Death:

  • Use savedInstanceState + SavedStateHandle.
  • Restore data in onCreate.
  • In Jetpack Compose, use rememberSaveable.

Code Example:

class MyViewModel(savedStateHandle: SavedStateHandle) : ViewModel() {
    private val counter = savedStateHandle.getLiveData("counter", 0)
    fun increment() {
        counter.value = (counter.value ?: 0) + 1
    }
}

Follow-up:

  • Difference between ViewModel vs onSaveInstanceState?
    → ViewModel survives config changes, not process death.
    → onSaveInstanceState survives both.

Q2. Difference between Service, Foreground Service, IntentService, and WorkManager.

  • Service: Background task, no UI, runs on main thread unless managed.
  • Foreground Service: Ongoing task with persistent notification (music, GPS).
  • IntentService (deprecated): Background thread, auto-stops when task finishes.
  • WorkManager: Deferrable, guaranteed execution, supports constraints (Wi-Fi, charging).

Best practice:

  • Use ForegroundService for ongoing tasks.
  • Use WorkManager for guaranteed background jobs.
  • Do not use IntentService in new projects.

Q3. How does Handler, Looper, and MessageQueue work?

  • Looper: Each thread can have a Looper processing messages.
  • MessageQueue: FIFO queue of tasks.
  • Handler: Posts tasks/messages to Looper.

Code Example:

val handler = Handler(Looper.getMainLooper())
handler.post {
    textView.text = "Updated on main thread"
}

Follow-up:

  • Why avoid Thread.sleep() in main thread?
    → It blocks Looper → causes ANR.

Q4. How do you prevent memory leaks?

Causes:

  • Static references to Activity/Fragment.
  • Long-lived coroutines not cancelled.
  • Listeners not unregistered.
  • ViewBinding not cleared in Fragments.

Prevention:

  • Use applicationContext for long-lived objects.
  • Cancel coroutines in onDestroy() or onCleared().
  • Clear ViewBinding in onDestroyView().
  • Use WeakReference when required.
  • Detect leaks with LeakCanary.

Follow-up:

  • Common RecyclerView leak?
    → Adapter holding Activity reference.

Q5. LiveData vs StateFlow vs SharedFlow.

  • LiveData: Lifecycle-aware, Android-only, simple.
  • StateFlow: Hot flow, always has latest value, great for UI state.
  • SharedFlow: Hot flow for one-time events (snackbar, navigation), supports replay.

Best practice:

  • Use StateFlow for state.
  • Use SharedFlow for events.
  • Convert to LiveData only for legacy code.

Q6. How do you debug and fix ANR?

Causes:

  • Blocking DB or network call on main thread.
  • Infinite loops.
  • Too much work in BroadcastReceiver or onCreate.

Fix:

  • Move I/O to Dispatchers.IO.
  • Optimize queries with indexes.
  • Use WorkManager for background jobs.

Debug tools:

  • adb shell dumpsys activity ANR
  • Android Studio Profiler.

Follow-up:

  • Simulate ANR → Thread.sleep(10000) in main thread.

Q7. How do you reduce app startup time?

Optimizations:

  • Delay heavy initializations.
  • Use App Startup library.
  • Avoid blocking in Application.onCreate.
  • Use SplashScreen API for smooth UX.

Debug tools:

  • Startup Profiler.
  • Traceview.

Follow-up:

  • Should Firebase init be in Application.onCreate?
    → No, unless critical. Lazy init recommended.

Q8. Difference between suspend function, launch, async.

  • suspend: Pausable, non-blocking function.
  • launch: Fire-and-forget coroutine, returns Job.
  • async: Concurrent execution, returns Deferred<T>.

Code Example:

viewModelScope.launch {
    val user = async { api.getUser() }
    val posts = async { api.getPosts() }
    println("User: ${user.await()}, Posts: ${posts.await()}")
}

Follow-up:

  • Use async when results are needed.
  • Use launch for side-effects.

Q9. What is structured concurrency?

  • Coroutines are bound to a scope.
  • If parent is cancelled, children are cancelled.
  • Prevents leaks & orphan jobs.

Code Example:

suspend fun fetchData() = coroutineScope {
    val user = async { api.getUser() }
    val posts = async { api.getPosts() }
    user.await() to posts.await()
}

Follow-up:

  • Difference:
    • coroutineScope → failure cancels siblings.
    • supervisorScope → siblings continue.

Q15. MVVM vs MVI vs MVP. Why Clean Architecture?

  • MVP: Presenter → tight coupling, harder to test.
  • MVVM: ViewModel holds state, UI observes (recommended by Google).
  • MVI: Unidirectional, predictable state, verbose.

Clean Architecture:

  • UI → ViewModel → UseCase → Repository → DataSource.
  • Improves testability & scalability.

Q16. How do you design an offline-first app?

  • Room DB = single source of truth.
  • Repository exposes Flow from DB.
  • Sync with server using WorkManager.
  • UI always reads from DB.

Best practice:

  • Use NetworkBoundResource or Mediator pattern.

Q17. How do you structure a modular Android project?

Modules:

  • app: entry, DI setup.
  • core: networking, utils.
  • feature-*: independent features.
  • shared: design system, common components.

Benefits:

  • Faster builds.
  • Independent feature dev/testing.
  • Supports dynamic feature delivery.

Q21. How do you detect and fix memory leaks?

Tools:

  • LeakCanary.
  • Android Profiler.

Fixes:

  • Clear ViewBinding in onDestroyView().
  • Cancel coroutines in onCleared().
  • Avoid static references to Context.

Q22. How do you optimize RecyclerView performance?

  • Use DiffUtil/ListAdapter.
  • Call setHasFixedSize(true).
  • Use RecycledViewPool for multiple lists.
  • Use Paging 3 for large lists.
  • Avoid nested RecyclerViews.

Follow-up:

  • Debug jank with Profile GPU Rendering.

Q23. What is overdraw?

  • Drawing the same pixel multiple times.
  • Debug: GPU Overdraw tool.

Fix:

  • Remove redundant backgrounds.
  • Flatten layouts.
  • Avoid nested transparency.

Q24. How do you reduce battery usage in background tasks?

  • Use WorkManager with constraints.
  • Batch network calls.
  • Prefer push notifications over polling.
  • Optimize location updates with FusedLocationProvider.

Best practice:

  • Avoid foreground services unless absolutely needed.

📘 Android Kotlin & Flow Interview Guide


Q10. What are Coroutines Dispatchers? How do they differ?

Dispatchers:

  • Dispatchers.Main → Main thread (UI updates).
  • Dispatchers.IO → I/O operations (network, DB).
  • Dispatchers.Default → CPU-heavy work (sorting, parsing).
  • Dispatchers.Unconfined → Starts on current thread, resumes where suspended (rare).

Best practice:

  • Always use withContext(Dispatchers.IO) for blocking I/O.
  • Never block Dispatchers.Main.

Code Example:

viewModelScope.launch(Dispatchers.IO) {
    val data = repository.getData()
    withContext(Dispatchers.Main) {
        textView.text = data
    }
}

Follow-up:

  • What happens if you do Thread.sleep() in Dispatchers.Main?
    → Causes ANR because it blocks UI thread.

Q11. Explain Flow basics and common operators.

Flow basics:

  • Flow = cold asynchronous data stream.
  • Emits values sequentially.
  • Collected with collect {}.

Common operators:

  • map → Transform values.
  • filter → Filter emissions.
  • take(n) → Limit emissions.
  • debounce() → Emit after quiet period.
  • flatMapConcat, flatMapMerge → Flatten flows.

Code Example:

val numbers = flowOf(1, 2, 3, 4, 5)
numbers
    .filter { it % 2 == 0 }
    .map { it * it }
    .collect { println(it) }   // Prints 4, 16

Follow-up:

  • Why is Flow cold?
    → Collection triggers execution. Until collected, nothing runs.

Q12. Difference between Cold Flow and Hot Flow (StateFlow/SharedFlow)?

Cold Flow:

  • Execution starts on collection.
  • Each collector gets independent stream.
  • Example: flow { emit(x) }.

Hot Flow:

  • Always active, even without collectors.
  • Multiple collectors share emissions.
  • Examples: StateFlow, SharedFlow.

Code Example:

val state = MutableStateFlow(0)
state.value = 5   // Always holds latest value

val shared = MutableSharedFlow<Int>()
viewModelScope.launch { shared.emit(10) }

Follow-up:

  • When to use StateFlow? → For UI state.
  • When to use SharedFlow? → For one-time events (navigation, toast).

Q13. Difference between flowOn and withContext.

  • withContext: Changes context once for entire block (suspends).
  • flowOn: Changes context of upstream flow (non-blocking).

Code Example:

flow {
    emit(loadData())   // Heavy I/O
}.flowOn(Dispatchers.IO)   // Switches only upstream
 .collect { println(it) }  // Collected on original dispatcher

Follow-up:

  • What if we use withContext inside flow builder?
    → Flow won’t be cancellable properly → avoid.

Q14. How do you test Flow?

  • Use Turbine library (app.cash.turbine).
  • Collect emissions in test coroutine.
  • Use runTest {} from kotlinx-coroutines-test.

Code Example:

@Test
fun testFlow() = runTest {
    val flow = flowOf(1, 2, 3)
    flow.test {
        assertEquals(1, awaitItem())
        assertEquals(2, awaitItem())
        assertEquals(3, awaitItem())
        awaitComplete()
    }
}

Follow-up:

  • Why not use collect directly?
    → Harder to assert emissions and completion.

Q29. How do you handle backpressure in Kotlin Flow?

Definition:

  • Backpressure = producer emits faster than consumer can process.

Handling techniques:

  • buffer() → Adds channel buffer.
  • conflate() → Drops intermediate values, keeps latest.
  • debounce() → Ignores rapid emissions.
  • sample() → Emits periodically.

Code Example:

flow {
    repeat(1000) {
        emit(it)
    }
}
.buffer()   // Allow producer to keep emitting
.conflate() // Only keep latest
.collect { delay(100); println(it) }

Follow-up:

  • Difference between buffer() vs conflate()?
    → buffer stores all values, conflate skips intermediate values.

📘 Android System Design Interview Guide


Q18. Design WhatsApp chat architecture (1:1 chat).

Requirements:

  • Real-time delivery.
  • Offline storage.
  • Read receipts.
  • End-to-end encryption.

Architecture:

  • Client: Stores chats in Room DB.
  • Server: Message broker (Kafka, RabbitMQ).
  • Transport: WebSocket (persistent).
  • Push notifications for offline delivery.
  • Encryption: AES (content), RSA (key exchange).

Flow:

  1. User A sends → stored in local DB → sent via WebSocket.
  2. Server stores + forwards to User B.
  3. If offline → push notification.
  4. User B receives → stored locally → sends ACK back.

Follow-up:

  • How to sync across devices?
    → Use message IDs + server as source of truth.

Q19. How do you implement Repository pattern?

Definition:

  • Repository mediates between data sources and domain.

Layers:

  • UI → ViewModel → Repository → DataSource (Network/DB).

Code Example:

class UserRepository(
    private val api: ApiService,
    private val dao: UserDao
) {
    fun getUser(id: Int): Flow<User> = flow {
        val cached = dao.getUser(id)
        if (cached != null) emit(cached)

        val remote = api.getUser(id)
        dao.insert(remote)
        emit(remote)
    }
}

Follow-up:

  • Why not call DAO & API directly in ViewModel?
    → Breaks separation of concerns, harder to test.

Q20. How would you implement push notifications system?

Architecture:

  • Server uses FCM (Firebase Cloud Messaging).
  • Client registers device token.
  • Server pushes to FCM → FCM delivers to device.

Steps:

  1. Client app registers with FCM → gets token.
  2. Token sent to backend.
  3. Server sends notification payload to FCM API.
  4. FCM routes to correct device.

Code Example (Client):

FirebaseMessaging.getInstance().token.addOnCompleteListener { task ->
    if (task.isSuccessful) {
        val token = task.result
        sendTokenToServer(token)
    }
}

Follow-up:

  • How to handle notification when app in background?
    → FCM delivers automatically to system tray.

Q25. How do you scale an Android app for millions of users?

Scalability concerns:

  • Networking: Use Retrofit + OkHttp + caching.
  • Data: Room + offline-first sync.
  • Architecture: Modularization for faster builds.
  • Monitoring: Firebase Crashlytics, Performance.
  • Testing: Automated CI/CD pipelines.

Follow-up:

  • What about feature flags?
    → Use Remote Config or LaunchDarkly.

Q26. How to design video streaming (YouTube-like)?

Requirements:

  • Adaptive bitrate.
  • Resume playback.
  • Offline download.

Client:

  • ExoPlayer with DASH/HLS.
  • Cache media chunks with OkHttp cache.

Server:

  • Store video in multiple bitrates.
  • CDN for distribution.
  • Metadata API for titles, comments, etc.

Flow:

  1. App requests manifest (MPD/HLS).
  2. ExoPlayer selects bitrate based on bandwidth.
  3. Segments fetched via CDN.

Follow-up:

  • How to handle slow networks?
    → Adaptive bitrate + prefetch.

Q27. How do you secure APIs in Android?

  • Use HTTPS (TLS).
  • Token-based auth (JWT, OAuth2).
  • Short-lived tokens, refresh tokens.
  • Certificate pinning (OkHttp).
  • Obfuscation with ProGuard/R8.
  • Avoid secrets in APK → use remote config or keystore.

Code Example:

val client = OkHttpClient.Builder()
    .certificatePinner(
        CertificatePinner.Builder()
            .add("yourdomain.com", "sha256/abc123...")
            .build()
    )
    .build()

Follow-up:

  • Can we fully hide API keys in Android?
    → No, only obscure them. Use backend proxy if possible.

Q28. How would you implement offline sync?

Pattern:

  • Local DB = source of truth.
  • Pending operations stored in queue.
  • WorkManager retries sync with server.

Flow:

  1. User edits entity → saved in DB with pending=true.
  2. Sync worker pushes pending changes.
  3. On success, mark as synced.

Code Example:

suspend fun sync() {
    val pending = dao.getPending()
    for (item in pending) {
        val response = api.update(item)
        if (response.isSuccessful) {
            dao.markSynced(item.id)
        }
    }
}

Follow-up:

  • Conflict resolution?
    → Last-write-wins or server-driven merge.

📘 Android Leadership & Behavioral Interview Guide


Q30. How do you mentor junior developers in your team?

Approach:

  • Onboarding: Pair programming, walkthrough of architecture, coding standards.
  • Knowledge sharing: Tech talks, code reviews, documentation.
  • Guided autonomy: Let them solve problems, provide feedback instead of solutions.
  • Feedback loop: Regular 1:1s, retrospective reviews.

Follow-up:

  • What if a junior keeps repeating mistakes?
    → Identify gaps (knowledge vs carelessness). Create a growth plan with examples.

Q31. How do you handle conflicts in a team?

Steps:

  1. Listen actively to both sides.
  2. Focus on problem, not people.
  3. Encourage constructive discussion.
  4. Align with project goals.
  5. If unresolved → escalate respectfully.

Example:

  • Two devs disagree on architecture → organize design review → pros/cons of each → consensus.

Q32. How do you make architecture decisions?

  • Gather requirements (scalability, deadlines).
  • Evaluate trade-offs (MVVM vs MVI, modularization vs monolith).
  • Propose POC (proof of concept).
  • Document decision (ADR – Architecture Decision Record).
  • Revisit decisions when context changes.

Follow-up:

  • Example: Why MVVM over MVP?
    → MVVM better supports reactive UIs with Flow/LiveData.

Q33. How do you communicate with non-technical stakeholders?

  • Avoid jargon → explain in business terms.
  • Use analogies → “Caching is like saving a shortcut instead of recalculating.”
  • Provide timelines with risks & trade-offs.
  • Share visual diagrams instead of long emails.

Follow-up:

  • Example: Explaining app crash issue.
    → “The app stops working due to memory shortage, we need to optimize resources.”

Q34. How do you ensure quality in your team’s code?

  • Define coding guidelines.
  • Enforce PR reviews.
  • Automate checks (ktlint, detekt, unit tests).
  • Add CI/CD pipelines.
  • Encourage TDD or at least unit tests for critical logic.

Follow-up:

  • How to handle deadline pressure?
    → Deliver MVP with quality, avoid hacks that increase tech debt.

Q35. How do you balance coding and leadership responsibilities?

  • Prioritize → delegate tasks where possible.
  • Spend ~50% on coding, ~50% on mentoring, reviews, planning.
  • Block calendar for deep work.
  • Keep communication async when possible (Slack/Email).

Follow-up:

  • What if project is delayed?
    → Step in hands-on, support critical tasks, shield team from stress.

📘 Android Jetpack Compose Interview Guide


Q36. Difference between remember and rememberSaveable.

  • remember: Stores state in memory only → lost on config changes or process death.
  • rememberSaveable: Persists state using SavedStateHandle (Bundle) → survives config changes & process death.

Code Example:

@Composable
fun Counter() {
    var count by remember { mutableStateOf(0) }   // Lost on rotation
    var savedCount by rememberSaveable { mutableStateOf(0) } // Survives

    Column {
        Text("Count: $count | Saved: $savedCount")
        Button(onClick = { count++ }) { Text("Inc Count") }
        Button(onClick = { savedCount++ }) { Text("Inc Saved Count") }
    }
}

Follow-up:

  • When to use remember? → Temporary UI state.
  • When to use rememberSaveable? → State that must survive process death.

Q37. How does Recomposition work in Compose?

  • Compose uses state-driven UI.
  • When mutableStateOf value changes → Composable that reads it re-runs (recomposes).
  • Compose tries to skip unchanged parts (smart diffing).

Follow-up:

  • How to optimize recomposition?
    • Use derivedStateOf for derived values.
    • Hoist state higher in hierarchy.
    • Use remember to cache expensive computations.

Q38. How do you handle side effects in Compose?

  • LaunchedEffect: Runs suspend code tied to key lifecycle.
  • DisposableEffect: Cleanup when leaving composition.
  • SideEffect: Post a side-effect after every recomposition.
  • rememberCoroutineScope: Launch coroutines manually.

Code Example:

@Composable
fun TimerExample() {
    var time by remember { mutableStateOf(0) }

    LaunchedEffect(Unit) {
        while (true) {
            delay(1000)
            time++
        }
    }
    Text("Time: $time")
}

Q39. What is state hoisting in Compose?

  • Moving state up from a Composable → parent controls state, Composable becomes stateless.
  • Improves reusability, testability.

Code Example:

@Composable
fun Counter(count: Int, onIncrement: () -> Unit) {
    Button(onClick = onIncrement) {
        Text("Count: $count")
    }
}

@Composable
fun Parent() {
    var count by remember { mutableStateOf(0) }
    Counter(count) { count++ }
}

Q40. How to use Compose with ViewModel?

  • ViewModel holds state as StateFlow/LiveData.
  • Collect state in Composable using collectAsState() or observeAsState().

Code Example:

class MyViewModel : ViewModel() {
    private val _count = MutableStateFlow(0)
    val count: StateFlow<Int> = _count

    fun increment() { _count.value++ }
}

@Composable
fun CounterScreen(viewModel: MyViewModel = viewModel()) {
    val count by viewModel.count.collectAsState()
    Button(onClick = { viewModel.increment() }) {
        Text("Count: $count")
    }
}

Q41. How do you test Composables?

  • Use Compose Testing APIs (composeTestRule).
  • Test tags with Modifier.testTag.
  • Assertions like assertIsDisplayed, assertTextEquals.

Code Example:

@get:Rule
val composeTestRule = createComposeRule()

@Test
fun testButtonClick() {
    composeTestRule.setContent {
        Counter()
    }
    composeTestRule.onNodeWithText("Count: 0").assertIsDisplayed()
    composeTestRule.onNodeWithText("Inc Count").performClick()
    composeTestRule.onNodeWithText("Count: 1").assertIsDisplayed()
}

📘 Android Advanced Tech Stack Interview Guide


Q42. Explain Dependency Injection (DI) in Android. Why Hilt/Dagger?

Concept:

  • DI = providing dependencies from outside instead of creating inside.
  • Improves testability, reusability, and modularity.

Hilt (recommended):

  • Built on top of Dagger.
  • Provides standard components (Application, Activity, ViewModel scope).
  • Generates boilerplate automatically.

Code Example:

@HiltAndroidApp
class MyApp : Application()

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
    @Inject lateinit var repository: UserRepository
}

Follow-up:

  • Why Hilt over Koin?
    → Hilt is compile-time safe, faster runtime. Koin is simpler but reflection-based.

Q43. How does Room Database work? How do you handle migrations?

Room basics:

  • Entity = table.
  • DAO = data access object.
  • Database = main entry point.

Code Example:

@Entity
data class User(@PrimaryKey val id: Int, val name: String)

@Dao
interface UserDao {
    @Query("SELECT * FROM User WHERE id=:id")
    fun getUser(id: Int): Flow<User>

    @Insert
    suspend fun insert(user: User)
}

@Database(entities = [User::class], version = 2)
abstract class AppDb : RoomDatabase() {
    abstract fun userDao(): UserDao
}

Migration:

val MIGRATION_1_2 = object : Migration(1, 2) {
    override fun migrate(db: SupportSQLiteDatabase) {
        db.execSQL("ALTER TABLE User ADD COLUMN age INTEGER NOT NULL DEFAULT 0")
    }
}

Q44. What is Paging 3? Why use it?

  • Handles large lists efficiently.
  • Built with Kotlin Flow & coroutines.
  • Supports remote + local (Room) sources.

Code Example:

class UserPagingSource(private val api: ApiService) : PagingSource<Int, User>() {
    override suspend fun load(params: LoadParams<Int>): LoadResult<Int, User> {
        val page = params.key ?: 1
        val response = api.getUsers(page)
        return LoadResult.Page(
            data = response,
            prevKey = if (page == 1) null else page - 1,
            nextKey = if (response.isEmpty()) null else page + 1
        )
    }
}

Q45. Explain Navigation Component (XML vs Compose).

  • XML Nav Component: Uses nav_graph.xml + SafeArgs.
  • Compose Navigation: Uses NavHost + rememberNavController().

Code Example (Compose):

@Composable
fun AppNav() {
    val navController = rememberNavController()
    NavHost(navController, startDestination = "home") {
        composable("home") { HomeScreen(onNavigate = { navController.navigate("details") }) }
        composable("details") { DetailsScreen() }
    }
}

Follow-up:

  • How to handle deep links?
    → Define in nav graph or NavDeepLinkBuilder.

Q46. WorkManager vs AlarmManager vs JobScheduler.

  • WorkManager: Guaranteed, deferrable, respects constraints.
  • AlarmManager: Fires at exact/system-defined time, not aware of constraints.
  • JobScheduler: API 21+, batch jobs, replaced by WorkManager.

Code Example:

class SyncWorker(appContext: Context, params: WorkerParameters) : CoroutineWorker(appContext, params) {
    override suspend fun doWork(): Result {
        // sync logic
        return Result.success()
    }
}

Q47. How do you integrate Firebase in Android?

  • Auth: Email, Google Sign-In.
  • Firestore: Realtime DB with offline caching.
  • Crashlytics: Crash reporting.
  • Remote Config: Feature flags, A/B testing.

Code Example (Firestore):

val db = Firebase.firestore
db.collection("users").document("1").set(mapOf("name" to "Firoj"))

Follow-up:

  • Why Remote Config over hardcoding flags?
    → Allows feature rollout without app release.

Q48. How do you secure APIs in Android?

  • Always use HTTPS (TLS).
  • Use OAuth2/JWT for authentication.
  • Certificate pinning with OkHttp.
  • Do not store secrets in APK → use Keystore or server proxy.

Code Example:

val client = OkHttpClient.Builder()
    .certificatePinner(
        CertificatePinner.Builder()
            .add("api.example.com", "sha256/AbCdEfGh...")
            .build()
    )
    .build()

Q49. What is DataStore? Why use it over SharedPreferences?

  • SharedPreferences: Synchronous I/O → can block main thread.
  • DataStore: Built on coroutines & Flow.
    • Preferences DataStore: Key-value.
    • Proto DataStore: Strongly typed schema.

Code Example (Preferences):

val Context.dataStore by preferencesDataStore("settings")

val USERNAME = stringPreferencesKey("username")

suspend fun saveName(context: Context, name: String) {
    context.dataStore.edit { prefs -> prefs[USERNAME] = name }
}

fun readName(context: Context): Flow<String?> =
    context.dataStore.data.map { it[USERNAME] }

Q50. Explain Gradle Kotlin DSL and KSP.

  • Gradle Kotlin DSL: Use Kotlin (build.gradle.kts) instead of Groovy.
    → Safer, autocompletion, type-safe.
  • KSP (Kotlin Symbol Processing): Replaces KAPT (annotation processing).
    → Faster builds, supports libraries like Room, Moshi, Hilt.

Dependency Injection (DI)

  • Hilt / Dagger basics
  • Why DI, scopes, ViewModel injection
  • Alternatives (Koin, manual DI)

Room Database & Paging 3

  • Room basics (entities, DAO, migrations)
  • Paging 3 for large data lists
  • Flow integration

Navigation

  • Jetpack Navigation (Compose + XML)
  • Deep links
  • SafeArgs (XML) or rememberNavController() (Compose)

WorkManager & Background Tasks

  • Periodic vs One-time
  • Constraints (Wi-Fi, charging)
  • Chained workers

Testing & CI/CD

  • Unit testing (JUnit, Mockito, Kotest)
  • UI testing (Espresso, Compose Test)
  • CI/CD with GitHub Actions, Jenkins, Bitrise

Firebase / Analytics

  • Firebase Auth, Firestore basics
  • Crashlytics, Analytics integration
  • Remote Config for feature flags

Networking (Retrofit + OkHttp)

  • Interceptors (logging, auth)
  • Caching strategies
  • Error handling with sealed classes

Security

  • Jetpack Security (EncryptedSharedPreferences)
  • Keystore API
  • Obfuscation with ProGuard/R8

Modern Android Tools

  • KSP (Kotlin Symbol Processing)
  • Gradle Kotlin DSL
  • Jetpack DataStore (replacing SharedPreferences)

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top