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), JavaScript & TypeScript, ReactJS). 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.
01Chọn native (Kotlin/Swift) hay cross-platform (Flutter, React Native) cho một dự án mới? Bạn cân nhắc gì?Mobile (Android, iOS, Flutter) · Senior
Gợi ý trả lời
Ôn thêm câu hỏi Mobile (Android, iOS, Flutter) →Không có đáp án chung; quyết định dựa trên yêu cầu sản phẩm, đội ngũ và chi phí dài hạn.
- Cross-platform (Flutter, React Native, hoặc Kotlin Multiplatform để chia sẻ logic): một codebase cho hai nền tảng, ra mắt nhanh, hợp với app nghiệp vụ, thương mại điện tử, nội bộ.
- Native: cần tích hợp sâu với hệ điều hành (widget, camera hoặc Bluetooth phức tạp, xử lý nền), hiệu năng đồ hoạ cao, hoặc muốn dùng tính năng mới của OS ngay khi ra mắt.
Các yếu tố cần cân nhắc:
- Kỹ năng hiện có của team và khả năng tuyển người.
- Mức độ tuỳ biến UI theo từng nền tảng.
- Chi phí viết plugin native khi thư viện không có sẵn.
- Kích thước app và thời gian khởi động.
Người phỏng vấn muốn nghe: bạn đưa ra lập luận gắn với bối cảnh cụ thể và nhắc tới phương án lai, vd. thêm màn hình Flutter vào app native.
02Trình bày vòng đời (lifecycle) của Activity trên Android hoặc của app/view trên iOS.Mobile (Android, iOS, Flutter) · Junior
Gợi ý trả lời
Ôn thêm câu hỏi Mobile (Android, iOS, Flutter) →Vòng đời cho biết khi nào hệ điều hành tạo, hiển thị, tạm dừng và huỷ màn hình, để bạn khởi tạo và giải phóng tài nguyên đúng lúc.
- Android Activity:
onCreate→onStart→onResume(đang tương tác) →onPause→onStop→onDestroy. Xoay màn hình mặc định sẽ huỷ và tạo lại Activity. - iOS UIKit:
viewDidLoad(một lần) →viewWillAppear→viewDidAppear→viewWillDisappear→viewDidDisappear. Trạng thái app: active, inactive, background. - SwiftUI / Compose: nghĩ theo state và các hook như
.task,onAppear,LaunchedEffect,DisposableEffect.
Hay sai: đăng ký listener hoặc sensor trong
onResumemà quên huỷ ởonPause, gây rò rỉ và hao pin. Trên Android nên thu thập Flow bằngrepeatOnLifecycle.- Android Activity:
03Làm sao giữ dữ liệu khi xoay màn hình hoặc khi app bị hệ điều hành kill ở background?Mobile (Android, iOS, Flutter) · Junior
Gợi ý trả lời
Ôn thêm câu hỏi Mobile (Android, iOS, Flutter) →Có hai tình huống khác nhau: thay đổi cấu hình (xoay màn hình, đổi ngôn ngữ) và process death (hệ điều hành kill app để lấy RAM).
Android:
ViewModelsống qua thay đổi cấu hình nhưng không sống qua process death.SavedStateHandle(trong ViewModel) hoặcrememberSaveable(Compose) lưu trạng thái nhỏ như id, nội dung form, vị trí cuộn.- Dữ liệu lớn hoặc quan trọng lưu bền vững bằng Room hoặc DataStore.
iOS: SwiftUI có
@SceneStoragecho trạng thái UI nhỏ; UIKit có state restoration; dữ liệu thật lưu bằng SwiftData, Core Data hoặc file.Người phỏng vấn muốn nghe: bạn biết cách kiểm thử process death, vd. bật "Don't keep activities" hoặc dùng
adb shell am killkhi app ở background.04Phân biệt
var,letvàconst. Hoisting và temporal dead zone là gì?JavaScript & TypeScript · JuniorGợi ý trả lời
Ôn thêm câu hỏi JavaScript & TypeScript →varcó phạm vi hàm, cònlet/constcó phạm vi block ({}).constkhông cho gán lại biến, nhưng object hay array bên trong vẫn sửa được.- Hoisting: khai báo được "kéo" lên đầu phạm vi.
varđược khởi tạo sẵn làundefined, nên dùng trước khi khai báo không báo lỗi. let/constcũng được hoist nhưng chưa khởi tạo. Truy cập trước dòng khai báo némReferenceError; khoảng đó gọi là temporal dead zone.- Function declaration được hoist cả phần thân, nên gọi được trước khi định nghĩa.
console.log(a); // undefined console.log(b); // ReferenceError var a = 1; let b = 2;Người phỏng vấn muốn nghe: mặc định dùng
const, cần gán lại mới dùnglet, và gần như không dùngvartrong code mới.- Hoisting: khai báo được "kéo" lên đầu phạm vi.
05
==và===khác nhau thế nào? Những giá trị nào là falsy?JavaScript & TypeScript · JuniorGợi ý trả lời
Ôn thêm câu hỏi JavaScript & TypeScript →===so sánh cả kiểu lẫn giá trị.==ép kiểu trước khi so, nên cho ra kết quả khó đoán:"0" == 0làtrue,[] == falsecũngtrue.- Các giá trị falsy:
false,0,-0,0n,"",null,undefined,NaN. Mọi thứ khác là truthy, kể cả[],{}và"0". NaN === NaNlàfalse; dùngNumber.isNaN(x).- Ngoại lệ hay được chấp nhận:
x == nullđể bắt cảnulllẫnundefined.
Hay sai: viết
const page = input || 1. Khi người dùng nhập0, giá trị bị thay bằng1. Dùng nullish coalescinginput ?? 1để chỉ thay khi lànullhoặcundefined.- Các giá trị falsy:
06Props và state khác nhau thế nào? Vì sao không được sửa trực tiếp state?ReactJS · Junior
Gợi ý trả lời
Ôn thêm câu hỏi ReactJS →Props là dữ liệu cha truyền xuống, component con chỉ đọc. State là dữ liệu component tự quản lý; đổi state sẽ khiến component render lại.
- React so sánh state cũ và mới bằng tham chiếu (
Object.is). Sửa trực tiếpstate.items.push(x)thì tham chiếu không đổi, React có thể không render lại, và các tối ưu nhưmemocũng hỏng. - Luôn tạo object hoặc array mới:
setItems(prev => [...prev, newItem]); setUser(prev => ({ ...prev, name: "An" }));setStatekhông cập nhật biến ngay trong lần render hiện tại. React gom nhiều lần set lại (batching) rồi mới render.
Hay sai: gọi
setCount(count + 1)ba lần liên tiếp và mong tăng 3. Kết quả chỉ tăng 1; muốn tăng 3 phải dùng dạng hàmsetCount(c => c + 1).- React so sánh state cũ và mới bằng tham chiếu (
07Trong 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.
08Vì 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
09Kotlin 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:
10Vì sao bạn muốn rời công ty hiện tại?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á →Trả lời thật nhưng hướng về phía trước: nói điều bạn đang tìm kiếm, không kể xấu nơi cũ.
Lý do chấp nhận được: muốn thử thách mới, muốn làm sản phẩm, dự án kết thúc, công ty tái cấu trúc, thu nhập chưa tương xứng (nói khéo).
Câu mẫu: "Em đã làm 2 năm ở dự án outsourcing, học được nhiều về quy trình và làm việc với khách nước ngoài. Giờ em muốn gắn bó lâu dài với một sản phẩm để thấy được tác động của những gì mình làm, và vị trí bên mình đúng hướng đó."
Hay sai: chê sếp, chê đồng nghiệp, kể chuyện drama. Người phỏng vấn sẽ nghĩ bạn cũng sẽ nói về công ty họ như vậy. Nếu nhảy việc nhiều, chuẩn bị lời giải thích cho từng lần.
Bạn đã phỏng vấn ở CÔNG TY TNHH DU MỤC?
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