dezembro 21, 2025
14 min de leitura

Perguntas de entrevista para desenvolvedor Android júnior

interview
career-advice
job-search
entry-level
Perguntas de entrevista para desenvolvedor Android júnior
Milad Bonakdar

Milad Bonakdar

Autor

Prepare-se para uma entrevista Android júnior com perguntas sobre Kotlin, ciclo de vida, Jetpack Compose, Room, rede e coroutines.


Introdução

Em uma entrevista para desenvolvedor Android júnior, espere perguntas que avaliam se você consegue explicar Kotlin, ciclos de vida do Android, estado no Compose, armazenamento local, chamadas de rede e coroutines de forma prática. Boas respostas conectam cada conceito a uma app pequena e confiável: estado da UI no lugar certo, main thread livre, dados persistidos com segurança e erros tratados com clareza.

Use este guia para praticar respostas curtas e relacione cada tema a um projeto do seu currículo ou portfólio. Uma app de tarefas, clima ou uma tela ligada a uma API torna suas respostas mais específicas e menos decoradas.


Fundamentos do Kotlin (6 Perguntas)

1. Qual é a diferença entre val e var em Kotlin?

Resposta:

  • val: Declara uma variável imutável (somente leitura). Uma vez atribuída, seu valor não pode ser alterado.
  • var: Declara uma variável mutável. Seu valor pode ser alterado após a inicialização.
  • Melhor Prática: Use val por padrão para segurança. Use var somente quando precisar reatribuir o valor.
val name = "John"  // Não pode ser alterado
var age = 25       // Pode ser alterado
age = 26           // Válido
// name = "Jane"   // Erro: Val não pode ser reatribuído

Raridade: Muito Comum Dificuldade: Fácil


2. Explique os tipos Nullable e o operador Safe Call em Kotlin.

Resposta: O sistema de tipos do Kotlin distingue entre tipos nullable e não nullable para evitar exceções de ponteiro nulo.

  • Tipo Nullable: Adicione ? após o tipo: String?
  • Safe Call (?.): Chama um método somente se o objeto não for nulo
  • Elvis Operator (?:): Fornece um valor padrão se for nulo
  • Not-null Assertion (!!): Lança exceção se for nulo (use com moderação)
var email: String? = "[email protected]"

// Safe call
val length = email?.length  // Retorna null se email for null

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

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

// Not-null assertion (perigoso)
val len = email!!.length  // Lança exceção se email for null

Raridade: Muito Comum Dificuldade: Fácil


3. Qual é a diferença entre uma class e uma data class em Kotlin?

Resposta:

  • Classe Regular: Definição de classe padrão
  • Data Class: Gera automaticamente métodos úteis para armazenar dados
    • equals() e hashCode()
    • toString()
    • Função copy()
    • Funções componentN() para desestruturação
// Classe 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() gerado)
println(user1)           // User(name=John, age=25) (toString() gerado)

// Cópia com modificações
val user3 = user1.copy(age = 26)

// Desestruturação
val (name, age) = user1

Raridade: Muito Comum Dificuldade: Fácil


4. O que são expressões Lambda e funções Higher-order em Kotlin?

Resposta:

  • Lambda: Função anônima que pode ser passada como um valor
  • Função Higher-order: Função que recebe funções como parâmetros ou retorna uma função
// Expressão Lambda
val sum = { a: Int, b: Int -> a + b }
println(sum(5, 3))  // 8

// Função Higher-order
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

// Operações de coleção
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]

Raridade: Muito Comum Dificuldade: Médio


5. Explique as Extension Functions em Kotlin.

Resposta: As extension functions permitem adicionar novas funções às classes existentes sem modificar seu código-fonte.

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

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

// Extension function com parâmetros
fun Int.times(action: () -> Unit) {
    repeat(this) {
        action()
    }
}

3.times {
    println("Hello")
}
// Imprime "Hello" três vezes

Raridade: Comum Dificuldade: Fácil


6. Qual é a diferença entre == e === em Kotlin?

Resposta:

  • ==: Igualdade estrutural (compara valores usando equals())
  • ===: Igualdade referencial (compara referências de memória)
