diciembre 21, 2025
14 min de lectura

Preguntas de entrevista para desarrollador Android junior

interview
career-advice
job-search
entry-level
Preguntas de entrevista para desarrollador Android junior
Milad Bonakdar

Milad Bonakdar

Autor

Prepárate para una entrevista Android junior con preguntas sobre Kotlin, ciclo de vida, Jetpack Compose, Room, redes y corrutinas.


Introducción

En una entrevista para desarrollador Android junior, espera preguntas que comprueben si puedes explicar Kotlin, ciclos de vida de Android, estado en Compose, almacenamiento local, peticiones de red y corrutinas de forma práctica. Las mejores respuestas conectan cada concepto con una app pequeña y fiable: colocar bien el estado de la UI, no bloquear el hilo principal, guardar datos de forma segura y manejar errores con claridad.

Usa esta guía para practicar respuestas breves y relaciónalas con un proyecto de tu CV o portafolio. Si creaste una app de tareas, clima o una pantalla conectada a una API, usa ese ejemplo para que la entrevista suene concreta y no memorizada.


Fundamentos de Kotlin (6 preguntas)

1. ¿Cuál es la diferencia entre val y var en Kotlin?

Respuesta:

  • val: Declara una variable inmutable (de solo lectura). Una vez asignada, su valor no se puede cambiar.
  • var: Declara una variable mutable. Su valor se puede cambiar después de la inicialización.
  • Mejor práctica: Usa val por defecto por seguridad. Solo usa var cuando necesites reasignar el valor.
val name = "John"  // No se puede cambiar
var age = 25       // Se puede cambiar
age = 26           // Válido
// name = "Jane"   // Error: Val no se puede reasignar

Frecuencia: Muy común Dificultad: Fácil


2. Explica los tipos Nullable y el operador Safe Call en Kotlin.

Respuesta: El sistema de tipos de Kotlin distingue entre tipos nullable y no nullable para prevenir excepciones de puntero nulo.

  • Tipo Nullable: Agrega ? después del tipo: String?
  • Safe Call (?.): Llama a un método solo si el objeto no es nulo
  • Operador Elvis (?:): Proporciona un valor predeterminado si es nulo
  • Not-null Assertion (!!): Lanza una excepción si es nulo (usar con moderación)
var email: String? = "[email protected]"

// Safe call
val length = email?.length  // Devuelve null si email es nulo

// Operador Elvis
val displayEmail = email ?: "Sin correo electrónico"

// Función Let
email?.let {
    println("Correo electrónico: $it")
}

// Not-null assertion (peligroso)
val len = email!!.length  // Lanza una excepción si email es nulo

Frecuencia: Muy común Dificultad: Fácil


3. ¿Cuál es la diferencia entre una class y una data class en Kotlin?

Respuesta:

  • Clase Regular: Definición de clase estándar
  • Data Class: Genera automáticamente métodos útiles para contener datos
    • equals() y hashCode()
    • toString()
    • función copy()
    • funciones componentN() para la desestructuración
// Clase regular
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() generado)
println(user1)           // User(name=John, age=25) (toString() generado)

// Copiar con modificaciones
val user3 = user1.copy(age = 26)

// Desestructuración
val (name, age) = user1

Frecuencia: Muy común Dificultad: Fácil


4. ¿Qué son las expresiones Lambda y las funciones de orden superior en Kotlin?

Respuesta:

  • Lambda: Función anónima que se puede pasar como un valor
  • Función de orden superior: Función que toma funciones como parámetros o devuelve una función
// Expresión Lambda
val sum = { a: Int, b: Int -> a + b }
println(sum(5, 3))  // 8

// Función de orden superior
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

// Operaciones de colección
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]

Frecuencia: Muy común Dificultad: Media


5. Explica las funciones de extensión en Kotlin.

Respuesta: Las funciones de extensión te permiten agregar nuevas funciones a las clases existentes sin modificar su código fuente.

// Función de extensión para String
fun String.isPalindrome(): Boolean {
    return this == this.reversed()
}

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

// Función de extensión con parámetros
fun Int.times(action: () -> Unit) {
    repeat(this) {
        action()
    }
}

3.times {
    println("Hola")
}
// Imprime "Hola" tres veces

Frecuencia: Común Dificultad: Fácil


6. ¿Cuál es la diferencia entre == y === en Kotlin?

