Luyện Phỏng Vấn IT — 2000+ Câu Hỏi Phỏng Vấn IT Có Đáp Án 2026

Danh mục

Android iconAndroid

val khai báo biến chỉ đọc, không thể gán lại sau khi khởi tạo (tương tự final trong Java), còn var khai báo biến có thể thay đổi giá trị.

  • Dùng val làm mặc định giúp code an toàn hơn và phù hợp với nguyên tắc immutability trong kiến trúc MVVM.
  • Kotlin compiler khuyến khích dùng val ở mọi nơi có thể.

Trong Kotlin, biến mặc định không được phép null để loại bỏ NullPointerException.

  • Muốn cho phép null, bạn khai báo kiểu nullable bằng ? (ví dụ String?).
  • Compiler bắt buộc bạn xử lý null ngay lúc biên dịch qua các toán tử như ?. (safe call) hoặc !! (ép buộc).
  • Điều này giúp các lỗi liên quan đến null không thể lọt qua bước compile.

Extension function cho phép bạn thêm method mới vào class có sẵn mà không cần kế thừa hay sửa code gốc.

Ví dụ có thể thêm method thẳng vào class String: fun String.isPhoneNumber(): Boolean { return this.length == 10 }.

Data class tự động sinh ra các method boilerplate như equals(), hashCode(), toString()copy().

  • Data class cần ít nhất một tham số trong constructor và rất phù hợp để chứa dữ liệu (như response API hay model database).
  • Chúng không thể là abstract, open, sealed, hay inner class, và tất cả tham số constructor phải được đánh dấu val hoặc var.

Companion object cho phép định nghĩa các thành viên kiểu static (hằng số, factory method) bên trong class, thuộc về class chứ không phải instance.

  • Mỗi class chỉ có một companion object.
  • Có thể gọi trực tiếp mà không cần tạo instance: MyClass.myStaticMethod().
  • Giống static trong Java nhưng linh hoạt hơn vì có thể implement interface.

Activity lifecycle có 6 method chính theo thứ tự: onCreate() (khởi tạo), onStart() (trở nên visible), onResume() (có focus), onPause() (mất focus), onStop() (không còn visible), onDestroy() (dọn dẹp).

  • Hiểu lifecycle rất quan trọng vì bạn phải lưu state trong onSaveInstanceState() và khôi phục trong onCreate().
  • Memory leak thường xảy ra khi giữ strong reference đến Activity quá vòng đời của nó.

Activity là component độc lập chiếm toàn màn hình, quản lý vòng đời UI cho một màn hình.

  • Fragment là component UI nhẹ hơn, có thể tái sử dụng, và luôn tồn tại trong một Activity — có thể swap vào/ra tùy ý.
  • Fragment giúp xây dựng UI linh hoạt thích ứng với nhiều kích thước màn hình, còn Activity đại diện cho các màn hình riêng biệt trong app.

Intent là object nhắn tin để yêu cầu một hành động từ component khác trong app.

  • Explicit intent chỉ định chính xác component cần khởi động (ví dụ mở một Activity cụ thể trong app).
  • Implicit intent khai báo action mà không chỉ định component, để hệ thống tìm app phù hợp xử lý (ví dụ mở URL trong browser).
  • Cả hai loại đều có thể truyền dữ liệu qua bundle.

Normal permission (internet, vibrate) được cấp tự động khi cài đặt.

  • Dangerous permission (camera, location, contacts) phải xin lúc runtime.
  • Để xin runtime permission: tạo ActivityResultContract, dùng registerForActivityResult(), và kiểm tra ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED trước khi dùng tính năng được bảo vệ.
  • Từ Android 6 trở đi, bỏ qua runtime permission sẽ làm app crash.

Intent filter được khai báo trong manifest để chỉ định loại implicit intent mà Activity có thể xử lý.

  • Chúng định nghĩa action, category, và kiểu dữ liệu.
  • Khi bạn gửi implicit intent, hệ thống tìm tất cả app có Intent filter phù hợp.

Ví dụ Intent với action ACTION_VIEW và data http:// có thể mở browser.

Process là một instance riêng biệt của app với bộ nhớ và tài nguyên riêng, chạy độc lập với các process khác.

  • Thread là đơn vị thực thi nhẹ hơn trong một process, chia sẻ bộ nhớ với các thread khác.
  • Android app có main thread (UI thread) xử lý toàn bộ UI, còn background thread xử lý tác vụ nặng.
  • Tuyệt đối không được block main thread.

