10 câu hỏi có thể gặp khi phỏng vấn
Chọn theo kỹ năng và cấp của tin (Mobile (Android, iOS, Flutter)). Gợi ý trả lời do Drama Công Ty biên soạn để bạn ôn nhanh, không phải đề của công ty.
01Trong Flutter, StatelessWidget và StatefulWidget khác nhau thế nào? setState hoạt động ra sao?Mobile (Android, iOS, Flutter) · Junior
Gợi ý trả lời
Ôn thêm câu hỏi Mobile (Android, iOS, Flutter) →StatelessWidgetchỉ phụ thuộc tham số đầu vào;StatefulWidgetcó một objectStateriêng, tồn tại qua các lần build, để giữ dữ liệu thay đổi theo thời gian.setStateđánh dấu widget "dirty"; ở frame kế tiếp Flutter gọi lạibuildcho widget đó và cây con.- Widget là cấu hình nhẹ, tạo lại liên tục; Element và RenderObject mới là phần được tái sử dụng.
- Dọn dẹp controller, stream subscription trong
dispose.
void _increment() => setState(() => _count++);Hay sai: gọi
setStatesau khi widget đã bị huỷ (sau mộtawait); hãy kiểm tramounted. ĐặtsetStateở widget quá cao làm build lại cả màn hình, nên tách nhỏ widget và dùngconstconstructor.Khi state dùng chung nhiều màn hình, chuyển sang Provider, Riverpod hoặc Bloc.
02Vì sao không được làm việc nặng trên main thread (UI thread)? Bạn xử lý thế nào?Mobile (Android, iOS, Flutter) · Junior
Gợi ý trả lời
Ôn thêm câu hỏi Mobile (Android, iOS, Flutter) →Main thread vẽ UI và nhận thao tác người dùng. Mỗi frame chỉ có khoảng 16 ms (60 Hz) hoặc ít hơn với màn 120 Hz, nên việc nặng sẽ gây giật, lag; Android chặn quá lâu sẽ báo ANR.
Cách xử lý theo nền tảng:
- Android: coroutines với
Dispatchers.IOcho I/O,Dispatchers.Defaultcho tính toán, sau đó cập nhật state trên Main. - iOS:
async/awaitvới Swift concurrency; code UI đánh dấu@MainActor. - Flutter: Dart chạy một isolate; I/O bất đồng bộ không chặn, nhưng tính toán nặng (parse JSON lớn, xử lý ảnh) nên đẩy sang isolate khác bằng
Isolate.runhoặccompute.
Hay sai: nghĩ
asynclà đủ. Hàm async trong Dart vẫn chạy trên cùng isolate, nên vòng lặp tính toán nặng vẫn làm đơ UI.- Android: coroutines với
03Kotlin coroutines: suspend function, scope, dispatcher và structured concurrency là gì?Mobile (Android, iOS, Flutter)
Gợi ý trả lời
Ôn thêm câu hỏi Mobile (Android, iOS, Flutter) →Coroutine là tác vụ nhẹ có thể tạm dừng (suspend) mà không chặn thread.
suspend funchỉ gọi được từ coroutine hoặc từ suspend function khác.- Scope quyết định vòng đời:
viewModelScope,lifecycleScopetự huỷ coroutine khi ViewModel hoặc màn hình bị huỷ. - Dispatcher quyết định chạy ở đâu:
Main,IO,Default. - Structured concurrency: coroutine con gắn với cha; cha huỷ thì con huỷ, con lỗi thì lan lên cha (trừ khi dùng
SupervisorJob).
viewModelScope.launch { val user = withContext(Dispatchers.IO) { repo.fetchUser() } _state.value = UiState.Success(user) }Hay sai: dùng
GlobalScopegây rò rỉ; bắtCancellationExceptiontrongcatch (e: Exception)mà không ném lại, làm hỏng cơ chế huỷ.- Scope quyết định vòng đời:
04Swift concurrency: async/await, Task, actor và @MainActor hoạt động thế nào?Mobile (Android, iOS, Flutter)
Gợi ý trả lời
Ôn thêm câu hỏi Mobile (Android, iOS, Flutter) →Swift concurrency đưa bất đồng bộ vào ngôn ngữ, cùng với kiểm tra data race lúc biên dịch (chặt hơn từ Swift 6).
async/await: viết code bất đồng bộ tuần tự, thay cho completion handler lồng nhau.Task { }tạo tác vụ mới;async letvàTaskGroupchạy song song có cấu trúc. Task hỗ trợ huỷ hợp tác quaTask.checkCancellation().actor: kiểu tham chiếu mà trạng thái bên trong chỉ được truy cập tuần tự, tránh data race. Gọi từ ngoài phảiawait.@MainActor: đảm bảo code chạy trên main thread, dùng cho ViewModel và UI.Sendable: đánh dấu kiểu an toàn khi chuyển giữa các vùng concurrency.
@MainActor final class ProfileViewModel: ObservableObject { @Published var user: User? func load() async { user = try? await api.fetchUser() } }Hay sai: actor reentrancy: sau mỗi
await, trạng thái của actor có thể đã bị thay đổi.05So sánh các kiến trúc MVC, MVVM, MVI/Clean Architecture cho ứng dụng mobile.Mobile (Android, iOS, Flutter)
Gợi ý trả lời
Ôn thêm câu hỏi Mobile (Android, iOS, Flutter) →Mục tiêu chung là tách UI khỏi logic để dễ test và dễ bảo trì.
- MVC: dễ bắt đầu nhưng trên iOS UIKit thường thành "Massive View Controller".
- MVVM: ViewModel giữ state và logic, View chỉ quan sát và hiển thị. Hợp tự nhiên với SwiftUI, Jetpack Compose (ViewModel + StateFlow) và Flutter.
- MVI / Unidirectional data flow: một state bất biến duy nhất cho màn hình, UI gửi intent, reducer tạo state mới. Dễ debug, hợp với UI khai báo.
- Clean Architecture: chia layer presentation / domain / data, repository che giấu nguồn dữ liệu (API, cache, DB).
Người phỏng vấn muốn nghe: lý do chọn kiến trúc cho dự án cụ thể. App nhỏ không cần đủ use case và mapper ở mọi tầng; app lớn nhiều team thì nên chia module theo tính năng.
06Danh sách dài (RecyclerView, LazyColumn, List, ListView) bị giật khi cuộn. Bạn tối ưu thế nào?Mobile (Android, iOS, Flutter)
Gợi ý trả lời
Ôn thêm câu hỏi Mobile (Android, iOS, Flutter) →Trước tiên đo bằng công cụ (Android Studio Profiler, Instruments, Flutter DevTools), sau đó xử lý các nguyên nhân phổ biến:
- Ảnh: tải bất đồng bộ, resize đúng kích thước hiển thị, có cache (Coil, Glide, Kingfisher, cached_network_image).
- Công việc trong bind/build: không format ngày, parse dữ liệu hay tính toán mỗi lần vẽ item; chuẩn bị sẵn trong ViewModel.
- Key ổn định: dùng id làm key (
keytrongLazyColumn,ValueKeytrong Flutter,Identifiabletrong SwiftUI) để không tạo lại item không cần thiết. - Cập nhật một phần:
DiffUtil/ListAdapterthay vìnotifyDataSetChanged. - Compose: state ổn định để tránh recompose thừa; Flutter: dùng
ListView.buildervàconstwidget. - Phân trang dữ liệu (Paging 3, infinite scroll).
Người phỏng vấn muốn nghe: bạn đo trước và sau, không chỉ sửa theo cảm tính.
07Bạn phát hiện và xử lý memory leak trong ứng dụng mobile thế nào?Mobile (Android, iOS, Flutter) · Senior
Gợi ý trả lời
Ôn thêm câu hỏi Mobile (Android, iOS, Flutter) →Leak trên mobile thường do một object sống lâu giữ tham chiếu tới object lẽ ra phải được giải phóng, như màn hình, view hay context.
- Android: giữ
Activity/Contexttrong singleton hoặc biến static, listener không huỷ đăng ký, coroutine hoặc callback sống lâu hơn màn hình. Phát hiện bằng LeakCanary và Memory Profiler (heap dump). - iOS: ARC không tự phá được retain cycle, vd. closure giữ
selftrong khiselfgiữ closure, delegate khai báostrong. Dùng[weak self],weak var delegate. Phát hiện bằng Memory Graph Debugger và Instruments (Leaks, Allocations). - Flutter: quên
disposecontroller,StreamSubscription,Timer. Xem bằng DevTools Memory.
Người phỏng vấn muốn nghe: quy trình tái hiện (mở rồi đóng màn hình nhiều lần, xem số instance có giảm không) và một ca bạn đã sửa thật.
- Android: giữ
08Thiết kế tính năng offline-first: app vẫn dùng được khi mất mạng và đồng bộ khi có mạng lại.Mobile (Android, iOS, Flutter) · Senior
Gợi ý trả lời
Ôn thêm câu hỏi Mobile (Android, iOS, Flutter) →Lấy database cục bộ làm nguồn sự thật cho UI: UI chỉ đọc từ DB local, tầng sync lo việc đồng bộ với server.
- Lưu trữ: Room, SwiftData / Core Data, Drift hoặc Isar (Flutter). UI quan sát dữ liệu qua Flow,
@Queryhoặc Stream. - Ghi khi offline: lưu thay đổi vào hàng đợi (outbox) kèm trạng thái pending; khi có mạng thì gửi lên, nhận phản hồi rồi đánh dấu đã đồng bộ.
- Lập lịch đồng bộ: WorkManager (Android), BGTaskScheduler (iOS), có ràng buộc mạng và backoff khi lỗi.
- Xung đột: chọn chiến lược như last-write-wins theo timestamp server, version number, hoặc cho người dùng chọn với dữ liệu quan trọng.
- Request cần idempotent (gửi kèm id do client sinh) để retry không tạo bản ghi trùng.
Người phỏng vấn muốn nghe: bạn tính tới đồng bộ từng phần (delta sync) và cách hiển thị trạng thái "chưa đồng bộ" cho người dùng.
- Lưu trữ: Room, SwiftData / Core Data, Drift hoặc Isar (Flutter). UI quan sát dữ liệu qua Flow,
09Bảo mật ứng dụng mobile: lưu token, gọi API và chống dịch ngược thế nào?Mobile (Android, iOS, Flutter) · Senior
Gợi ý trả lời
Ôn thêm câu hỏi Mobile (Android, iOS, Flutter) →Coi app là môi trường không tin cậy: mọi thứ đóng gói trong app đều có thể bị đọc, nên kiểm soát quan trọng nhất phải nằm ở server.
- Lưu token: Keychain (iOS), Keystore kết hợp EncryptedSharedPreferences hoặc DataStore mã hoá (Android),
flutter_secure_storage. Không lưu trong SharedPreferences / UserDefaults thường hay log ra console. - Phiên đăng nhập: access token ngắn hạn, refresh token xoay vòng; đăng nhập OAuth dùng PKCE.
- Mạng: chỉ HTTPS; có thể dùng certificate pinning nhưng cần kế hoạch xoay chứng chỉ.
- Secret: không nhúng API key quan trọng trong app; gọi qua backend.
- Chống can thiệp: obfuscation (R8), Play Integrity API, App Attest để server xác minh app thật.
Hay sai: tin rằng obfuscation là đủ. Nó chỉ làm chậm kẻ tấn công.
- Lưu token: Keychain (iOS), Keystore kết hợp EncryptedSharedPreferences hoặc DataStore mã hoá (Android),
10Bạn có câu hỏi gì cho chúng tôi không?Câu hỏi hành vi & văn hoá
Gợi ý trả lời
Ôn thêm câu hỏi Câu hỏi hành vi & văn hoá →Luôn có câu hỏi. Đây là cơ hội thể hiện sự quan tâm và cũng là lúc bạn đánh giá công ty.
Câu hỏi nên chuẩn bị (chọn 2–3 câu hợp với người đang phỏng vấn):
- "Một ngày làm việc của vị trí này thường như thế nào? Team có bao nhiêu người, gồm những vai trò gì?"
- "Ba tháng đầu, anh/chị kỳ vọng người vào vị trí này làm được gì?"
- "Team đang gặp thách thức kỹ thuật hoặc quy trình nào lớn nhất?"
- "Quy trình release và code review của team ra sao?"
- "Công ty đánh giá hiệu suất và review lương theo chu kỳ nào?"
- "Vì sao vị trí này đang tuyển: mở rộng team hay thay người?"
Hay sai: "Dạ không ạ", hoặc chỉ hỏi về nghỉ phép, lương thưởng ngay ở vòng kỹ thuật. Chuyện chế độ nên hỏi kỹ với HR.
Bạn đã phỏng vấn ở Motorist Pte Ltd?
Kể lại các vòng và câu hỏi đã gặp (ẩn danh) để người sau ôn đúng trọng tâm.
Chia sẻ kinh nghiệm phỏng vấn