Respuesta:

  • ==: Igualdad estructural (compara valores usando equals())
  • ===: Igualdad referencial (compara referencias de memoria)
val str1 = "Hola"
val str2 = "Hola"
val str3 = str1

println(str1 == str2)   // true (mismo valor)
println(str1 === str2)  // true (optimización del string pool)
println(str1 === str3)  // true (misma referencia)

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

println(list1 == list2)   // true (mismo contenido)
println(list1 === list2)  // false (diferentes objetos)

Frecuencia: Común Dificultad: Fácil


Componentes de Android (5 preguntas)

7. ¿Qué es una Activity y explica su ciclo de vida?

Respuesta: Una Activity representa una sola pantalla con una interfaz de usuario. Tiene un ciclo de vida bien definido:

Loading diagram...
  • onCreate(): La Activity se crea. Inicializa la UI, establece la vista de contenido.
  • onStart(): La Activity se vuelve visible.
  • onResume(): La Activity está en primer plano e interactiva.
  • onPause(): La Activity pierde el foco (otra Activity pasa al primer plano).
  • onStop(): La Activity ya no es visible.
  • onDestroy(): La Activity se destruye.
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        // Inicializar vistas, configurar datos
    }
    
    override fun onStart() {
        super.onStart()
        // La Activity se vuelve visible
    }
    
    override fun onResume() {
        super.onResume()
        // La Activity es interactiva, iniciar animaciones
    }
    
    override fun onPause() {
        super.onPause()
        // La Activity pierde el foco, pausar acciones en curso
    }
}

Frecuencia: Muy común Dificultad: Fácil


8. ¿Cuál es la diferencia entre una Activity y un Fragment?

Respuesta:

  • Activity: Representa una pantalla completa. Punto de entrada para la interacción del usuario. Tiene su propio ciclo de vida.
  • Fragment: Porción reutilizable de la UI dentro de una Activity. Pueden existir múltiples fragments en una Activity. Tiene su propio ciclo de vida vinculado a la Activity anfitriona.
  • Beneficios de los Fragments:
    • Reusabilidad en las Activities
    • Componentes de UI modulares
    • Soporte para tablets (diseños de múltiples paneles)
    • Integración del componente de navegación
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)
        // Inicializar vistas
    }
}

// En la 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()
    }
}

Frecuencia: Muy común Dificultad: Fácil


9. ¿Qué es un Intent y cuáles son sus tipos?

Respuesta: Intent es un objeto de mensajería utilizado para solicitar una acción de otro componente de la aplicación.

  • Intent Explícito: Especifica el componente exacto para iniciar (por nombre de clase)
  • Intent Implícito: Declara una acción general, y el sistema encuentra el componente apropiado
// Intent Explícito - iniciar una Activity específica
val intent = Intent(this, DetailActivity::class.java)
intent.putExtra("USER_ID", 123)
startActivity(intent)

// Intent Implícito - abrir URL
val webIntent = Intent(Intent.ACTION_VIEW, Uri.parse("https://ejemplo.com"))
startActivity(webIntent)

// Intent Implícito - compartir texto
val shareIntent = Intent(Intent.ACTION_SEND).apply {
    type = "text/plain"
    putExtra(Intent.EXTRA_TEXT, "¡Mira esto!")
}
startActivity(Intent.createChooser(shareIntent, "Compartir vía"))

// Intent Implícito - hacer una llamada telefónica
val callIntent = Intent(Intent.ACTION_DIAL).apply {
    data = Uri.parse("tel:1234567890")
}
startActivity(callIntent)

Frecuencia: Muy común Dificultad: Fácil


10. ¿Qué es un Service en Android?

Respuesta: Un Service es un componente que se ejecuta en segundo plano para realizar operaciones de larga duración sin una interfaz de usuario.

  • Tipos:
    • Foreground Service: Realiza operaciones notables (reproductor de música). Muestra notificación.
    • Background Service: Realiza operaciones no directamente notadas por el usuario.
    • Bound Service: Permite que los componentes se vinculen a él e interactúen.
class MyService : Service() {
    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        // Realizar trabajo en segundo plano
        Thread {
            // Operación de larga duración
            Thread.sleep(5000)
            stopSelf()
        }.start()
        
        return START_STICKY
    }
    
    override fun onBind(intent: Intent?): IBinder? {
        return null
    }
}

