November 22, 2025
13 min read

Junior Android Developer Interview Questions and Answers

interview
career-advice
job-search
entry-level
Junior Android Developer Interview Questions and Answers
Milad Bonakdar

Milad Bonakdar

Author

Prepare for a junior Android developer interview with focused Kotlin, lifecycle, Jetpack Compose, Room, networking, and coroutine questions.


Introduction

For a junior Android developer interview, expect questions that test whether you can explain Kotlin basics, Android component lifecycles, Compose state, local storage, networking, and coroutines in practical terms. Strong answers connect each concept to how you would build a small, reliable app: keep UI state in the right place, avoid blocking the main thread, persist data safely, and handle errors clearly.

Use this guide to rehearse concise answers, then pair each topic with a small project example from your resume or portfolio. If you built a task app, weather app, or API-backed screen, map the answers below to that work so your interview sounds specific rather than memorized.


Kotlin Fundamentals (6 Questions)

1. What is the difference between val and var in Kotlin?

Answer:

  • val: Declares an immutable (read-only) variable. Once assigned, its value cannot be changed.
  • var: Declares a mutable variable. Its value can be changed after initialization.
  • Best Practice: Use val by default for safety. Only use var when you need to reassign the value.
val name = "John"  // Cannot be changed
var age = 25       // Can be changed
age = 26           // Valid
// name = "Jane"   // Error: Val cannot be reassigned

Rarity: Very Common Difficulty: Easy


2. Explain Nullable types and the Safe Call operator in Kotlin.

Answer: Kotlin's type system distinguishes between nullable and non-nullable types to prevent null pointer exceptions.

  • Nullable Type: Add ? after the type: String?
  • Safe Call (?.): Calls a method only if the object is not null
  • Elvis Operator (?:): Provides a default value if null
  • Not-null Assertion (!!): Throws exception if null (use sparingly)
var email: String? = "[email protected]"

// Safe call
val length = email?.length  // Returns null if email is null

// Elvis operator
val displayEmail = email ?: "No email"

// Let function
email?.let {
    println("Email: $it")
}

// Not-null assertion (dangerous)
val len = email!!.length  // Throws exception if email is null

Rarity: Very Common Difficulty: Easy


3. What is the difference between a class and a data class in Kotlin?

Answer:

  • Regular Class: Standard class definition
  • Data Class: Automatically generates useful methods for holding data
    • equals() and hashCode()
    • toString()
    • copy() function
    • componentN() functions for destructuring
// Regular class
class Person(val name: String, val age: Int)

// Data class
data class User(val name: String, val age: Int)

val user1 = User("John", 25)
val user2 = User("John", 25)

println(user1 == user2)  // true (equals() generated)
println(user1)           // User(name=John, age=25) (toString() generated)

// Copy with modifications
val user3 = user1.copy(age = 26)

// Destructuring
val (name, age) = user1

Rarity: Very Common Difficulty: Easy


4. What are Lambda expressions and Higher-order functions in Kotlin?

Answer:

  • Lambda: Anonymous function that can be passed as a value
  • Higher-order Function: Function that takes functions as parameters or returns a function
// Lambda expression
val sum = { a: Int, b: Int -> a + b }
println(sum(5, 3))  // 8

// Higher-order function
fun calculate(a: Int, b: Int, operation: (Int, Int) -> Int): Int {
    return operation(a, b)
}

val result = calculate(10, 5) { x, y -> x * y }
println(result)  // 50

// Collection operations
val numbers = listOf(1, 2, 3, 4, 5)
val doubled = numbers.map { it * 2 }
val evens = numbers.filter { it % 2 == 0 }

println(doubled)  // [2, 4, 6, 8, 10]
println(evens)    // [2, 4]

Rarity: Very Common Difficulty: Medium


5. Explain Extension Functions in Kotlin.

Answer: Extension functions allow you to add new functions to existing classes without modifying their source code.