Jetpack Compose là bộ công cụ UI declarative hiện đại cho phép xây dựng giao diện Android bằng Kotlin function thay vì XML.

  • Nó đang thay thế XML vì code ngắn gọn hơn, type-safe, và dễ tái sử dụng hơn.
  • Compose tự động cập nhật UI khi state thay đổi, loại bỏ boilerplate và giúp UI dễ test hơn.
  • Google đã tuyên bố đây là cách tiếp cận được khuyến nghị cho UI Android.

@Composable đánh dấu một Kotlin function là pure, idempotent (cùng input → cùng output), và được gọi bởi Compose runtime chứ không phải do bạn gọi thủ công — đây là ràng buộc quan trọng nhất.

  • Function phải trả về Unit, không có side effect trực tiếp, và chạy nhanh.
  • Annotation @Composable báo cho Compose runtime theo dõi recomposition và state change.

Modifier là object có thể chain lại để thêm behavior, styling, hay layout cho composable.

Ví dụ: .size(100.dp), .background(Color.Blue), .padding(16.dp), .clickable { }, .weight(1f). Thứ tự quan trọng vì modifier chain từ trái sang phải: .padding().background() khác với .background().padding(). Modifier là lập trình hàm ứng dụng tốt nhất, kết hợp các behavior nhỏ thành behavior phức tạp.

ViewModel lưu trữ dữ liệu UI theo cách lifecycle-aware, tồn tại qua configuration change như xoay màn hình.

  • Nó không giữ reference đến View (ngăn memory leak) và là nơi lý tưởng để đặt business logic, gọi API, và quản lý state.
  • Tạo ViewModel mỗi màn hình bằng viewModel() trong Compose hoặc ViewModelProvider trong Activity/Fragment.
  • Tuyệt đối không truyền Activity hay View vào ViewModel.

Room là lớp trừu tượng type-safe trên SQLite, tự động sinh code lúc compile, ngăn SQL injection và lỗi runtime.

  • Bạn định nghĩa entity (data class), DAO (database access object với query), và Database class.
  • Room hỗ trợ LiveData/Flow để quan sát thay đổi database.
  • An toàn và dễ dùng hơn SQLite thô rất nhiều, và tích hợp tốt với coroutine và Compose.

DAO là interface với các method cho thao tác CRUD trên database. Bạn annotate method với @Query, @Insert, @Update, @Delete. Room sinh implementation lúc compile.

Ví dụ: @Query("SELECT * FROM users WHERE id = :id") suspend fun getUser(id: Int): User. DAO là nơi định nghĩa query, và dùng suspend function giúp query tự động chạy trên background thread.

Retrofit là HTTP client type-safe chuyển REST API thành Kotlin interface.

  • Bạn định nghĩa interface với method được annotate bằng HTTP verb như @GET, @POST, thêm kiểu request/response, và Retrofit tự sinh implementation.
  • Nó tự động deserialize JSON response thành Kotlin object, xử lý header, và hỗ trợ cả callback lẫn coroutine.
  • OkHttp là thư viện networking nền mà Retrofit dùng.

Sealed class giới hạn một class hierarchy thành tập hợp subclass cố định, được định nghĩa trong cùng một file hoặc package.

  • Rất phù hợp để biểu diễn các trạng thái giới hạn như response API (Success/Error/Loading).
  • Khi dùng với when, compiler bắt buộc xử lý hết tất cả subclass, không cần nhánh else, giúp code an toàn hơn rất nhiều.

Scope function thực thi một block code trên một object trong một scope tạm thời. let chạy code chỉ khi object khác null (trả về kết quả lambda). run giống let nhưng dùng this thay vì tham số. apply cấu hình object và trả về chính object đó (dùng khi khởi tạo). also giống apply nhưng truyền object vào làm tham số. with giống run nhưng cú pháp khác.

Chọn loại dựa vào nhu cầu null-safety, giá trị trả về, hay cách tham chiếu object.