// Iniciar servicio
val serviceIntent = Intent(this, MyService::class.java)
startService(serviceIntent)

// Detener servicio
stopService(serviceIntent)

Frecuencia: Común Dificultad: Media


11. ¿Qué es un BroadcastReceiver?

Respuesta: BroadcastReceiver es un componente que responde a anuncios de broadcast a nivel de sistema.

  • Casos de uso: Batería baja, cambios en la conectividad de red, SMS recibido, inicio completado
  • Registro:
    • Estático: En AndroidManifest.xml (limitado en versiones más nuevas de Android)
    • Dinámico: En código (preferido)
class MyBroadcastReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context?, intent: Intent?) {
        when (intent?.action) {
            Intent.ACTION_BATTERY_LOW -> {
                // Manejar batería baja
            }
            ConnectivityManager.CONNECTIVITY_ACTION -> {
                // Manejar cambio de red
            }
        }
    }
}

// Registrar dinámicamente
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)
    }
}

Frecuencia: Común Dificultad: Fácil


Desarrollo de UI (4 preguntas)

12. ¿Cuál es la diferencia entre LinearLayout, RelativeLayout y ConstraintLayout?

Respuesta:

  • LinearLayout: Organiza los elementos secundarios en una sola fila o columna. Simple pero puede conducir a diseños anidados.
  • RelativeLayout: Posiciona los elementos secundarios en relación con otros o con el padre. Más flexible pero complejo.
  • ConstraintLayout: Diseño moderno y flexible. Jerarquía de vista plana. Recomendado para UIs complejas.
<!-- Ejemplo de ConstraintLayout -->
<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="Título"
        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="Haz clic aquí"
        app:layout_constraintTop_toBottomOf="@id/title"
        app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

Frecuencia: Muy común Dificultad: Fácil


13. ¿Qué es RecyclerView y cómo funciona?

Respuesta: RecyclerView es un widget eficiente para mostrar grandes listas reciclando vistas.

  • Componentes:
    • Adapter: Vincula datos a las vistas
    • ViewHolder: Contiene referencias a las vistas (evita llamadas a findViewById)
    • LayoutManager: Posiciona los elementos (Lineal, 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
}

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

Frecuencia: Muy común Dificultad: Media


14. ¿Qué es Jetpack Compose?

Respuesta: Jetpack Compose es el moderno kit de herramientas de UI declarativa de Android.

  • Declarativo: Describe cómo debería verse la UI, no cómo construirla
  • Beneficios: Menos código, intuitivo, potente, acelera el desarrollo
  • Composable Functions: Bloques de construcción de la UI de Compose
@Composable
fun Greeting(name: String) {
    Text(text = "¡Hola, $name!")
}

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

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

Frecuencia: Muy común Dificultad: Fácil


15. ¿Cuál es la diferencia entre match_parent y wrap_content?

Respuesta: Estos son parámetros de diseño que definen las dimensiones de la vista:

  • match_parent: La vista se expande para llenar el tamaño del padre
  • wrap_content: La vista se dimensiona para ajustarse a su contenido
  • Tamaño fijo: Valor dp específico (por ejemplo, 100dp)
<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    
    <!-- Llena el ancho del padre, ajusta la altura al contenido -->
    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Título" />
    
    <!-- Ajusta ambas dimensiones -->
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Haz clic aquí" />
    
    <!-- Tamaño fijo -->
    <ImageView
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:src="@drawable/icon" />
</LinearLayout>

Frecuencia: Muy común Dificultad: Fácil


Datos y Redes (5 preguntas)

16. ¿Cómo se realiza una solicitud de red en Android?

Respuesta: Usa bibliotecas como Retrofit u OkHttp para las redes. Evita usar HttpURLConnection directamente.

// Configuración de Retrofit
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.ejemplo.com/"
    
    val apiService: ApiService by lazy {
        Retrofit.Builder()
            .baseUrl(BASE_URL)
            .addConverterFactory(GsonConverterFactory.create())
            .build()
            .create(ApiService::class.java)
    }
}

// Uso en ViewModel
class UserViewModel : ViewModel() {
    fun fetchUser(userId: Int) {
        viewModelScope.launch {
            try {
                val user = RetrofitClient.apiService.getUser(userId)
                // Actualizar UI
            } catch (e: Exception) {
                // Manejar error
            }
        }
    }
}

Frecuencia: Muy común Dificultad: Media


17. ¿Qué es Room y cómo se usa?