val str1 = "Hello"
val str2 = "Hello"
val str3 = str1

println(str1 == str2)   // true (mesmo valor)
println(str1 === str2)  // true (otimização do string pool)
println(str1 === str3)  // true (mesma referência)

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

println(list1 == list2)   // true (mesmo conteúdo)
println(list1 === list2)  // false (objetos diferentes)

Raridade: Comum Dificuldade: Fácil


Componentes Android (5 Perguntas)

7. O que é uma Activity e explique seu ciclo de vida.

Resposta: Uma Activity representa uma única tela com uma interface de usuário. Possui um ciclo de vida bem definido:

Loading diagram...
  • onCreate(): A Activity é criada. Inicialize a UI, defina o content view.
  • onStart(): A Activity se torna visível.
  • onResume(): A Activity está em primeiro plano e interativa.
  • onPause(): A Activity está perdendo o foco (outra activity vindo para o primeiro plano).
  • onStop(): A Activity não está mais visível.
  • onDestroy(): A Activity é destruída.
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        // Inicialize as views, configure os dados
    }
    
    override fun onStart() {
        super.onStart()
        // Activity se tornando visível
    }
    
    override fun onResume() {
        super.onResume()
        // Activity é interativa, inicie animações
    }
    
    override fun onPause() {
        super.onPause()
        // Activity perdendo o foco, pause as ações em andamento
    }
}

Raridade: Muito Comum Dificuldade: Fácil


8. Qual é a diferença entre uma Activity e um Fragment?

Resposta:

  • Activity: Representa uma tela inteira. Ponto de entrada para a interação do usuário. Tem seu próprio ciclo de vida.
  • Fragment: Porção reutilizável da UI dentro de uma Activity. Vários fragments podem existir em uma activity. Tem seu próprio ciclo de vida vinculado à activity hospedeira.
  • Benefícios dos Fragments:
    • Reutilização entre activities
    • Componentes de UI modulares
    • Suporte para tablets (layouts multi-pane)
    • Integração do componente de navegação
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)
        // Inicialize as views
    }
}

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

Raridade: Muito Comum Dificuldade: Fácil


9. O que é uma Intent e quais são seus tipos?

Resposta: Intent é um objeto de mensagem usado para solicitar uma ação de outro componente do aplicativo.

  • Intent Explícita: Especifica o componente exato para iniciar (pelo nome da classe)
  • Intent Implícita: Declara uma ação geral e o sistema encontra o componente apropriado
// Intent Explícita - iniciar activity específica
val intent = Intent(this, DetailActivity::class.java)
intent.putExtra("USER_ID", 123)
startActivity(intent)

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

// Intent Implícita - compartilhar texto
val shareIntent = Intent(Intent.ACTION_SEND).apply {
    type = "text/plain"
    putExtra(Intent.EXTRA_TEXT, "Confira isso!")
}
startActivity(Intent.createChooser(shareIntent, "Compartilhar via"))

// Intent Implícita - fazer chamada telefônica
val callIntent = Intent(Intent.ACTION_DIAL).apply {
    data = Uri.parse("tel:1234567890")
}
startActivity(callIntent)

Raridade: Muito Comum Dificuldade: Fácil


10. O que é um Service no Android?

Resposta: Um Service é um componente que é executado em segundo plano para executar operações de longa duração sem uma interface de usuário.

  • Tipos:
    • Foreground Service: Executa operações perceptíveis (tocador de música). Mostra notificação.
    • Background Service: Executa operações não percebidas diretamente pelo usuário.
    • Bound Service: Permite que os componentes se liguem a ele e interajam.
class MyService : Service() {
    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        // Executar trabalho em segundo plano
        Thread {
            // Operação de longa duração
            Thread.sleep(5000)
            stopSelf()
        }.start()
        
        return START_STICKY
    }
    
    override fun onBind(intent: Intent?): IBinder? {
        return null
    }
}

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

// Parar serviço
stopService(serviceIntent)

Raridade: Comum Dificuldade: Médio


11. O que é um BroadcastReceiver?