Coroutine là các hàm nhẹ có thể tạm dừng và tiếp tục, cho phép lập trình bất đồng bộ hiệu quả mà không block thread.

  • Khác với callback dễ dẫn đến "callback hell", coroutine cho phép viết code bất đồng bộ trông như code tuần tự, dễ đọc hơn nhiều.
  • Một thread có thể chạy hàng nghìn coroutine nhờ cơ chế suspension, tiết kiệm bộ nhớ và tăng hiệu suất đáng kể.

launch là coroutine fire-and-forget, trả về Job và không trả về kết quả. async dùng khi cần thực hiện tác vụ và lấy kết quả về, trả về Deferred.

  • Dùng launch cho tác vụ độc lập (như cập nhật UI), còn async khi cần kết quả trả về (như fetch data từ network).
  • Phải gọi await() trên Deferred để lấy kết quả.

Suspend function là hàm được đánh dấu bằng từ khóa suspend, có thể bị tạm dừng và tiếp tục bởi coroutine.

  • Chúng chỉ có thể được gọi từ suspend function khác hoặc trong một coroutine scope.
  • Suspend function là nền tảng của Kotlin coroutines, cho phép viết code bất đồng bộ theo kiểu tuần tự.

Ví dụ điển hình là bất kỳ hàm nào được đánh dấu suspend bên trong viewModelScope.launch {}.

Bạn có thể dùng try-catch thông thường bên trong coroutine để xử lý exception cục bộ.

  • Với exception không được bắt, dùng CoroutineExceptionHandler để bắt ở cấp coroutine scope. viewModelScope dùng SupervisorJob nên coroutine con thất bại không hủy coroutine anh em, nhưng exception không được bắt sẽ KHÔNG tự động được log — chúng sẽ crash coroutine đó trong im lặng trừ khi bạn thêm CoroutineExceptionHandler hoặc try-catch.
  • Structured concurrency đảm bảo exception trong coroutine con được truyền lên đúng cách cho scope cha.

Destructuring cho phép bạn tách một object thành các biến riêng lẻ trong một câu lệnh duy nhất.

Ví dụ val (name, age) = person trích xuất thuộc tính từ data class.

Fragment lifecycle phức tạp hơn Activity: onAttach(), onCreate(), onCreateView(), onViewCreated(), onStart(), onResume(), onPause(), onStop(), onDestroyView(), onDestroy(), onDetach().

  • Điểm khác biệt quan trọng là View của Fragment có thể bị destroy (onDestroyView) trong khi Fragment vẫn còn trong bộ nhớ.
  • Vì vậy phải dọn dẹp View references trong onDestroyView() để tránh memory leak.

replace() xóa Fragment hiện tại và thêm Fragment mới vào chỗ đó — khi back thì Fragment bị replace sẽ được tạo lại. add() giữ Fragment hiện có và chồng Fragment mới lên trên — khi back thì quay về Fragment cũ.

Dùng replace() để điều hướng giữa các màn hình, còn add() cho overlay như dialog hay bottom sheet.

Service là component chạy nền không có tương tác người dùng, thích hợp cho các tác vụ chạy lâu như phát nhạc, tải file, hay theo dõi vị trí.

  • Service không có UI nhưng chạy trên main thread mặc định, nên cần offload tác vụ nặng sang background thread hoặc coroutine.
  • Foreground service hiển thị notification liên tục và dùng cho tác vụ nền ưu tiên cao.

BroadcastReceiver là component lắng nghe các broadcast trong toàn hệ thống (như pin yếu, thay đổi network, hay broadcast tùy chỉnh).

  • Bạn đăng ký nó trong manifest (static) hoặc trong code (dynamic), và nó kích hoạt onReceive() khi nhận được broadcast phù hợp.
  • Tránh thực hiện tác vụ nặng trong onReceive() vì phải hoàn thành nhanh — dùng WorkManager cho tác vụ dài.

ContentProvider là lớp trừu tượng cung cấp quyền truy cập có kiểm soát vào dữ liệu của app cho các app hoặc process khác.

  • Nó dùng URI để xác định resource và implement các thao tác CRUD qua query(), insert(), update(), delete().
  • Danh bạ, lịch, và ảnh đều dùng ContentProvider.
  • Chúng đảm bảo tính nhất quán dữ liệu, bảo mật qua permission, và cho phép chia sẻ dữ liệu qua ranh giới app.