// Extension function for String
fun String.isPalindrome(): Boolean {
    return this == this.reversed()
}

println("radar".isPalindrome())  // true
println("hello".isPalindrome())  // false

// Extension function with parameters
fun Int.times(action: () -> Unit) {
    repeat(this) {
        action()
    }
}

3.times {
    println("Hello")
}
// Prints "Hello" three times

Rarity: Common Difficulty: Easy


6. What is the difference between == and === in Kotlin?

Answer:

  • ==: Structural equality (compares values using equals())
  • ===: Referential equality (compares memory references)
val str1 = "Hello"
val str2 = "Hello"
val str3 = str1

println(str1 == str2)   // true (same value)
println(str1 === str2)  // true (string pool optimization)
println(str1 === str3)  // true (same reference)

val list1 = listOf(1, 2, 3)
val list2 = listOf(1, 2, 3)

println(list1 == list2)   // true (same content)
println(list1 === list2)  // false (different objects)

Rarity: Common Difficulty: Easy


Android Components (5 Questions)

7. What is an Activity and explain its lifecycle.

Answer: An Activity represents a single screen with a user interface. It has a well-defined lifecycle:

Loading diagram...
  • onCreate(): Activity is created. Initialize UI, set content view.
  • onStart(): Activity becomes visible.
  • onResume(): Activity is in foreground and interactive.
  • onPause(): Activity losing focus (another activity coming to foreground).
  • onStop(): Activity no longer visible.
  • onDestroy(): Activity is destroyed.
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        // Initialize views, setup data
    }
    
    override fun onStart() {
        super.onStart()
        // Activity becoming visible
    }
    
    override fun onResume() {
        super.onResume()
        // Activity is interactive, start animations
    }
    
    override fun onPause() {
        super.onPause()
        // Activity losing focus, pause ongoing actions
    }
}

Rarity: Very Common Difficulty: Easy


8. What is the difference between an Activity and a Fragment?

Answer:

  • Activity: Represents a full screen. Entry point for user interaction. Has its own lifecycle.
  • Fragment: Reusable portion of UI within an Activity. Multiple fragments can exist in one activity. Has its own lifecycle tied to the host activity.
  • Benefits of Fragments:
    • Reusability across activities
    • Modular UI components
    • Support for tablets (multi-pane layouts)
    • Navigation component integration
class MyFragment : Fragment() {
    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.fragment_my, container, false)
    }
    
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        // Initialize views
    }
}

// In Activity
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        
        supportFragmentManager.beginTransaction()
            .replace(R.id.fragment_container, MyFragment())
            .commit()
    }
}

Rarity: Very Common Difficulty: Easy


9. What is an Intent and what are its types?

Answer: Intent is a messaging object used to request an action from another app component.

  • Explicit Intent: Specifies the exact component to start (by class name)
  • Implicit Intent: Declares a general action, and the system finds the appropriate component
// Explicit Intent - start specific activity
val intent = Intent(this, DetailActivity::class.java)
intent.putExtra("USER_ID", 123)
startActivity(intent)

// Implicit Intent - open URL
val webIntent = Intent(Intent.ACTION_VIEW, Uri.parse("https://example.com"))
startActivity(webIntent)

// Implicit Intent - share text
val shareIntent = Intent(Intent.ACTION_SEND).apply {
    type = "text/plain"
    putExtra(Intent.EXTRA_TEXT, "Check this out!")
}
startActivity(Intent.createChooser(shareIntent, "Share via"))

// Implicit Intent - make phone call
val callIntent = Intent(Intent.ACTION_DIAL).apply {
    data = Uri.parse("tel:1234567890")
}
startActivity(callIntent)

Rarity: Very Common Difficulty: Easy


10. What is a Service in Android?