Resposta: BroadcastReceiver é um componente que responde a anúncios de broadcast em todo o sistema.

  • Casos de Uso: Bateria fraca, mudanças na conectividade de rede, SMS recebido, inicialização concluída
  • Registro:
    • Estático: No AndroidManifest.xml (limitado em versões mais recentes do Android)
    • Dinâmico: No código (preferido)
class MyBroadcastReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context?, intent: Intent?) {
        when (intent?.action) {
            Intent.ACTION_BATTERY_LOW -> {
                // Lidar com bateria fraca
            }
            ConnectivityManager.CONNECTIVITY_ACTION -> {
                // Lidar com mudança de rede
            }
        }
    }
}

// Registrar dinamicamente
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)
    }
}

Raridade: Comum Dificuldade: Fácil


Desenvolvimento de UI (4 Perguntas)

12. Qual é a diferença entre LinearLayout, RelativeLayout e ConstraintLayout?

Resposta:

  • LinearLayout: Organiza os filhos em uma única linha ou coluna. Simples, mas pode levar a layouts aninhados.
  • RelativeLayout: Posiciona os filhos em relação uns aos outros ou ao pai. Mais flexível, mas complexo.
  • ConstraintLayout: Layout moderno e flexível. Hierarquia de visualização plana. Recomendado para UIs complexas.
<!-- Exemplo 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="Clique Aqui"
        app:layout_constraintTop_toBottomOf="@id/title"
        app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

Raridade: Muito Comum Dificuldade: Fácil


13. O que é RecyclerView e como funciona?

Resposta: RecyclerView é um widget eficiente para exibir grandes listas reciclando visualizações.

  • Componentes:
    • Adapter: Vincula dados às visualizações
    • ViewHolder: Mantém referências às visualizações (evita chamadas findViewById)
    • LayoutManager: Posiciona os itens (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
}

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

Raridade: Muito Comum Dificuldade: Médio


14. O que é Jetpack Compose?

Resposta: Jetpack Compose é o moderno kit de ferramentas de UI declarativo do Android.

  • Declarativo: Descreva como a UI deve ser, não como construí-la
  • Benefícios: Menos código, intuitivo, poderoso, acelera o desenvolvimento
  • Funções Composable: Blocos de construção da UI do Compose
@Composable
fun Greeting(name: String) {
    Text(text = "Olá, $name!")
}

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

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

Raridade: Muito Comum Dificuldade: Fácil


15. Qual é a diferença entre match_parent e wrap_content?

Resposta: Esses são parâmetros de layout que definem as dimensões da visualização:

  • match_parent: A visualização se expande para preencher o tamanho do pai
  • wrap_content: A visualização se dimensiona para ajustar seu conteúdo
  • Tamanho Fixo: Valor dp específico (por exemplo, 100dp)
<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    
    <!-- Preenche a largura do pai, envolve a altura do conteúdo -->
    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Título" />
    
    <!-- Envolve ambas as dimensões -->
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Clique Aqui" />
    
    <!-- Tamanho fixo -->
    <ImageView
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:src="@drawable/icon" />
</LinearLayout>

Raridade: Muito Comum Dificuldade: Fácil


Dados e Rede (5 Perguntas)

16. Como você faz uma solicitação de rede no Android?

Resposta: Use bibliotecas como Retrofit ou OkHttp para rede. Evite usar HttpURLConnection diretamente.

// Configuração do 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.example.com/"
    
    val apiService: ApiService by lazy {
        Retrofit.Builder()
            .baseUrl(BASE_URL)
            .addConverterFactory(GsonConverterFactory.create())
            .build()
            .create(ApiService::class.java)
    }
}

// Uso no ViewModel
class UserViewModel : ViewModel() {
    fun fetchUser(userId: Int) {
        viewModelScope.launch {
            try {
                val user = RetrofitClient.apiService.getUser(userId)
                // Atualizar UI
            } catch (e: Exception) {
                // Lidar com erro
            }
        }
    }
}

Raridade: Muito Comum Dificuldade: Médio


17. O que é Room e como você o usa?

