Việc làm

28/09 · Hà Nội · Tại văn phòng

Senior Mobile Engineer Collaborator (IOS/Android)

One Mount Group · Mobile Application Developer

Xem mô tả & ứng tuyển trên ITviecMô tả công việc, lương và ứng tuyển nằm ở ITviec.

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), C/C++ & Embedded). 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.

  1. 01Kotlin coroutines: suspend function, scope, dispatcher và structured concurrency là gì?Mobile (Android, iOS, Flutter)

    Gợi ý trả lời

    Coroutine là tác vụ nhẹ có thể tạm dừng (suspend) mà không chặn thread. suspend fun chỉ gọi được từ coroutine hoặc từ suspend function khác.

    • Scope quyết định vòng đời: viewModelScope, lifecycleScope tự 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 GlobalScope gây rò rỉ; bắt CancellationException trong catch (e: Exception) mà không ném lại, làm hỏng cơ chế huỷ.

    Ôn thêm câu hỏi Mobile (Android, iOS, Flutter) →
  2. 02Swift concurrency: async/await, Task, actor và @MainActor hoạt động thế nào?Mobile (Android, iOS, Flutter)

    Gợi ý trả lời

    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 let và TaskGroup chạy song song có cấu trúc. Task hỗ trợ huỷ hợp tác qua Task.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ải await.
    • @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.

    Ôn thêm câu hỏi Mobile (Android, iOS, Flutter) →
  3. 03So 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

    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.

    Ôn thêm câu hỏi Mobile (Android, iOS, Flutter) →
  4. 04Move semantics và rvalue reference giải quyết vấn đề gì? Rule of 3/5/0 là gì?C/C++ & Embedded

    Gợi ý trả lời

    Move semantics cho phép chuyển tài nguyên (buffer, handle) từ một object tạm hoặc sắp bị bỏ đi sang object khác thay vì sao chép sâu. Rvalue reference T&& nhận các object như vậy; std::move chỉ là phép ép kiểu để cho phép move.

    std::vector<int> a(1'000'000);
    std::vector<int> b = std::move(a);  // chuyển con trỏ, không chép 1 triệu phần tử
    
    • Rule of 3: nếu tự viết destructor, copy constructor hoặc copy assignment thì gần như phải viết đủ cả ba.
    • Rule of 5: thêm move constructor và move assignment.
    • Rule of 0: tốt nhất là không tự viết hàm nào, để các thành viên RAII (vector, unique_ptr) lo.

    Hay sai: dùng lại object sau khi đã move. Object vẫn hợp lệ nhưng ở trạng thái không xác định.

    Ôn thêm câu hỏi C/C++ & Embedded →
  5. 05Viết hàm phục vụ ngắt (ISR) cần lưu ý những gì?C/C++ & Embedded

    Gợi ý trả lời

    ISR phải ngắn và nhanh: ghi nhận sự kiện, lấy dữ liệu tối thiểu, rồi để phần xử lý nặng cho task hoặc vòng lặp chính.

    • Không gọi hàm có thể block (delay, chờ mutex), không printf, tránh cấp phát động.
    • Biến dùng chung với code chính khai báo volatile và truy cập atomic, hoặc tắt ngắt ngắn khi đọc dữ liệu nhiều byte.
    • Xoá cờ ngắt đúng cách để không bị gọi lại liên tục.
    • Với RTOS, chỉ dùng API dành cho ngắt (vd. hàm có hậu tố FromISR trong FreeRTOS) để báo task qua semaphore, queue hoặc task notification.
    • Dùng ring buffer cho dữ liệu đến liên tục như UART.

    Người phỏng vấn muốn nghe: bạn biết về độ ưu tiên ngắt, ngắt lồng nhau, và cách đo thời gian chạy ISR bằng cách bật tắt một chân GPIO rồi xem trên oscilloscope.

    Ôn thêm câu hỏi C/C++ & Embedded →
  6. 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

    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 (key trong LazyColumn, ValueKey trong Flutter, Identifiable trong SwiftUI) để không tạo lại item không cần thiết.
    • Cập nhật một phần: DiffUtil / ListAdapter thay vì notifyDataSetChanged.
    • Compose: state ổn định để tránh recompose thừa; Flutter: dùng ListView.builder và const widget.
    • 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.

    Ôn thêm câu hỏi Mobile (Android, iOS, Flutter) →
  7. 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

    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/Context trong 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ữ self trong khi self giữ closure, delegate khai báo strong. Dùng [weak self], weak var delegate. Phát hiện bằng Memory Graph Debugger và Instruments (Leaks, Allocations).
    • Flutter: quên dispose controller, 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.

    Ôn thêm câu hỏi Mobile (Android, iOS, Flutter) →
  8. 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

    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, @Query hoặ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.

    Ôn thêm câu hỏi Mobile (Android, iOS, Flutter) →
  9. 09RTOS khác vòng lặp super-loop thế nào? Mutex khác semaphore ra sao?C/C++ & Embedded

    Gợi ý trả lời

    Super-loop chạy tuần tự các việc trong một vòng while(1), đơn giản nhưng một việc chậm sẽ làm trễ mọi việc khác. RTOS có scheduler preemptive theo độ ưu tiên, giúp các việc cần thời gian thực được đáp ứng đúng hạn.

    • Mutex: khoá loại trừ cho tài nguyên dùng chung, có khái niệm sở hữu (ai khoá thì người đó mở) và thường có priority inheritance để giảm đảo ưu tiên.
    • Binary semaphore: dùng để báo hiệu giữa ISR và task hoặc giữa các task, không có sở hữu.
    • Counting semaphore: quản lý N tài nguyên giống nhau.

    Hay sai: dùng semaphore thay mutex để bảo vệ tài nguyên, gặp priority inversion: task ưu tiên cao chờ task thấp đang giữ khoá, trong khi task trung bình chiếm CPU.

    Người phỏng vấn muốn nghe: bạn biết đặt độ ưu tiên task và kích thước stack hợp lý, và từng phát hiện stack overflow.

    Ôn thêm câu hỏi C/C++ & Embedded →
  10. 10Bạn hãy giới thiệu ngắn về bản thân.Câu hỏi hành vi & văn hoá

    Gợi ý trả lời

    Đây là câu mở đầu gần như chắc chắn có. Dùng công thức Hiện tại – Quá khứ – Tương lai, gói trong khoảng 1–2 phút.

    • Hiện tại: vị trí, số năm kinh nghiệm, mảng/công nghệ chính.
    • Quá khứ: một hai dự án hoặc thành tích liên quan nhất tới vị trí đang tuyển.
    • Tương lai: vì sao bạn quan tâm tới vị trí này.

    Câu mẫu: "Em là backend developer, khoảng 3 năm kinh nghiệm, chủ yếu làm Java Spring cho hệ thống thanh toán. Ở dự án gần nhất em phụ trách module đối soát và tối ưu các truy vấn báo cáo chạy chậm. Em đang tìm môi trường làm sản phẩm lâu dài để đi sâu hơn về thiết kế hệ thống, nên rất quan tâm tới vị trí này."

    Hay sai: kể từ quê quán, trường cấp ba, hoặc đọc lại nguyên CV.

    Ôn thêm câu hỏi Câu hỏi hành vi & văn hoá →

Bạn đã phỏng vấn ở One Mount Group?

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