Answer: A Service is a component that runs in the background to perform long-running operations without a user interface.

  • Types:
    • Foreground Service: Performs noticeable operations (music player). Shows notification.
    • Background Service: Performs operations not directly noticed by user.
    • Bound Service: Allows components to bind to it and interact.
class MyService : Service() {
    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        // Perform background work
        Thread {
            // Long-running operation
            Thread.sleep(5000)
            stopSelf()
        }.start()
        
        return START_STICKY
    }
    
    override fun onBind(intent: Intent?): IBinder? {
        return null
    }
}

// Start service
val serviceIntent = Intent(this, MyService::class.java)
startService(serviceIntent)

// Stop service
stopService(serviceIntent)

Rarity: Common Difficulty: Medium


11. What is a BroadcastReceiver?

Answer: BroadcastReceiver is a component that responds to system-wide broadcast announcements.

  • Use Cases: Battery low, network connectivity changes, SMS received, boot completed
  • Registration:
    • Static: In AndroidManifest.xml (limited in newer Android versions)
    • Dynamic: In code (preferred)
class MyBroadcastReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context?, intent: Intent?) {
        when (intent?.action) {
            Intent.ACTION_BATTERY_LOW -> {
                // Handle low battery
            }
            ConnectivityManager.CONNECTIVITY_ACTION -> {
                // Handle network change
            }
        }
    }
}

// Register dynamically
class MainActivity : AppCompatActivity() {
    private val receiver = MyBroadcastReceiver()
    
    override fun onStart() {
        super.onStart()
        val filter = IntentFilter(Intent.ACTION_BATTERY_LOW)
        registerReceiver(receiver, filter)
    }
    
    override fun onStop() {
        super.onStop()
        unregisterReceiver(receiver)
    }
}

Rarity: Common Difficulty: Easy


UI Development (4 Questions)

12. What is the difference between LinearLayout, RelativeLayout, and ConstraintLayout?

Answer:

  • LinearLayout: Arranges children in a single row or column. Simple but can lead to nested layouts.
  • RelativeLayout: Positions children relative to each other or parent. More flexible but complex.
  • ConstraintLayout: Modern, flexible layout. Flat view hierarchy. Recommended for complex UIs.
<!-- ConstraintLayout example -->
<androidx.constraintlayout.widget.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    
    <TextView
        android:id="@+id/title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Title"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent" />
    
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Click Me"
        app:layout_constraintTop_toBottomOf="@id/title"
        app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

Rarity: Very Common Difficulty: Easy


13. What is RecyclerView and how does it work?

Answer: RecyclerView is an efficient widget for displaying large lists by recycling views.

  • Components:
    • Adapter: Binds data to views
    • ViewHolder: Holds references to views (avoids findViewById calls)
    • LayoutManager: Positions items (Linear, Grid, Staggered)
// Data class
data class User(val name: String, val email: String)

// ViewHolder
class UserViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
    private val nameText: TextView = itemView.findViewById(R.id.nameText)
    private val emailText: TextView = itemView.findViewById(R.id.emailText)
    
    fun bind(user: User) {
        nameText.text = user.name
        emailText.text = user.email
    }
}

// Adapter
class UserAdapter(private val users: List<User>) : 
    RecyclerView.Adapter<UserViewHolder>() {
    
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UserViewHolder {
        val view = LayoutInflater.from(parent.context)
            .inflate(R.layout.item_user, parent, false)
        return UserViewHolder(view)
    }
    
    override fun onBindViewHolder(holder: UserViewHolder, position: Int) {
        holder.bind(users[position])
    }
    
    override fun getItemCount() = users.size
}

// In Activity
recyclerView.layoutManager = LinearLayoutManager(this)
recyclerView.adapter = UserAdapter(userList)

Rarity: Very Common Difficulty: Medium


14. What is Jetpack Compose?