Respuesta: Room es una capa de abstracción sobre SQLite para facilitar el acceso a la base de datos.

  • Componentes:
    • Entity: Representa una tabla
    • DAO (Data Access Object): Define las operaciones de la base de datos
    • Database: Contenedor de la base de datos
// 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")
    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
            }
        }
    }
}

Frecuencia: Muy común Dificultad: Media


18. ¿Qué es SharedPreferences?

Respuesta: SharedPreferences almacena pequeñas cantidades de datos primitivos como pares clave-valor.

  • Casos de uso: Ajustes de usuario, preferencias, flags simples
  • No para: Grandes cantidades de datos, objetos complejos (usa Room en su lugar)
// Guardar datos
val sharedPref = getSharedPreferences("MisPrefs", Context.MODE_PRIVATE)
with(sharedPref.edit()) {
    putString("username", "John")
    putInt("age", 25)
    putBoolean("isLoggedIn", true)
    apply()  // o commit()
}

// Leer datos
val username = sharedPref.getString("username", "Invitado")
val age = sharedPref.getInt("age", 0)
val isLoggedIn = sharedPref.getBoolean("isLoggedIn", false)

// Eliminar datos
with(sharedPref.edit()) {
    remove("username")
    apply()
}

// Limpiar todo
with(sharedPref.edit()) {
    clear()
    apply()
}

Frecuencia: Muy común Dificultad: Fácil


19. ¿Cuál es la diferencia entre apply() y commit() en SharedPreferences?

Respuesta: Ambos guardan los cambios en SharedPreferences, pero difieren en el comportamiento:

  • apply(): Asíncrono. Regresa inmediatamente. Los cambios se escriben en el disco en segundo plano. Sin valor de retorno.
  • commit(): Síncrono. Se bloquea hasta que se escriben los cambios. Devuelve un booleano (éxito/fracaso).
  • Mejor práctica: Usa apply() a menos que necesites el valor de retorno.
val editor = sharedPref.edit()

// apply() - asíncrono, preferido
editor.putString("key", "value")
editor.apply()  // Regresa inmediatamente

// commit() - síncrono
editor.putString("key", "value")
val success = editor.commit()  // Espera la escritura, devuelve un booleano
if (success) {
    // Guardado exitoso
}

Frecuencia: Común Dificultad: Fácil


20. ¿Qué son las Coroutines en Kotlin?

Respuesta: Las Coroutines proporcionan una forma de escribir código asíncrono secuencialmente, lo que facilita su lectura y mantenimiento.

  • Beneficios: Ligeras, concurrencia estructurada, manejo de excepciones
  • Conceptos clave:
    • suspend: Función que se puede pausar y reanudar
    • launch: Inicia una coroutine (dispara y olvida)
    • async: Inicia una coroutine y devuelve un resultado
    • Dispatchers: Controla en qué hilo se ejecuta la coroutine
class MyViewModel : ViewModel() {
    fun loadData() {
        viewModelScope.launch {
            try {
                // Hilo principal
                showLoading()
                
                // Cambiar al hilo de IO para la llamada de red
                val data = withContext(Dispatchers.IO) {
                    fetchDataFromNetwork()
                }
                
                // De vuelta al hilo principal
                updateUI(data)
            } catch (e: Exception) {
                handleError(e)
            }
        }
    }
    
    suspend fun fetchDataFromNetwork(): String {
        delay(2000)  // Simula el retraso de la red
        return "Datos del servidor"
    }
}

// Ejecución paralela
viewModelScope.launch {
    val deferred1 = async { fetchUser(1) }
    val deferred2 = async { fetchUser(2) }
    
    val user1 = deferred1.await()
    val user2 = deferred2.await()
}

Frecuencia: Muy común Dificultad: Media

Newsletter subscription

Consejos de carrera semanales que realmente funcionan

Recibe las últimas ideas directamente en tu bandeja de entrada

Crea un Currículum que Te Contrate 60% Más Rápido

En minutos, crea un currículum personalizado y compatible con ATS que ha demostrado conseguir 6 veces más entrevistas.

Crea un mejor currículum

Compartir esta publicación

Haz que tus 6 Segundos Cuenten

Los reclutadores escanean currículums durante un promedio de solo 6 a 7 segundos. Nuestras plantillas probadas están diseñadas para captar la atención al instante y mantenerlos leyendo.