Resposta: Room é uma camada de abstração sobre o SQLite para facilitar o acesso ao banco de dados.

  • Componentes:
    • Entity: Representa uma tabela
    • DAO (Data Access Object): Define operações de banco de dados
    • Database: Contêiner do banco de dados
// 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
            }
        }
    }
}

Raridade: Muito Comum Dificuldade: Médio


18. O que é SharedPreferences?

Resposta: SharedPreferences armazena pequenas quantidades de dados primitivos como pares de chave-valor.

  • Casos de Uso: Configurações do usuário, preferências, flags simples
  • Não para: Grandes dados, objetos complexos (use Room em vez disso)
// Salvar dados
val sharedPref = getSharedPreferences("MyPrefs", Context.MODE_PRIVATE)
with(sharedPref.edit()) {
    putString("username", "John")
    putInt("age", 25)
    putBoolean("isLoggedIn", true)
    apply()  // ou commit()
}

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

// Remover dados
with(sharedPref.edit()) {
    remove("username")
    apply()
}

// Limpar tudo
with(sharedPref.edit()) {
    clear()
    apply()
}

Raridade: Muito Comum Dificuldade: Fácil


19. Qual é a diferença entre apply() e commit() em SharedPreferences?

Resposta: Ambos salvam as alterações no SharedPreferences, mas diferem no comportamento:

  • apply(): Assíncrono. Retorna imediatamente. Alterações gravadas no disco em segundo plano. Sem valor de retorno.
  • commit(): Síncrono. Bloqueia até que as alterações sejam gravadas. Retorna booleano (sucesso/falha).
  • Melhor Prática: Use apply() a menos que precise do valor de retorno.
val editor = sharedPref.edit()

// apply() - assíncrono, preferido
editor.putString("key", "value")
editor.apply()  // Retorna imediatamente

// commit() - síncrono
editor.putString("key", "value")
val success = editor.commit()  // Espera pela gravação, retorna booleano
if (success) {
    // Salvamento bem-sucedido
}

Raridade: Comum Dificuldade: Fácil


20. O que são Coroutines em Kotlin?

Resposta: As Coroutines fornecem uma maneira de escrever código assíncrono sequencialmente, tornando-o mais fácil de ler e manter.

  • Benefícios: Leve, concorrência estruturada, tratamento de exceções
  • Conceitos Chave:
    • suspend: Função que pode ser pausada e retomada
    • launch: Inicia uma coroutine (disparar e esquecer)
    • async: Inicia uma coroutine e retorna um resultado
    • Dispatchers: Controla em qual thread a coroutine é executada
class MyViewModel : ViewModel() {
    fun loadData() {
        viewModelScope.launch {
            try {
                // Thread principal
                showLoading()
                
                // Mudar para a thread IO para chamada de rede
                val data = withContext(Dispatchers.IO) {
                    fetchDataFromNetwork()
                }
                
                // De volta à thread principal
                updateUI(data)
            } catch (e: Exception) {
                handleError(e)
            }
        }
    }
    
    suspend fun fetchDataFromNetwork(): String {
        delay(2000)  // Simula atraso de rede
        return "Dados do servidor"
    }
}

// Execução paralela
viewModelScope.launch {
    val deferred1 = async { fetchUser(1) }
    val deferred2 = async { fetchUser(2) }
    
    val user1 = deferred1.await()
    val user2 = deferred2.await()
}

Raridade: Muito Comum Dificuldade: Médio


Newsletter subscription

Dicas de carreira semanais que realmente funcionam

Receba as últimas ideias diretamente na sua caixa de entrada

Sua Próxima Entrevista Está a Apenas um Currículo de Distância

Crie um currículo profissional e otimizado em minutos. Não são necessárias habilidades de design—apenas resultados comprovados.

Criar meu currículo

Compartilhar esta publicação

Reduza o Tempo de Escrita do Currículo em 90%

O candidato a emprego médio gasta mais de 3 horas formatando um currículo. Nossa IA faz isso em menos de 15 minutos, levando você à fase de candidatura 12 vezes mais rápido.