Answer: Jetpack Compose is Android's modern declarative UI toolkit, built for Kotlin. Instead of updating views manually, you describe the UI for the current state and Compose redraws the affected parts when state changes.

  • Composable functions: Kotlin functions annotated with @Composable that describe UI.
  • State: Values such as text input, loading state, or selected items that drive what appears on screen.
  • State hoisting: Move state up to a parent or ViewModel when multiple composables need it.
  • Interview cue: Mention remember for local UI state, rememberSaveable for simple state that should survive configuration changes, and ViewModel for screen-level state.
@Composable
fun Greeting(name: String) {
    Text(text = "Hello, $name!")
}

@Composable
fun Counter() {
    var count by remember { mutableStateOf(0) }
    
    Column(
        modifier = Modifier.padding(16.dp),
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Text(text = "Count: $count")
        Button(onClick = { count++ }) {
            Text("Increment")
        }
    }
}

@Preview
@Composable
fun PreviewCounter() {
    Counter()
}

Rarity: Very Common Difficulty: Easy


15. What is the difference between match_parent and wrap_content?

Answer: These are layout parameters that define view dimensions:

  • match_parent: View expands to fill the parent's size
  • wrap_content: View sizes itself to fit its content
  • Fixed Size: Specific dp value (e.g., 100dp)
<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    
    <!-- Fills parent width, wraps content height -->
    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Title" />
    
    <!-- Wraps both dimensions -->
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Click Me" />
    
    <!-- Fixed size -->
    <ImageView
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:src="@drawable/icon" />
</LinearLayout>

Rarity: Very Common Difficulty: Easy


Data & Networking (5 Questions)

16. How do you make a network request in Android?

Answer: In a modern Android app, network calls usually live behind a repository and run from a ViewModel or use case with coroutines. Retrofit or OkHttp are common choices. A junior-level answer should also mention loading, success, empty, and error states so the UI does not freeze or fail silently.

// Retrofit setup
interface ApiService {
    @GET("users/{id}")
    suspend fun getUser(@Path("id") userId: Int): User
    
    @POST("users")
    suspend fun createUser(@Body user: User): User
}

object RetrofitClient {
    private const val BASE_URL = "https://api.example.com/"
    
    val apiService: ApiService by lazy {
        Retrofit.Builder()
            .baseUrl(BASE_URL)
            .addConverterFactory(GsonConverterFactory.create())
            .build()
            .create(ApiService::class.java)
    }
}

// Usage in ViewModel
class UserViewModel : ViewModel() {
    fun fetchUser(userId: Int) {
        viewModelScope.launch {
            try {
                val user = RetrofitClient.apiService.getUser(userId)
                // Update UI state with success
            } catch (e: Exception) {
                // Update UI state with a useful error
            }
        }
    }
}

Rarity: Very Common Difficulty: Medium


17. What is Room and how do you use it?

Answer: Room is Android's persistence library that provides an abstraction layer over SQLite. It is useful when the app needs structured local data, offline support, caching, or relational queries. In an interview, make it clear that database work should not run on the main thread.

  • Components:
    • Entity: Represents a table
    • DAO (Data Access Object): Defines database operations
    • Database: Database holder
  • Practical example: Cache API results in Room, display cached data immediately, then refresh from the network when available.
// Entity
@Entity(tableName = "users")
data class User(
    @PrimaryKey(autoGenerate = true) val id: Int = 0,
    @ColumnInfo(name = "name") val name: String,
    @ColumnInfo(name = "email") val email: String
)

// DAO
@Dao
interface UserDao {
    @Query("SELECT * FROM users")
    suspend fun getAllUsers(): List<User>
    
    @Query("SELECT * FROM users WHERE id = :userId")
    fun getUserById(userId: Int): User?
    
    @Insert
    suspend fun insertUser(user: User)
    
    @Delete
    suspend fun deleteUser(user: User)
    
    @Update
    suspend fun updateUser(user: User)
}