onSaveInstanceState() là callback để lưu trạng thái UI tạm thời (như vị trí scroll) trước khi activity bị destroy do configuration change.

  • Bundle này được truyền vào onCreate() hoặc onRestoreInstanceState().
  • Khác với lưu trữ lâu dài như SharedPreferences hay database.
  • Dùng nó cho trạng thái tạm thời dễ lưu hơn là phải tạo lại UI từ đầu.

mutableStateOf() tạo một state object có thể quan sát, kích hoạt recomposition khi thay đổi. remember lưu cache giá trị state qua các lần recomposition để không bị tạo lại mỗi lần.

Kết hợp lại: `val count = remember { mutableStateOf

  1. } hoặc ngắn gọn hơn là var count by remember { mutableStateOf
  2. }. Khi count` thay đổi, chỉ các composable đang đọc nó mới recompose, không phải cả màn hình

remember giữ state qua các lần recomposition trong scope của composable nhưng mất khi configuration change như xoay màn hình. rememberSaveable persist state qua configuration change bằng cách tự động lưu vào Bundle.

Dùng rememberSaveable cho dữ liệu người dùng nhập (form fields) và remember cho state UI tạm thời (animation values). rememberSaveable có overhead cao hơn một chút.

State hoisting nghĩa là chuyển state từ composable con lên composable cha, làm cho composable con trở nên stateless.

  • Composable stateless nhận state qua tham số và callback để cập nhật state, giúp tái sử dụng trên nhiều màn hình.

Ví dụ hoist state của TextField lên cha để nhiều composable có thể đọc.

LaunchedEffect là side-effect function khởi chạy một coroutine scope gắn với lifecycle của composable. Dùng cho các tác vụ một lần như load data, animation, hay analytics. Nó chạy khi key parameter thay đổi và hủy khi composable rời khỏi composition.

Ví dụ: LaunchedEffect(userId) { loadUserData(userId) } load lại data mỗi khi userId thay đổi.

Dùng Compose Navigation library với NavControllerNavHost.

  • Định nghĩa NavGraph với các màn hình và route, rồi dùng navController.navigate(route) để điều hướng.
  • State được giữ lại khi quay về màn hình trước.
  • Với Compose Navigation 2.8+ (stable từ Oct 2024), dùng type-safe routes qua @Serializable thay vì string-based routes. BackHandler cho phép tùy chỉnh hành vi nút back.

Dùng MaterialTheme (Material 3) hoặc CompositionLocal tùy chỉnh để tập trung hóa các thuộc tính theme.

  • Định nghĩa màu sắc, typography, và shape trong theme object, dùng CompositionLocalProvider để cung cấp chúng cho cây composable.
  • Wrap app trong MaterialTheme(colorScheme = colorScheme, typography = typography) { App() } — lưu ý tham số là colorScheme (Material 3), không phải colors (Material 2).
  • Hỗ trợ dark/light mode qua isSystemInDarkTheme().

MVVM (Model-View-ViewModel) tách UI logic khỏi business logic: Model chứa dữ liệu và business rule, View hiển thị UI (Activity/Fragment/Composable), ViewModel chứa UI state và logic, tồn tại qua configuration change.

  • ViewModel expose LiveData/StateFlow để View observe.
  • Sự tách biệt này cải thiện khả năng test (test ViewModel không cần UI), tái sử dụng, và bảo trì.
  • Hầu hết app Android ngày nay đều dùng MVVM.

Repository pattern trừu tượng hóa các nguồn dữ liệu (local database, remote API, cache) sau một interface duy nhất, để ViewModel không cần biết dữ liệu đến từ đâu.

  • Sự decoupling này giúp test dễ hơn (mock repository) và cho phép đổi nguồn dữ liệu mà không cần sửa ViewModel.
  • Repository điều phối giữa dữ liệu local và remote, implement caching strategy, và là single source of truth.

StateFlow là lựa chọn ưu tiên cho code mới (2025) — tích hợp tự nhiên với coroutine, luôn có value non-null có thể đọc an toàn, và hoạt động tốt với Compose.

  • LiveData lifecycle-aware và tự động unsubscribe nhưng đang ở chế độ maintenance.
  • StateFlow yêu cầu initial value; LiveData thì không.
  • Khi dùng StateFlow trong Fragment, observe trong repeatOnLifecycle để tránh memory leak.

Dependency injection nghĩa là cung cấp dependency của một object qua tham số thay vì object tự tạo bên trong.

