diciembre 21, 2025
14 min de lectura

Preguntas para Entrevistas de Desarrollador Mobile Junior (Android): Guía Completa

interview
career-advice
job-search
entry-level
Preguntas para Entrevistas de Desarrollador Mobile Junior (Android): Guía Completa
Milad Bonakdar

Milad Bonakdar

Autor

Domina los fundamentos del desarrollo de Android con preguntas esenciales para entrevistas que cubren Kotlin, Activities, Fragments, Jetpack Compose, persistencia de datos y las mejores prácticas de Android para desarrolladores junior.


Introducción

El desarrollo de Android impulsa miles de millones de dispositivos en todo el mundo, lo que la convierte en una de las plataformas móviles más importantes. Con Kotlin como lenguaje preferido y herramientas modernas como Jetpack Compose, los desarrolladores de Android crean aplicaciones ricas y de alto rendimiento para diversos usuarios.

Esta guía cubre preguntas esenciales de entrevista para Desarrolladores Android Junior. Exploramos los fundamentos de Kotlin, los componentes de Android, el desarrollo de UI, la persistencia de datos, las redes y las mejores prácticas para ayudarte a prepararte para tu primer puesto de desarrollador de Android.


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.