// Database
@Database(entities = [User::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
    abstract fun userDao(): UserDao
    
    companion object {
        @Volatile
        private var INSTANCE: AppDatabase? = null
        
        fun getDatabase(context: Context): AppDatabase {
            return INSTANCE ?: synchronized(this) {
                val instance = Room.databaseBuilder(
                    context.applicationContext,
                    AppDatabase::class.java,
                    "app_database"
                ).build()
                INSTANCE = instance
                instance
            }
        }
    }
}

Rarity: Very Common Difficulty: Medium


18. What is SharedPreferences?

Answer: SharedPreferences stores small amounts of primitive data as key-value pairs.

  • Use Cases: User settings, preferences, simple flags
  • Not for: Large data, complex objects (use Room instead)
// Save data
val sharedPref = getSharedPreferences("MyPrefs", Context.MODE_PRIVATE)
with(sharedPref.edit()) {
    putString("username", "John")
    putInt("age", 25)
    putBoolean("isLoggedIn", true)
    apply()  // or commit()
}

// Read data
val username = sharedPref.getString("username", "Guest")
val age = sharedPref.getInt("age", 0)
val isLoggedIn = sharedPref.getBoolean("isLoggedIn", false)

// Remove data
with(sharedPref.edit()) {
    remove("username")
    apply()
}

// Clear all
with(sharedPref.edit()) {
    clear()
    apply()
}

Rarity: Very Common Difficulty: Easy


19. What is the difference between apply() and commit() in SharedPreferences?

Answer: Both save changes to SharedPreferences, but differ in behavior:

  • apply(): Asynchronous. Returns immediately. Changes written to disk in background. No return value.
  • commit(): Synchronous. Blocks until changes are written. Returns boolean (success/failure).
  • Best Practice: Use apply() unless you need the return value.
val editor = sharedPref.edit()

// apply() - asynchronous, preferred
editor.putString("key", "value")
editor.apply()  // Returns immediately

// commit() - synchronous
editor.putString("key", "value")
val success = editor.commit()  // Waits for write, returns boolean
if (success) {
    // Save successful
}

Rarity: Common Difficulty: Easy


20. What is Coroutines in Kotlin?

Answer: Coroutines let you write asynchronous code in a sequential style. In Android interviews, connect them to practical work: network calls, database operations, timers, and background tasks that should not block the main thread.

  • Benefits: Lightweight concurrency, structured cancellation, readable async code
  • Key Concepts:
    • suspend: Function that can be paused and resumed
    • launch: Starts a coroutine (fire and forget)
    • async: Starts a coroutine and returns a result
    • Dispatchers: Control which thread the coroutine runs on
    • viewModelScope: Cancels work automatically when the ViewModel is cleared
class MyViewModel : ViewModel() {
    fun loadData() {
        viewModelScope.launch {
            try {
                // Main thread
                showLoading()
                
                // Switch to IO thread for network call
                val data = withContext(Dispatchers.IO) {
                    fetchDataFromNetwork()
                }
                
                // Back to main thread
                updateUI(data)
            } catch (e: Exception) {
                handleError(e)
            }
        }
    }
    
    suspend fun fetchDataFromNetwork(): String {
        delay(2000)  // Simulates network delay
        return "Data from server"
    }
}

// Parallel execution
viewModelScope.launch {
    val deferred1 = async { fetchUser(1) }
    val deferred2 = async { fetchUser(2) }
    
    val user1 = deferred1.await()
    val user2 = deferred2.await()
}

Rarity: Very Common Difficulty: Medium


Newsletter subscription

Weekly career tips that actually work

Get the latest insights delivered straight to your inbox

Build a Resume That Gets You Hired 60% Faster

In minutes, create a tailored, ATS-friendly resume proven to land 6X more interviews.

Build a better resume

Share this post

Beat the 75% ATS Rejection Rate

3 out of 4 resumes never reach a human eye. Our keyword optimization increases your pass rate by up to 80%, ensuring recruiters actually see your potential.