Lợi ích: test dễ hơn (truyền mock dependency), loose coupling, linh hoạt khi đổi implementation.

Ví dụ: thay vì val database = Database.getInstance() bên trong class, hãy truyền vào: class MyClass(val database: Database). Code modular và testable hơn nhiều.

Hilt là thư viện DI được xây trên Dagger, thiết kế riêng cho Android.

  • Nó loại bỏ boilerplate bằng cách cung cấp module có sẵn cho Android component.
  • Đánh dấu Application với @HiltAndroidApp, Activity/Fragment với @AndroidEntryPoint, và inject bằng @Inject.
  • Hilt tự động quản lý scope (như @Singleton) và tích hợp liền mạch với ViewModel và composable.

Hilt scope kiểm soát thời gian sống của dependency instance: @Singleton sống suốt vòng đời app, @ActivityScoped theo vòng đời Activity, @ViewModelScoped theo ViewModel, và @FragmentScoped theo Fragment.

  • Scope mặc định là unscoped (tạo instance mới mỗi lần).
  • Chọn scope dựa vào thời gian muốn tái sử dụng instance. @Singleton phổ biến nhất nhưng dùng loại khác để quản lý bộ nhớ tốt hơn.

Interceptor chặn HTTP request/response để chỉnh sửa chúng. Application interceptor chạy một lần cho mỗi request, còn network interceptor chạy cho mỗi lần gọi network thực tế. Dùng phổ biến: thêm auth header, log request/response, xử lý token refresh, cache.

Ví dụ: OkHttpClient().addInterceptor { chain -> chain.proceed(chain.request().newBuilder().addHeader("Authorization", token).build()) }.

Dùng Interceptor để thêm auth header vào mọi request.

  • Khi token hết hạn, interface Authenticator xử lý token refresh bằng cách chặn response 401 và retry với token mới.
  • Lưu token an toàn trong EncryptedSharedPreferences.
  • Gửi token trong Authorization header: Authorization: Bearer token.
  • Xử lý token hết hạn một cách graceful để người dùng không thấy lỗi.

DataStore là sự thay thế hiện đại cho SharedPreferences, được xây dựng trên coroutine và Flow cho lưu trữ dữ liệu type-safe và transactional.

  • Async-first (không blocking), hỗ trợ protocol buffer cho type safety, và an toàn hơn SharedPreferences vốn có vấn đề threading.
  • Dùng context.dataStore.data.map { it.setting } để đọc và context.dataStore.updateData { it.copy(setting = value) } để ghi.

WorkManager lên lịch background work có thể defer được, tồn tại qua app restart và system reboot.

  • Nó tự động xử lý Doze Mode và App Standby, chọn cơ chế scheduling tốt nhất (JobScheduler, BroadcastReceiver).
  • Dùng cho tác vụ đáng tin cậy, không khẩn cấp như sync data, upload file, hay báo cáo định kỳ.
  • Tạo Worker subclass, dùng OneTimeWorkRequest hoặc PeriodicWorkRequest, và enqueue với WorkManager.enqueue().

Paging 3 là thư viện phân trang được khuyến nghị, load hiệu quả dataset lớn từ nguồn local/remote.

  • Bạn tạo PagingSource (hoặc RemoteMediator cho network+database), dùng Pager để tạo paginated stream, và collect PagingData trong UI với collectAsLazyPagingItems().
  • Nó xử lý tự động loading, appending, retry, và deduplication, loại bỏ lỗi phân trang thủ công.

Observer pattern cho phép các object (observer) đăng ký nhận thông báo về thay đổi trạng thái của object khác (subject).

  • Trong Android, LiveData/StateFlow là các implementation của pattern này.
  • Khi bạn gọi observe() hoặc collect(), bạn đang đăng ký làm observer.
  • Khi dữ liệu thay đổi, tất cả observer được thông báo, decoupling nguồn dữ liệu khỏi UI.

Configuration change destroy và recreate Activity/Fragment.

  • Bảo toàn state bằng: SavedInstanceState (UI state tạm thời), ViewModel (logic và data), và SharedPreferences/DataStore (data lâu dài).
  • Không nên dùng android:configChanges để tắt recreation trừ khi thực sự cần thiết.
  • Trong Compose, state tự động tồn tại qua rotation nếu dùng rememberSaveable.
  • State management đúng cách giúp rotation trong suốt với người dùng.