Android Interview Questions
I recently lost my job after being laid off due to client budget reductions. It was a tough period, as I had to go through numerous interviews—some positive, some not. It took me more than six months to secure a new gig, so if you’re in a similar situation, don’t give up. Keep pushing forward.
Below, I’ve put together a guide with common Android Kotlin interview questions and answers. I hope you find it helpful!

Android
1. What are the Android Components?
- Services: This is a component that runs in the background performs long running operations. It does not provide a user interface.
- Activities: This is the entry point for interacting with the user. It represents a single screen with a user interface.
- Broadcast Receivers: It is a component that enables the app to listen and respond to broadcast messages from other apps or the system itself.
- Content Providers: It an Android component that manages access to shared app data and allows other apps to securely query, insert, update, or delete data using a standardized interface.
2. What is the lifecycle flow of an Activity from creation and what happens during each stage?
- OnCreate: This is the state where you can perform basic application startup logic that happens only once during the lifecycle of an activity e.g binding data to lists, associate the activity with a view model and instantiate some class variables.
- OnStart: This is the state where activity starts being visible to the user but it is not interactable. You can perform start animations here.
- OnResume: This is the state where the activity is on the foreground. Here you can register listeners or start any services that require activity to be on foreground.
- OnPause: This is the state where the activity is no longer the foreground activity. Its a good places to unregister listeners and save any data that needs to be saved and also stop any services that require the activity to be on the foreground in order to run.
- OnStop: This is the state where the activity is no longer visible to the user. It is a good place to stop all animations and other visual changes.
- OnDestroy: This is the state where the activity is completely destroyed. It is a good place to release all resources e.g any item that was tied to the activity context
- OnRestart: Can be called after OnStop was called if the activity is brought back to the foreground before it was destroyed.
3. What are the types of Services in Android?
- Foreground Service: Runs actively and shows a notification (e.g., a music player).
- Background Service: Runs without a UI and stops when completed (e.g., syncing data).
- Bound Service: Allows components (e.g., activities) to bind to it for interaction (e.g., client-server model).
4. How do you start a Service in Android?
A service can be started using:
- startService() → For Unbound Services
- bindService() → For Bound Services
val intent = Intent(this, MyService::class.java)
startService(intent)
5. How do you stop a Service in Android?
You can stop a service using:
- stopSelf() → The service stops itself.
- stopService() → Another component stops the service.
stopService(Intent(this, MyService::class.java))
6. What is the difference between startService() and bindService()?
Feature | startService() | bindService() |
---|---|---|
Type | Unbound Service | Bound Service |
Lifecycle | Runs until stopSelf() or stopService() is called | Stops when all clients unbind |
Interaction | No direct interaction with components | Allows communication (IPC) |
Example Use | Music Player, Background Sync | Messenger, Client-Server Apps |
7. What is the onStartCommand?
It is called when service is started by explicitly calling startService(). It decides what happens if the service is killed by the system using returning values.
Common return values:
START_STICKY
→ Restart service after being killed (without intent data).START_NOT_STICKY
→ Do not restart after being killed.START_REDELIVER_INTENT
→ Restart and redeliver the last intent.
8. What is the difference between a Service and a Thread
Feature | Service | Thread |
---|---|---|
Runs in | Main Thread (by default) | Background Thread |
Handles UI? | No | No |
Stops on App Close? | No | Yes |
Best for | Long-running background tasks | Short-lived background tasks |
A Service runs in the background but does NOT create a separate thread! Use a Thread or Coroutine inside a Service for heavy tasks.
9. How do you create a foreground service?
A Foreground Service runs with a visible notification and is used for ongoing tasks like music playback, location tracking, etc.
class MyForegroundService : Service() {
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
val notification = Notification.Builder(this, “channelId”)
.setContentTitle(“Service Running”)
.setSmallIcon(R.drawable.ic_notification)
.build()
startForeground(1, notification)
return START_STICKY
}
override fun onBind(intent: Intent?): IBinder? = null
}
10. What is the difference between a foreground and background service?
Feature | Foreground Service | Background Service |
---|---|---|
UI Visibility | Shows a notification | No notification |
System Priority | High (less likely to be killed) | Low (may be stopped by Android) |
Example | Music Player, Location Tracking | Syncing data, Background tasks |
11. What is an Intent service(Deprecated)?
It is a subclass of Service that provides a simplified way to handle asynchronous operations in the background. It handles each intent on a worker thread and stops itself automatically when the work is done. Its now recommended to use WorkManager instead.
class MyIntentService : IntentService(“MyIntentService”) {
override fun onHandleIntent(intent: Intent?) {
// Background task runs here
}
}
12. What is the best way to run background tasks in Android?
Task Type | Recommended Solution |
---|---|
Short background task | Kotlin Coroutines or Threads |
Long-running task | Foreground Service |
Periodic task (Sync, Upload) | WorkManager |
Exact time execution | AlarmManager |
Heavy background work (Download, Network) | WorkManager |
13. How does Android handle Service Kill and Restart?
If the system kills a service due to low memory, it may or may not restart based on the return value of onStartCommand()
Return Value | Restart Behavior |
---|
START_STICKY | Service restarts without intent data |
START_NOT_STICKY | Service is NOT restarted |
START_REDELIVER_INTENT | Service restarts with the last intent |
14. What permissions are required for Services?
Some services require permissions in AndroidManifest.xml
<uses-permission android:name=”android.permission.FOREGROUND_SERVICE”/>
15. What is WorkManager?
WorkManager is Google’s recommended API for background tasks that need to be executed reliably.
Feature | WorkManager | Service |
---|---|---|
Handles Restarts? | Yes, even after app kills/reboots | No, unless START_STICKY |
Best for | Background work (sync, uploads) | Foreground work (music, tracking) |
Battery Optimized? | Yes | No |
16. Can a Service run indefinitely in the background?
No, Android restricts background services for battery optimization.
- On Android Oreo (API 26+), background services are stopped after a few minutes.
- Use Foreground Service or WorkManager for long-running tasks.
17. What is the difference between onPause() and onStop()?
Feature | onPause() | onStop() |
---|---|---|
When Called? | When activity goes into the background but is still visible (e.g., dialog appears). | When activity is completely hidden. |
User Interaction? | No user interaction, but UI is still visible. | UI is completely hidden. |
Common Use? | Pause animations, save lightweight data. | Save state, release resources. |
18. How do you start an activity?
You can start an activity by using an intent.
val intent = Intent(this, SecondActivity::class.java)
intent.putExtra(“username”, “JohnDoe”)
startActivity(intent)
19. How d you stop an activity?
You can stop an activity by using finish()
20. What is onSaveInstanceState() and onRestoreInstanceState()?
These methods help save and restore activity state when it is destroyed and recreated (e.g., during a configuration change).
onSaveInstanceState(Bundle)
→ Saves UI state before activity gets destroyed. When the system stops, onSaveInstanceState is calledonRestoreInstanceState(Bundle)
→ Restores UI state when activity is recreated. You can check for saved instance in Oncreate method oronRestoreInstanceState which is called after OnStart
21. What is the difference between an Explicit and Implicit Intent?
Type | Description | Example |
---|
Explicit Intent | Used to start a specific activity within the same app. | Intent(this, SecondActivity::class.java) |
Implicit Intent | Used to request an action from another app (e.g., open a URL, share a file). | Intent(Intent.ACTION_VIEW, Uri.parse("https://www.google.com")) |
22. What are the different launch modes of an Activity?
Mode | Description |
---|
standard | Default mode. A new instance is created every time. |
singleTop | If the activity is already at the top of the stack, a new instance is not created. |
singleTask | A single instance is created and reused for all requests. |
singleInstance | Creates a separate task for the activity. |
<activity android:name=”.MainActivity” android:launchMode=”singleTask”/>
23. What is the difference between startActivity() and startActivityForResult()?
startActivity()
→ Used to start a new activity without expecting a result.startActivityForResult()
→ Used when the new activity should return some data to the previous activity.
Example:
val intent = Intent(this, SecondActivity::class.java)
startActivityForResult(intent, 100)
In SecondActivity, return the result:
val resultIntent = Intent()
resultIntent.putExtra("message", "Hello from SecondActivity")
setResult(Activity.RESULT_OK, resultIntent)
finish()
In MainActivity, handle the result:
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == 100 && resultCode == Activity.RESULT_OK) {
val message = data?.getStringExtra("message")
println("Received: $message")
}
}
24. How do you handle configuration changes in an Activity?
When the device rotates, the activity is destroyed and recreated.
Solutions to Handle Configuration Changes:
- Use onSaveInstanceState() and onRestoreInstanceState()
- Use ViewModel (Best Practice)
- Use android:configChanges in Manifest (Not Recommended)
25. What is an Activity Task and Back Stack?
Task – A collection of activities that work together
Back stack – Is the order is which activites are placed in memory
When an activity is started, it is pushed onto the back stack. Pressing Back removes it from the stack.
26. How do you finish an Activity and remove it from the Back Stack?
- Use
finish()
to remove the activity. - Use
FLAG_ACTIVITY_CLEAR_TOP
to remove all previous activities.
27. How do you handle Activity Transitions?
Use overridePendingTransition() to animate transitions between activities.
28. What is a fragment?
It is a modular, reusable UI component that represents a portion of an activity. Fragments have their own lifecycle, receive their own input events, and can be added or removed dynamically within an activity.
29. How is a Fragment different from an Activity?
Lifecycle | Independent | Dependent on an activity |
UI Representation | Represents a full screen | A portion of UI |
Navigation | Can exist alone | Needs to be hosted by an activity |
Multiple Instances | Only one can run at a time | Multiple fragments can exist in one activity |
30. How do you create a Fragment?
To create a Fragment, extend the Fragment
class and override its lifecycle methods.
class MyFragment : Fragment(R.layout.fragment_layout)
31. What are Fragment Lifecycle Methods?
onAttach() | Called when fragment is attached to the activity. |
onCreate() | Called when the fragment is created. |
onCreateView() | Called to create the fragment’s UI. |
onViewCreated() | Called after onCreateView() when view is created. |
onStart() | Fragment becomes visible. |
onResume() | Fragment becomes active and user can interact. |
onPause() | Called when fragment is partially visible. |
onStop() | Called when fragment is no longer visible. |
onDestroyView() | Cleanup of views (e.g., to prevent memory leaks). |
onDestroy() | Called when fragment is destroyed. |
onDetach() | Called when fragment is detached from activity. |
32. How do you pass data from an Activity to a Fragment?
Use arguments bundle to pass data to a fragment.
Example:
In Activity (Sending Data)
val fragment = MyFragment()
val bundle = Bundle()
bundle.putString(“message”, “Hello Fragment!”)
fragment.arguments = bundle
supportFragmentManager.beginTransaction()
.replace(R.id.fragment_container, fragment)
.commit()
In Fragment (Receiving Data)
val message = arguments?.getString(“message”)
33. How do you pass data from a Fragment to an Activity?
Use an interface callback to communicate from a fragment to an activity.
Step 1: Create an Interface in Fragment
interface FragmentListener {
fun onDataReceived(data: String)
}
class MyFragment : Fragment(R.layout.fragment_layout) {
var listener: FragmentListener? = null
override fun onAttach(context: Context) {
super.onAttach(context)
listener = context as? FragmentListener
}
fun sendDataToActivity() {
listener?.onDataReceived(“Hello from Fragment!”)
}
}
Step 2: Implement Interface in Activity
class MainActivity : AppCompatActivity(), FragmentListener {
override fun onDataReceived(data: String) {
Toast.makeText(this, data, Toast.LENGTH_SHORT).show()
}
}
34. How do you handle Fragment navigation?
Use the Navigation Component with NavController
.
findNavController().navigate(R.id.action_fragmentA_to_fragmentB)
35. What is the difference between replace() and add() in Fragments?
Method | Description |
---|
add() | Adds the new fragment on top of the existing one. |
replace() | Removes the current fragment before adding a new one. |
36. What is ViewModel
and why is it used with Fragments?
ViewModel
stores UI-related data and survives configuration changes.
To share data between Activity and Fragment:
37. What are the types of Broadcast Receivers?
Type | Description |
---|
Static Receiver | Declared in AndroidManifest.xml , listens for system or custom broadcasts even when the app is not running. |
Dynamic Receiver | Registered at runtime using registerReceiver() inside an activity or service. |
38. How do you send broadcasts?
Use sendBroadcast()
or sendOrderedBroadcast()
to send an intent.
val intent = Intent(“com.example.CUSTOM_BROADCAST”)
sendBroadcast(intent)
39. What is the difference between sendBroadcast()
, sendOrderedBroadcast()
, and LocalBroadcastManager
?
Method | Description |
---|
sendBroadcast() | Sends a broadcast to all registered receivers. |
sendOrderedBroadcast() | Sends a broadcast in a sequential order, allowing receivers to modify data. |
LocalBroadcastManager (deprecated) | Sends a broadcast within the app (improves security & performance). |
40. How do you unregister a dynamic Broadcast Receiver?
Use unregisterReceiver()
in onDestroy()
.
override fun onDestroy() {
super.onDestroy()
unregisterReceiver(myReceiver)
}
41. What is the different between normal broadcast and ordered broadcast?
Normal Broadcast | Sent to all receivers at the same time (no order). |
Ordered Broadcast | Delivered one by one, allowing receivers to modify or stop the broadcast. |
sendOrderedBroadcast(intent, null)
42. How do you receive system broadcasts like network changes or battery level?
Create your receiver class and declare the receiver under the manifest file with the intent filter that is required.
<receiver android:name=”.NetworkReceiver”>
<intent-filter>
<action android:name=”android.net.conn.CONNECTIVITY_CHANGE” />
</intent-filter>
</receiver>
class NetworkReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
Toast.makeText(context, “Network Changed!”, Toast.LENGTH_SHORT).show()
}
}
43. Can a Broadcast Receiver start an Activity or a Service?
Yes, but starting an activity from a broadcast is not recommended.
44. Why are implicit broadcasts restricted in Android 8.0 (Oreo) and later?
To improve performance and battery life, Android 8.0 restricts implicit broadcasts to prevent unnecessary wake-ups.
Solution:
Use JobScheduler or WorkManager for background tasks.
Use LocalBroadcastManager for app-specific broadcasts.
45. What is the Boot Completed broadcast and how is it used?
The BOOT_COMPLETED
broadcast is sent when the device finishes booting
<receiver android:name=”.BootReceiver” android:enabled=”true” android:exported=”false”>
<intent-filter>
<action android:name=”android.intent.action.BOOT_COMPLETED” />
</intent-filter>
</receiver>
BootReceiver Implementationclass BootReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
if (intent?.action == Intent.ACTION_BOOT_COMPLETED) {
Log.d(“BootReceiver”, “Device booted!”)
}
}
}
46.What are the best practices for using Broadcast Receivers?
✔ Use dynamic receivers instead of static when possible.
✔ Unregister dynamic receivers to prevent memory leaks.
✔ Use LocalBroadcastManager (deprecated) or LiveData for app-internal communication.
✔ Avoid long-running operations inside onReceive() (use WorkManager instead).
✔ Minimize implicit broadcasts to avoid unnecessary wake-ups.
47. What are some common system broadcasts in Android?
Action | Description |
---|---|
android.intent.action.BOOT_COMPLETED | Device has finished booting. |
android.net.conn.CONNECTIVITY_CHANGE | Network connectivity changed. |
android.intent.action.BATTERY_LOW | Battery is low. |
android.intent.action.AIRPLANE_MODE | Airplane mode changed. |
android.intent.action.PACKAGE_ADDED | A new app was installed. |
48. What is the alternative to Broadcast Receivers for background tasks?
Android provides alternatives for background processing:
WorkManager → For persistent background tasks.
JobScheduler → For scheduled tasks.
val workRequest = OneTimeWorkRequestBuilder<MyWorker>().build()
WorkManager.getInstance(context).enqueue(workRequest)
49. Why do we need a Content Provider?
- To share app data with other apps securely
- To provide a controlled access to app data
- To provide a standardized interface for data access
- To handle database transactions in a structured way
50. How do you create a Content Provider?
Extend the ContentProvider
class and override its methods:
class MyContentProvider : ContentProvider() {
override fun onCreate(): Boolean {
return true // Initialize database or other resources here
}
override fun query(
uri: Uri, projection: Array<String>?, selection: String?,
selectionArgs: Array<String>?, sortOrder: String?
): Cursor? {
// Query logic here
return null
}
override fun insert(uri: Uri, values: ContentValues?): Uri? {
// Insert logic here
return null
}
override fun update(uri: Uri, values: ContentValues?, selection: String?, selectionArgs: Array<String>?): Int {
// Update logic here
return 0
}
override fun delete(uri: Uri, selection: String?, selectionArgs: Array<String>?): Int {
// Delete logic here
return 0
}
override fun getType(uri: Uri): String? {
return “vnd.android.cursor.dir/vnd.example.items”
}
}
Declare it in the manifest
android:authorities
→ Defines a unique authority for the provider.android:exported="true"
→ Allows other apps to access this provider.
51. How do you access data from a Content Provider?
Use ContentResolver to interact with the provider.
val cursor = contentResolver.query(
ContactsContract.CommonDataKinds.Phone.CONTENT_URI, // Content Provider URI
arrayOf(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME), // Columns to retrieve
null, null, null
)
cursor?.use {
while (it.moveToNext()) {
val name = it.getString(0) // Fetch contact name
Log.d(“Contacts”, “Name: $name”)
}
}
52. What is the role of a URI in Content Providers?
It uniquely identifies data in a content provider
Example
content://com.example.provider/users/1
53. What is the role of ContentResolver?
It is a class that allows the app to communicate with the Content provider
54. What is MIME Type in Content Providers?
MIME type (getType()
) tells what kind of data is being provided.
MIME Type | Description |
---|---|
vnd.android.cursor.dir/vnd.example.items | Multiple records |
vnd.android.cursor.item/vnd.example.items | Single record |
55. What is android:grantUriPermissions
in a Content Provider?
It allows temporary access to data for another app.
56. What is the difference between exported="true"
and exported="false"
in Content Providers?
Setting | Description |
---|
exported="true" | Allows other apps to access the provider. |
exported="false" | Restricts access to the same app only. |
57. How do you secure a Content Provider?
- Set
android:exported="false"
to prevent external access. - Use permissions to restrict access:
58. What are some commonly used built-in Content Providers in Android?
Content Provider | Description |
---|---|
ContactsContract | Access contacts. |
MediaStore | Access media files (images, audio, videos). |
CalendarContract | Access calendar events. |
CallLog.Calls | Access call history. |
Telephony.SMS | Access SMS messages. |
59. What is an Intent in Android?
An intent is a messaging object used to request an action from an Android Component
60. What are the types of intents?
- Explicit intent – requests an action between android components within an app e.g Activity, Service
Example:Intent(this, SecondActivity::class.java)
- Implicit intent – an action that can invoke any app in the device
Intent(Intent.ACTION_VIEW, Uri.parse(“http://example.com”))
61. What is the difference between putExtra and getExtra in intents
putExtra – adds data to an intent that can be passed to a target component
getExtra – retrieves data from an intent in the target component
62. What is a PendingIntent
in Android?
It is a token that you given to another application to perform a future operation even if your app is not running. It is often used with Notifications, Alarms or Broadcasts
63. What are the possible flags used with Intents?
Flags are used to control how an activity is launched and handled. Some common flags include:
Intent.FLAG_ACTIVITY_NEW_TASK
: Starts a new task.Intent.FLAG_ACTIVITY_CLEAR_TOP
: If the target activity exists in the stack, all activities above it will be removed.Intent.FLAG_ACTIVITY_SINGLE_TOP
: If the target activity is already at the top of the stack, it will not be recreated.Intent.FLAG_ACTIVITY_NO_HISTORY
: The activity will not be kept in the history stack.Intent.FLAG_GRANT_READ_URI_PERMISSION
: Grant URI read permissions.
64. How do you send an implicit Intent?
An implicit Intent does not specify the component but rather an action or data type then the system will choose an appropriate activity (such as a web browser) to handle the action.. You can send an implicit intent like this:
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(“http://example.com”))
startActivity(intent)
65. What is a Bundle?
It is container used to pass data between activities or components
66. How do you start an activity without adding it to the back stack?
You can use the Intent.FLAG_ACTIVITY_NO_HISTORY
flag to prevent the activity from being added to the back stack.
67. What is ANR in Android?
This is a situation where the app becomes unresponsive for more than 5 seconds or 10 seconds when using broadcast receiver.
68. What is the cause for ANR?
ANRs occur when the main UI thread is blocked for too long. Some common causes include:
- Long-running operations on the main thread, such as:
Network calls
Database queries
File I/O operations - Heavy computations on the UI thread
- Slow or infinite loops in the main thread
- Deadlocks in multithreading
- Too many broadcast receivers running simultaneously
- Slow rendering due to excessive work in
onDraw()
oronMeasure()
69. How to avoid ANR?
- Avoid long running tasks on main thread
- Move long running tasks to WorkManager
- Optimize ui rendering
70. How to debug ANR?
- Check log cats filter by ANR
- Use Android Profiler: CPU for slow functions, Memory for memory leaks
- Check UI thread performance with Choreographer
Choreographer.getInstance().postFrameCallback {
val frameTime = System.nanoTime()
Log.d(“FrameTime”, “Frame rendered in $frameTime ns”)
}
71. Can an ANR occur on the main thread?
No, ANRs only occur when main thread is blocked
72. How does WorkManager help prevent ANR?
WorkManager is designed for running background tasks asynchronously without blocking the UI. It ensures tasks execute in the background, preventing long operations from freezing the UI.
73. What is a Custom View?
A custom view is a user defined UI component that extends the View class.
74. What is lifecycle of a Custom View?
The lifecycle of a custom View in Android follows a sequence of method calls from creation to destruction. Below is the typical lifecycle of a custom View:
- Constructor (Initialization)
Called when the View is created. This is where attributes, styles, and initial setup happen.
Example:class CustomView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
init {
// Initialization code
}
} - onAttachedToWindow()
Called when the View is attached to a window.
Useful for registering listeners or resources. - onMeasure(int widthMeasureSpec, int heightMeasureSpec)
Determines the View’s dimensions.
Needs to call setMeasuredDimension(width, height).Example:
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
val width = MeasureSpec.getSize(widthMeasureSpec)
val height = MeasureSpec.getSize(heightMeasureSpec)
setMeasuredDimension(width, height)
} - onSizeChanged(int w, int h, int oldw, int oldh)
Called when the View’s size changes. - onLayout(boolean changed, int left, int top, int right, int bottom) (For ViewGroups only)
Used to position child Views in a custom ViewGroup. - onDraw(Canvas canvas)
Draws the View’s content on the screen.
Example:
kotlin
Kopiuj
Edytuj
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
canvas.drawCircle(width / 2f, height / 2f, 50f, Paint().apply { color = Color.RED })
}
7. onTouchEvent(MotionEvent event)
Handles user interactions like touch events.Example:
override fun onTouchEvent(event: MotionEvent): Boolean {
if (event.action == MotionEvent.ACTION_DOWN) {
// Handle touch
return true
}
return super.onTouchEvent(event)
} - onDetachedFromWindow()
Called when the View is removed from the window.
Used for cleanup like unregistering listeners. - Garbage Collection
When no longer referenced, the View is garbage collected.
75. What is Jetpack Compose?
It is a modern fully declarative UI toolkit for Android that simplifies writing UI using Kotlin functions
76. How is Jetpack Compose UI different from XML?
It uses a declarative approach to writing UI where kotlin functions are used, better state management and less boilerplate code
77. What is a composable?
It is a function that is used to describes UI in compose. It is annotated with @Composable.
78. What is remember in Jetpack Compose?
The remember function is used to retain state across recompositions. It helps to avoid unnecessary recomputations of values.
79. What is rememberSaveable in Jetpack Compose?
The rememberSaveable function is used to retain state across activity creation even when activity is killed.
80. What is LaunchedEffect in Jetpack Compose?
LaunchedEffect is a side-effect function in Compose that launches a coroutine when a key changes. It is used for operations like fetching data from an API
81. How does Jetpack Compose handle recomposition?
Jetpack compose tracks state changes and re-executes the necessary composables, optimizing the UI updates.
82. What are Side Effects in Jetpack Compose?
Side effeects are operations that effect the external state e.g LaunchedEffect, DisposableEffect
83. How do you handle navigation in Jetpack Compose?
Navigation in Compose is managed using the Navigation component from androidx.navigation.compose. You create a NavHost and set start destination. Then add composable with route names.
Example:
NavHost(navController, startDestination = “home”) {
composable(“home”) { HomeScreen() }
composable(“details”) { DetailsScreen() }
}
84. What is a Modifier in Jetpack Compose?
A Modifier in Jetpack Compose is used to decorate or modify a Composable function’s behavior, appearance, and layout. It follows a chainable pattern, allowing multiple modifications.
85. What is CompositionLocal in Jetpack Compose?
CompositionLocal provides a way to pass data down the UI hierarchy without explicitly passing it as parameters. It’s useful for themes, user preferences, and dependency injection.
val LocalTextSize = compositionLocalOf { 16.sp }
@Composable
fun MyApp() {
CompositionLocalProvider(LocalTextSize provides 20.sp) {
Text(“Hello”, fontSize = LocalTextSize.current)
}
}
86. When should you use CompositionLocal over passing parameters?
Use CompositionLocal when:
The value applies to multiple components (e.g., theme, localization).
You want to avoid passing down the same parameter through multiple levels.
The value is contextual and may change dynamically.
Use normal function parameters when:
The value is specific to one Composable.
You need better readability and maintainability.
87. What is DisposableEffect in Jetpack Compose?
DisposableEffect is a side-effect function in Jetpack Compose that allows you to run an effect when a Composable enters the composition and dispose of it when it leaves.
The onDispose is triggered when the composable is removed from the composition.
@Composable
fun MyScreen() {
DisposableEffect(Unit) {
println(“Effect Started”)
onDispose {
println(“Effect Cleaned Up”)
}
}
}
88. How does DisposableEffect compare to LaunchedEffect?
Feature | LaunchedEffect | DisposableEffect |
---|---|---|
Purpose | Runs coroutines | Handles resource cleanup |
Scope | Uses CoroutineScope | Runs synchronously |
When it runs | When key changes | When Composable enters/exits |
Use LaunchedEffect
for coroutines (e.g., API calls).
Use DisposableEffect
for managing resources (e.g., registering listeners).
89. How do you test UI in Jetpack Compose?
Jetpack Compose provides ComposeTestRule
to test UI elements.
90. How do you test scrolling in Jetpack Compose?
Use .performScrollToNode() to scroll to an item.
91. How do you implement accessibility in Jetpack compose
Use semantics in the Modifier and you can define content description.
92. What is an Intent Filter?
It is declared in the manifest and specifies what type of intents an activity, service or broadcast receiver should respond to
93.What are the main components of an Intent Filter?
An Intent Filter consists of three main elements:
<action> – Defines the type of intent action, e.g., android.intent.action.VIEW, android.intent.action.SEND.
<category> – Specifies the category, e.g., android.intent.category.DEFAULT, android.intent.category.BROWSABLE.
<data> – Filters by data type such as URI scheme, MIME type, or host/path.
94.What is ViewModel in Android, and why is it used?
ViewModel
is a lifecycle-aware component that stores and manages UI-related data in a lifecycle-conscious way. It survives configuration changes such as screen rotations.
95.How does ViewModel survive configuration changes?
ViewModel
is retained in memory as long as the Activity
or Fragment
is within its lifecycle scope. Even if the activity is recreated (due to rotation), the ViewModel
is not destroyed unless the activity is completely removed from memory.
96.How do you share a ViewModel between Fragments?
Use activityViewModels() to share data across multiple fragments in the same activity.
97. What is LiveData, and how does it work?
LiveData is a lifecycle-aware observable data holder. It automatically updates UI components when data changes and respects lifecycle states.
98.How does LiveData respect the lifecycle of an Activity or Fragment?
LiveData only updates active observers—meaning it:
Updates UI only when the Activity/Fragment is in STARTED or RESUMED state.
Stops updating when UI is in the background (PAUSED/STOPPED).
This prevents memory leaks by automatically unsubscribing when UI is inactive.
99.What is MediatorLiveData and when should you use it?
MediatorLiveData combines multiple LiveData sources and reacts when any source updates.
100.What is LifecycleOwner in Android?
It is an interface for components like Activities, Fragments that have a lifecycle
101.What is LifecycleObserver in Android?
It is an interface that allows objects to observe lifecycle and react to lifecycle changes of LifecycleOwner
102.What is ViewModelProvider in Android?
ViewModelProvider is a factory class that creates and manages ViewModel instances in Android. It ensures that ViewModels persist across configuration changes like screen rotations.
103.What is ViewModelProvider.Factory, and when should you use it?
ViewModelProvider.Factory is used to create ViewModels with custom constructors (e.g., injecting dependencies).
Kotlin
103.Why does Google recommend StateFlow, SharedFlow over LiveData?
Better Coroutine Support
StateFlow works seamlessly with Kotlin Coroutines, making it ideal for modern asynchronous programming.
LiveData is tied to the main thread, while StateFlow can be collected on any thread.
More Predictable State Management
StateFlow always holds a value, ensuring UI state consistency.
LiveData can be null initially, leading to potential UI inconsistencies.
No Memory Leaks
LiveData requires a LifecycleOwner (e.g., an Activity/Fragment). If not properly removed, it can lead to memory leaks.
StateFlow has no lifecycle dependency, making it safer in ViewModel and repositories.
More Control Over Data Flow
StateFlow provides better state handling because it behaves like an observable state holder.LiveData re-emits the latest value when an observer is re-attached, which may cause unwanted UI updates
104.What is StateFlow and how is it different from Flow?
Feature | Flow | StateFlow |
---|
State Retention | No (doesn’t retain last value) | Yes (always holds a state) |
Hot/Cold | Cold (emits only when collected) | Hot (always active) |
Replays Last Value? | No | Yes |
Use Case | Streams of data (e.g., API calls) | UI state management |
105.What is SharedFlow and how is it different from StateFlow?
Feature | StateFlow | SharedFlow |
---|
State Retention | Yes (always has a value) | No |
Replays Values? | Only the latest state | Custom replay buffer |
Use Case | UI state management | One-time events (navigation, toasts) |
106. What is a Channel in Kotlin Coroutines? How does it differ from Flow?
Channel
is a coroutine-based messaging system that allows direct communication between coroutines. Unlike Flow
, a channel allows sending and receiving values asynchronously.
Feature | Flow | Channel |
---|---|---|
Data Emission | Sequential, one-by-one | Can send multiple values at the same time |
Hot/Cold | Cold | Hot |
Backpressure Handling | Buffers, suspends, or drops values | Uses buffering strategy |
Use Case | Streams (like API responses) | Two-way communication (producer-consumer pattern) |
107. When should you use Channels over SharedFlow? Use Channels when:
You need one-to-one communication (only one receiver at a time).
You want to send and receive values asynchronously in coroutines.
You need manual control over data flow (e.g., producer-consumer patterns).
Use SharedFlow when:
You need one-to-many communication (multiple collectors).
You want replay and buffering features for lost values.
You are dealing with UI events like navigation or toasts.
108. What are the different types of channels in Kotlin?
There are four main types of channels in Kotlin:
Channel Type | Behavior |
---|
Rendezvous Channel | No buffer; sender suspends until receiver is ready |
Buffered Channel | Has a buffer; sender only suspends when full |
Conflated Channel | Only latest value is kept; previous values are discarded |
Unlimited Channel | No limit on buffering |
109.What is the type inference in Kotlin?
This is a feature that allows the compiler to deduce/detect the data type of a variable without explicitly declaring the type
Example
var x = “you”
In this case the compiler, will automatically detect this is of String type without you necessarily declaring the type as below
Var x: String = “you”
110.What is the difference between val and var in Kotlin?
val: A read-only variable (cannot be reassigned after initialization), similar to final in Java.
var: A mutable variable (can be reassigned).
val x = 10 // Read-only
var y = 5 // Mutable
// x = 20 // Error: Cannot reassign a value to a ‘val’
y = 10 // Allowed
111.What is the purpose of the data keyword in Kotlin?
The data keyword is used to create a class that holds data. The compiler automatically generates useful methods like toString(), equals(), hashCode(), and copy()
data class Person(val name: String, val age: Int)
val person = Person(“Alice”, 25)
println(person) // Output: Person(name=Alice, age=25)
val person2 = person.copy(age = 30)
println(person2) // Output: Person(name=Alice, age=30)
112.What are extension functions in Kotlin?
Extension functions allow you to add new functionality to existing classes without modifying their code. They are defined outside the class but are called as if they were member functions.
fun String.addExclamation(): String {
return this + “!”
}
val greeting = “Hello”
println(greeting.addExclamation()) // Output: Hello!
113.What are smart casts in Kotlin?
Smart casts in Kotlin allow the compiler to intelligently cast an object to a specific type when specific conditions are met.
Example: If the null check passes then the object can be cast to String
fun printStringLengthSafely(text: String?) {
if (text != null) {
println(“Length of string: ${text.length}“) // Smart cast to String
} else {
println(“String is null”)
}
}
114.What is the difference between == and === in Kotlin?
==: Compares the values (equivalence).
===: Compares the references (whether they point to the same object in memory).
Example
fun main() {
val s1 = Student(“Ramesh”)
val s2 = Student(“Ramesh”)
println(s1==s2) //true
println(s1===s2) //false
}
𝐝𝐚𝐭𝐚 class Student(val name: String)
In above case, if we use a data class, then we are able to check the contents of the class otherwise it would be both false as in below.
fun main() {
val s1 = Student(“Ramesh”)
val s2 = Student(“Ramesh”)
println(s1) //Student@36baf30c
println(s2) //Student@7a81197d
}
class Student(name: String)
For primitive data types, === will converted to == which checks structural quality
val a = “Hello”
val b = “Hello”
println(a == b) // true (compares values)
println(a === b) // true (compares references)
val c = “World”
val d = “World”
println(c == d) // true (compares values)
println(c === d) // true (since Kotlin reuses String literals)
115. What is the purpose of companion object in Kotlin?
A companion object is a singleton object associated with a class. It can hold static-like members and allows you to call those members without creating an instance of the class.
116.What are sealed classes in Kotlin?
A sealed class is used to represent restricted class hierarchies. You can define a finite set of subclasses, making them useful for modeling restricted types like states, results, or responses.
117.What is the purpose of the lateinit keyword in Kotlin?
The lateinit keyword is used for non-nullable properties that will be initialized later before it is accessed.
118. What is the difference between List and MutableList in Kotlin?
List: An immutable list, meaning you can’t change the size or elements after it’s created.
MutableList: A mutable list, meaning you can modify the list by adding or removing elements.
119. What is the by keyword used for in Kotlin?
The by keyword is used in delegation. It allows an object to delegate the implementation of a method or property to another object.
Example: Property Delegation:
class MyClass {
var name: String by lazy { “Hello, Kotlin!” }
}
fun main() {
val obj = MyClass()
println(obj.name) // The value is initialized only when it’s accessed
}
Example: Delegation Pattern:
interface Printer {
fun printMessage(msg: String)
}
class ConsolePrinter : Printer {
override fun printMessage(msg: String) {
println(msg)
}
}
class PrinterDelegate(printer: Printer) : Printer by printer
val printer = PrinterDelegate(ConsolePrinter())
printer.printMessage(“Hello, Delegation!”) // Prints: Hello, Delegation!
120. What is a null safety operator in Kotlin?
Kotlin provides several null safety operators to deal with nullable types and avoid NullPointerException:
?. (Safe Call): Calls a method on a nullable object only if it’s not null.
?: (Elvis Operator): Provides a default value if the result is null.
!! (Not-null assertion): Asserts that an expression is non-null, otherwise throws an exception.
121.What are inline functions in Kotlin?
An inline function is a function whose body is directly inserted into the call site during compilation, which helps to avoid the overhead of function calls.
122.What are Coroutines?
Kotlin coroutines are a lightweight way to handle asynchronous programming and concurrent tasks. They allow for suspending and resuming computation without blocking threads, making it easier to work with background tasks (like network calls, database operations, etc.) in a non-blocking way.
123. What is the difference between launch and async in Kotlin Coroutines?
launch: Creates a coroutine that does not return a result (i.e., it returns a Job). It’s typically used for fire-and-forget tasks.
async: Creates a coroutine that returns a result wrapped in a Deferred object, which can be awaited. It’s used for tasks that return a result.
124. What is a suspend function in Kotlin?
A suspend function is a special function that can pause its execution (without blocking the thread) and resume later. It allows you to write asynchronous code in a synchronous style.
A function marked with the suspend modifier can be paused using the delay() function or similar operations, and can only be called from another coroutine or another suspend function.
125. What is runBlocking in Kotlin Coroutines?
runBlocking is a bridge between regular blocking code and coroutines. It is used to start a coroutine in a blocking manner (i.e., it will block the current thread until the coroutine completes). This is commonly used in the main function or in unit tests to start a coroutine and block the current thread.
126. What is the difference between delay() and Thread.sleep() in coroutines?delay()
: It is a suspending function that does not block the current thread. It only suspends the execution of the coroutine for the specified time and allows other coroutines to run in parallel.
: It is a blocking call that blocks the thread for the specified amount of time, meaning no other tasks can execute on that thread until the sleep is over.
Thread.sleep()
127. What is CoroutineScope?
It is used to defined the lifecycle of coroutines and manage coroutines. It controls when coroutines are cancelled or completed
128. What is GlobalScope and when should it be used?
GlobalScope is a global coroutine scope that is tied to the entire lifetime of the application. It’s often used for background tasks that are not tied to any specific lifecycle (like in Android, it is not tied to an activity or view model).
129. What is withContext in Kotlin Coroutines?
withContext is a suspending function that changes the context of a coroutine. It is used to switch between different dispatchers (e.g., background thread, main thread, etc.), and it waits for the result before continuing.
It’s typically used to switch to a different dispatcher when you need to run code on a different thread or context.
130. What is structured concurrency in Coroutine?
Structured concurrency refers to the idea that coroutines should be tied to a specific scope or lifecycle.This approach helps avoid dangling coroutines (coroutines that keep running even after the lifecycle is destroyed), which can cause memory leaks or unintended behavior.
131. How can you cancel a coroutine in Kotlin?
Coroutines can be canceled using the cancel() function, which can be applied to a Job or a CoroutineScope.
val job = GlobalScope.launch {
// Some long-running task
delay(1000)
println(“Task completed”)
}
job.cancel() // Cancels the coroutine
132. What is a Dispatcher in Coroutines?
Dispatcher is the mechanism that determines on which thread a coroutine will run
133. What are the types of Dispatchers in Coroutines?
- Dispatcher.IO: It is used for I/O bound tasks such as network operations
- Dispatcher.Main: It is used for UI related tasks. It ensures the coroutine runs on main thread
- Dispatcher.Default: It is optimized for CPU intensive tasks. It utilizes a limited a limit thread pool matching the number of CPU cores in your system
- Dispatcher.Unconfined: It runs coroutine on the caller thread until the first suspension point. After suspension, it resumes execution to the appropriate thread which may be different from the original thread
134. What is the difference between SupervisorScope and SupervisorJob in Kotlin Coroutines, and when should you use each?
Both SupervisorScope and SupervisorJob are used in Kotlin Coroutines to allow child coroutines to fail independently of each other. However, they have different use cases:
1. SupervisorScope
SupervisorScope creates a new coroutine scope where failures in one child coroutine do not cancel the other child coroutines.
It is commonly used when you want to ensure that one failed task does not cancel all other tasks running in the same scope.
Example using supervisorscope
suspend fun main() {
supervisorScope {
launch {
delay(500)
println(“Child 1 completes”)
}
launch {
delay(200)
throw RuntimeException(“Child 2 failed!”) // This failure does NOT affect Child 1
}
}
println(“SupervisorScope completed”)
}
Notice that even though Child 2 fails, Child 1 still completes execution.
2. SupervisorJob
SupervisorJob is a special type of Job that can be used in a CoroutineScope.
Unlike a regular Job, if a child coroutine fails, it does not cancel its siblings.
You manually pass a SupervisorJob when creating a coroutine scope.
Example
val scope = CoroutineScope(SupervisorJob())
scope.launch {
delay(500)
println(“Child 1 completes”)
}
scope.launch {
delay(200)
throw RuntimeException(“Child 2 failed!”) // This failure does NOT affect Child 1
}
Thread.sleep(1000) // Give coroutines time to execute
SupervisorJob allows one coroutine to fail without affecting the others in the same scope.
Feature | SupervisorScope | SupervisorJob |
---|---|---|
Type | A coroutine scope | A Job used inside a scope |
Usage | Used inside a coroutine (suspend function) | Used to create a CoroutineScope |
Failure Handling | Child failures do NOT cancel other coroutines | Child failures do NOT cancel siblings |
Scope Creation | Creates its own coroutine scope | Requires an explicit CoroutineScope(SupervisorJob()) |
135. Which thread does the coroutine in ViewModelScope run on?
By default, coroutines under ViewModelScope run on MainThread since the view model needs to update the UI.
However, if you need to perform long running background tasks such as network operations, you will need to use withContext(Dispatcher.IO).
136. How do you handle Exceptions in Coroutines
- You can use Try Catch methods inside launch and async
viewModelScope.launch {
try {
val data = withContext(Dispatchers.IO) {
apiService.getData() // API call
}
println(“Data fetched: $data”)
} catch (e: Exception) {
println(“Error: ${e.message}”) // Handle exception
}
} - You can use a CoroutineExceptionHandler
val exceptionHandler = CoroutineExceptionHandler { _, exception ->
println(“Caught exception: ${exception.message}”)
}viewModelScope.launch(exceptionHandler) {
throw RuntimeException(“Something went wrong!”) // This will be caught
}
137. What are Generics in Kotlin?
Generics allow to write flexible and reusable by defining type parameters. Instead of specifying a concrete type, generics enable working with any type while ensuring type safety at compile time.
Example Without Generics:
class StringBox(val value: String) // Works only for Strings
Example With Generics:
class Box<T>(val value: T) // Can store any type
138. What is the difference between in, out and invariant types in Generics?
Modifier | Keyword | Meaning | Example |
---|
Covariant | out | Can only produce (return ) values of type T | List<out T> |
Contravariant | in | Can only consume (accept ) values of type T | Comparator<in T> |
Invariant | No keyword | Can accept and return values | MutableList<T> |
Covariance (out
) – Read-Only
Allows subtypes to be assigned to supertypes (List<String>
is a List<Any>)
interface Producer<out T> { fun produce(): T }
class StringProducer : Producer<String> {
override fun produce(): String = “Hello”
}
val producer: Producer<Any> = StringProducer() // Allowed (out T)
Use out when you only return values (e.g., List<T>).
Contravariance (in) – Write-Only
Allows supertypes to be assigned to subtypes (Comparator<Any> is a Comparator<String>).
interface Consumer<in T> { fun consume(value: T) }
class StringConsumer : Consumer<Any> {
override fun consume(value: Any) {
println(“Consumed: $value”)
}
}
val consumer: Consumer<String> = StringConsumer() // Allowed (in T)
Use in
when you only consume values (e.g., Comparator<T>
).
Invariant (T
) – Both Read and Write
If neither in
nor out
is used, T behaves normally (both read and write).
class Box<T>(var value: T)
val intBox: Box<Int> = Box(10)
val anyBox: Box<Any> = intBox // ❌ Not allowed (invariant)
Use without in
or out
when modification (read/write) is required.
139. How do you define a generic function in Kotlin?
A generic function allows type flexibility while maintaining type safety.
140. What is reified type in Kotlin, and when should you use it?
In regular generics, type information is erased at runtime due to type erasure.
inline fun <T> printType() {
println(T::class) // ❌ ERROR: Cannot use T::class
}
Reified is used with inline functions
inline fun <reified T> printType() {
println(T::class)
}
printType<String>() // Prints: class kotlin.String
141. How do you restrict a generic type in Kotlin? (Type Bounds)
By default, generics work with any type, but you can restrict them using type bounds (T : SuperType).
Example: Restrict to Number Type
fun <T : Number> square(value: T): Double {
return value.toDouble() * value.toDouble()
}
println(square(5)) // Allowed (Int)
println(square(3.5)) // Allowed (Double)
// println(square(“Hello”)) // ❌ ERROR: String is not a Number
Use type bounds when you need specific properties or methods.
142. What is const val in Kotlin?
const val is used to declare compile time constants.
It is a faster, immutable and optimized way to define constants that cannot be changed during runtime.
Rules for const val:
Must be declared at the top-level or inside an object or companion object.
Can only hold primitive types (Int, Long, Double, String, etc.).
Evaluated at compile time (unlike val, which is assigned at runtime).
143. What are the different visibility modifiers in Kotlin?
Kotlin provides four visibility modifiers to control access to classes, functions, properties, and objects:
Modifier | Scope | Accessible From |
---|---|---|
public (default) | Everywhere | Anywhere in the project |
private | Same file/class | Only inside the same file or class |
protected | Same class & subclasses | Accessible in the same class and its subclasses |
internal | Same module | Accessible within the same module |
144. How do visibility modifiers work in data classes?
Primary constructor properties in data class are public by default.
You can restrict access using private or internal.
data class User(private val id: Int, val name: String)
fun main() {
val user = User(1, “Alice”)
println(user.name) // ✅ Allowed
// println(user.id) ❌ ERROR: id is private
}
145. What is the open keyword in Kotlin, and why is it needed?
By default, all classes, methods, and properties in Kotlin are final, meaning they cannot be inherited or overridden.
The open keyword allows a class, function, or property to be overridden in a subclass.
Example of open
for a class:
open class Animal { // Open class allows inheritance
open fun makeSound() {
println(“Animal makes a sound”)
}
}
class Dog : Animal() {
override fun makeSound() {
println(“Dog barks”)
}
}
fun main() {
val myDog = Dog()
myDog.makeSound() // Prints: Dog barks
}
146. What is a serializable and parcelable, and which one is better?
Serializable and Parcelables are frameworks that convert objects into a specific format in order for them to be transferred
Serializable uses reflection where an object is converted to a byte stream making it slower while a Parcelable it requires you to explicitly specify how to serialize (write) and deserialize (read) the object’s data using writeToParcel() and createFromParcel(). This direct approach is much faster since it avoids runtime introspection and instead follows the logic you define.
147. What are SharedPreferences in Android, and how are they used?
SharedPreferences in Android is a simple way to store and retrieve small pieces of data (such as key-value pairs) that need to persist across app sessions. It’s typically used for storing primitive data types like String
, Int
, Boolean
, Float
, and Long
, and is ideal for lightweight data storage.
148. What is Room Database in Android?
Room is an Object Relational Mapping (ORM) library that provides a higher-level abstraction over SQLite in Android. It allows developers to interact with an SQLite database using objects instead of raw SQL queries, making database operations easier, safer, and less error-prone.
149. What is Migration in Room, and why is it important?
A migration in Room is a way to handle schema changes in your database (like adding or removing tables or columns) between versions of your app.
When you increment the version of your database (in the @Database annotation), Room needs to know how to handle the transition from one schema version to another. If you don’t define a migration strategy, the database will be recreated, losing any data in the process.
Examples of Migration
val MIGRATION_1_2 = object : Migration(1, 2) {
override fun migrate(database: SupportSQLiteDatabase) {
// SQL to migrate from version 1 to version 2
database.execSQL(“ALTER TABLE users ADD COLUMN email TEXT”)
}
}
- Migration Object: Defines the changes required to migrate the database from version 1 to version 2 (e.g., adding a new column).
- Database Builder: When creating the database, you must provide the migration object if your app version changes.
150. What is Scoped Storage in Android, and why was it introduced?
Scoped Storage is a storage model introduced in Android 10 (API level 29), designed to improve user privacy and data security by limiting how apps can access the device’s storage. It restricts apps from having unrestricted access to the entire file system, particularly external storage.
151. What is Scoped Storage in Android, and why was it introduced?
Scoped Storage is a storage model introduced in Android 10 (API level 29), designed to improve user privacy and data security by limiting how apps can access the device’s storage. It restricts apps from having unrestricted access to the entire file system, particularly external storage.
Architecture
152. What is MVVM?
MVVM architecture stands for Model View ViewModel
- Model: This contains the data and business logic and is responsible for fetching, storing an managing data
- View: This is UI related e.g Activities Fragments, which is responsible for interacting with the user
- ViewModel: This holds data related to the UI. It communicates with the model to fetch the data and updates the View.
153. What are Design Patterns, and why are they important in Android development?
Design Patterns are general, reusable solutions to common problems that occur in software design. They represent best practices and proven strategies that help developers write efficient, maintainable, and scalable code
154. What is the Singleton Pattern in Android, and when should you use it?
The Singleton Pattern ensures that a class has only one instance and provides a global point of access to that instance. In Android development, it’s commonly used for managing instances like network clients, database connections, or shared preferences that should not have multiple instances across the app.
155. What is the Observer Pattern in Android
The Observer Pattern is used to allow a subject to notify its observers when its state changes, without needing to explicitly reference them. This pattern is useful when multiple components need to react to changes in the state of a specific object, without tight coupling between them.
Example: LiveData and StateFlow are built on observer pattern.
The UI components observe data in view model an update accordingly
156. What is the Factory Pattern in Android?
The Factory Pattern provides an interface for creating objects, but it allows subclasses or concrete classes to alter the type of objects that will be created. It abstracts the instantiation of objects, making it easy to change the object creation logic without modifying the rest of the code
You can use this approach when the creation logic of an object is complex or depends on certain conditions.
// Abstract class defining the product
interface Notification {
fun sendMessage(message: String)
}
// Concrete products
class EmailNotification : Notification {
override fun sendMessage(message: String) {
println(“Sending email: $message”)
}
}
class SmsNotification : Notification {
override fun sendMessage(message: String) {
println(“Sending SMS: $message”)
}
}
// Factory
object NotificationFactory {
fun createNotification(type: String): Notification {
return when (type) {
“email” -> EmailNotification()
“sms” -> SmsNotification()
else -> throw IllegalArgumentException(“Unknown notification type”)
}
}
}
// Usage
val notification = NotificationFactory.createNotification(“email”)
notification.sendMessage(“Hello!”)
157. What is the Decorator Pattern, and how can it be applied in Android?
The Decorator Pattern allows behavior to be added to an individual object, dynamically, without affecting the behavior of other objects from the same class. It is typically used to add new functionality to objects at runtime.
Common Use in Android:
Custom views where you need to add functionality (e.g., adding a border, shadow, or animation) to an existing view.
Wrapping a network or database service with additional functionality like logging or caching.
// Component interface
interface Notification {
fun sendMessage(message: String)
}
// Concrete component
class BasicNotification : Notification {
override fun sendMessage(message: String) {
println(“Sending message: $message”)
}
}
// Decorator class
class NotificationWithLogging(private val decorated: Notification) : Notification {
override fun sendMessage(message: String) {
println(“Logging: $message”) // Added behavior
decorated.sendMessage(message)
}
}
// Usage
val notification = NotificationWithLogging(BasicNotification())
notification.sendMessage(“Hello World!”)
158.
What is the Command Pattern in Android, and how is it typically used?
The Command Pattern encapsulates a request as an object, thereby allowing for parameterization of clients with different requests, queuing of requests, and logging of the request. It also allows the possibility of undoable operations.
Example
// Command interface
interface Command {
fun execute()
}
// Concrete Command
class LightOnCommand(private val light: Light) : Command {
override fun execute() {
light.turnOn()
}
}
class LightOffCommand(private val light: Light) : Command {
override fun execute() {
light.turnOff()
}
}
// Receiver
class Light {
fun turnOn() {
println(“Light is ON”)
}
fun turnOff() {
println(“Light is OFF”)
}
}
// Invoker
class RemoteControl {
private var command: Command? = null
fun setCommand(command: Command) {
this.command = command
}
fun pressButton() {
command?.execute()
}
}
// Usage
val light = Light()
val lightOn = LightOnCommand(light)
val lightOff = LightOffCommand(light)
val remote = RemoteControl()
remote.setCommand(lightOn)
remote.pressButton() // Light is ON
remote.setCommand(lightOff)
remote.pressButton() // Light is OFF
159. What is the Adapter Pattern, and how can it be used in Android?
The Adapter Pattern allows incompatible interfaces to work together. It acts as a bridge, converting one interface to another, enabling classes to work together that otherwise couldn’t because of incompatible interfaces.
Common Use in Android:
Adapters are commonly used with RecyclerViews, where the Adapter converts data items into views (ViewHolders).
It can also be used to adapt different third-party libraries or legacy code into your app’s architecture.
160. What is Clean architecture?
It is an approach to structuring your code base to ensure separation of concerns and independence of components.
It emphasizes a layered structure where each layer has a specific responsibility, and dependencies should flow inwards (i.e., outer layers can depend on inner layers, but not vice versa).
Clean Architecture is intended to make code testable, scalable, and maintainable by isolating business logic from UI and framework-related code.
Layers in Clean Architecture:
- Entities: Core business models and logic, independent of the application.
- Use Cases / Interactors: Application-specific business rules or logic.
- Interface Adapters: Maps data between different layers, e.g., converting data from use cases to something that can be displayed in the UI.
- Frameworks & Drivers: Outer layers such as UI (Android Views, Fragments, etc.), networking, database, etc.
161. How does MVVM work with Clean Architecture?
Clean Architecture offers a more structured approach to building large and complex applications, while MVVM is more suited for handling UI-related logic in simpler apps with reactive data updates.