onResume — Cơ bản, tương tác người dùng trong Android

Tác giả: IT Sectr Đã đăng: 2026-03-04 Thời gian đọc: 10 phút

onResume là một phương thức trong vòng đời Android được gọi khi Activity hoặc Fragment ra tiền cảnh và nhận được tiêu điểm đầu vào. Ở trạng thái này, màn hình sẵn sàng tương tác với người dùng: tất cả các sự kiện chạm, nhấn phím và cử chỉ được chuyển đến thành phần này. onResume là trạng thái làm việc của Activity, nơi ứng dụng dành phần lớn thời gian. Chính tại đây, bạn mở camera, bắt đầu phát video, bắt đầu nhận dạng giọng nói và đăng ký trình lắng nghe cảm biến yêu cầu quyền truy cập độc quyền. Để biết thêm về vòng đời Activity đầy đủ, hãy đọc bài viết Activity Lifecycle.

Những điểm chính

  • onResume — Activity ở tiền cảnh với tiêu điểm đầu vào; được gọi sau onStart hoặc sau khi quay lại từ hộp thoại
  • Tài nguyên độc quyền — camera, micrô, ghi hình được mở trong onResume và đóng trong onPause
  • Cặp onResume/onPause — tài nguyên yêu cầu tiêu điểm đầy đủ được quản lý bằng cặp này; đăng ký trong onResume, giải phóng trong onPause
  • onResume so với onStart — onStart = khả năng hiển thị, onResume = tương tác; hộp thoại ghi đè onResume nhưng không ghi đè onStart
  • Thời gian — onResume phải nhanh; các thao tác dài ở đây làm chậm phản hồi giao diện
  • Fragment.onResume — được gọi sau Activity.onResume, khi Fragment sẵn sàng tương tác
  • onResume trong Jetpack — lifecycleScope và LiveData sử dụng onResume để quản lý đăng ký tự động

Cơ bản về phương thức onResume trong Android

onResume — phương thức thứ ba trong vòng đời Activity, được gọi sau onStart, báo hiệu rằng màn hình đã sẵn sàng tương tác đầy đủ với người dùng. Tại thời điểm này, Activity ở đầu ngăn xếp tác vụ (back stack), hệ thống chuyển tất cả các sự kiện đầu vào cho nó, và ứng dụng có thể bắt đầu bất kỳ thao tác nào yêu cầu sự tham gia tích cực của người dùng: gọi video, trò chơi, ghi âm, vẽ trên Canvas.

onResume là một phần của “thời gian tồn tại ở tiền cảnh” (foreground lifetime) — khoảng thời gian giữa onResume và onPause. Đây là giai đoạn hoạt động tích cực nhất của Activity, khi ứng dụng tiêu thụ nhiều tài nguyên nhất: CPU để xử lý chạm, GPU để kết xuất hoạt ảnh, camera và micrô để ghi hình. Hiểu được cấp độ vòng đời này rất quan trọng để tối ưu hóa mức tiêu thụ năng lượng — các tài nguyên được mở trong onResume phải được đóng ngay lập tức trong onPause.

Theo Google I/O 2025, thời gian trung bình mà Activity dành ở trạng thái onResume mỗi phiên là 2–5 phút cho ứng dụng tin tức và 15–30 phút cho trò chơi và ứng dụng nhắn tin. Tất cả thời gian còn lại, Activity ở trạng thái onPause, onStop hoặc onDestroy. Điều này có nghĩa là tối ưu hóa mã onResume cụ thể mang lại lợi ích lớn nhất về hiệu suất và thời lượng pin.

onResume trong Activity

Trong Activity, phương thức onResume được gọi mỗi khi màn hình nhận được tiêu điểm đầu vào — khi khởi chạy lần đầu, khi quay lại từ Activity khác, khi đóng hộp thoại, khi mở khóa thiết bị. Đây là phương thức “nóng” có thể được gọi nhiều lần trong một phiên và việc triển khai nó phải nhẹ nhàng nhất có thể.

kotlin
class CameraActivity : AppCompatActivity() {
    private var cameraProvider: ProcessCameraProvider? = null
    private var preview: Preview? = null

    override fun onResume() {
        super.onResume()
        val cameraProviderFuture = ProcessCameraProvider.getInstance(this)
        cameraProviderFuture.addListener({
            cameraProvider = cameraProviderFuture.get()
            val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
            preview = Preview.Builder().build().also {
                it.setSurfaceProvider(binding?.viewFinder?.surfaceProvider)
            }
            try {
                cameraProvider?.unbindAll()
                cameraProvider?.bindToLifecycle(
                    this, cameraSelector, preview
                )
            } catch (e: Exception) {
                Log.e("Camera", "Failed to bind camera", e)
            }
        }, ContextCompact.getMainExecutor(this))
    }

    override fun onPause() {
        super.onPause()
        cameraProvider?.unbindAll()
        preview = null
    }
}

Ví dụ về CameraX minh họa cách sử dụng cổ điển của onResume/onPause: camera là tài nguyên độc quyền chỉ một ứng dụng có thể sử dụng tại một thời điểm. Ràng buộc camera với vòng đời qua bindToLifecycle tự động đóng camera trong onPause, nhưng lệnh gọi unbindAll rõ ràng đảm bảo giải phóng ngay lập tức. Điều này đặc biệt quan trọng khi chuyển đổi giữa các Activity: camera phải được giải phóng trước khi Activity khác cố gắng mở nó.

onResume trong Fragment

onResume trong Fragment được gọi sau khi Activity chứa nó đã nhận được onResume. Tuy nhiên, do đặc thù của FragmentManager và ViewPager, thời điểm gọi onResume cho Fragment có thể bị trễ so với Activity. Ví dụ: Fragment trong ViewPager với offscreenPageLimit = 1 chỉ nhận được onResume khi nó trở thành trang hiện tại, không phải khi Activity khởi động.

kotlin
class VideoPlayerFragment : Fragment() {
    private var exoPlayer: ExoPlayer? = null

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        exoPlayer = ExoPlayer.Builder(requireContext()).build()
        binding?.playerView?.player = exoPlayer
    }

    override fun onResume() {
        super.onResume()
        exoPlayer?.play()
        if (userVisibleHint) {
            startBiometricAuth()
        }
    }

    override fun onPause() {
        exoPlayer?.pause()
        stopBiometricAuth()
        super.onPause()
    }
}

Kiểm tra userVisibleHint trong Fragment.onResume có liên quan đến ViewPager: Fragment có thể nhận được onResume nhưng bị ẩn bởi một trang lân cận (ví dụ: trong quá trình chuyển đổi hoạt ảnh). Trong những trường hợp như vậy, việc bắt đầu video hoặc sinh trắc học trong onResume mà không kiểm tra khả năng hiển thị sẽ dẫn đến hành vi không mong muốn. Bắt đầu từ Fragment 1.5.0, nên sử dụng FragmentTransaction.setMaxLifecycle() để kiểm soát chính xác vòng đời của các fragment trong ViewPager2.

onResume so với onStart: khi nào sử dụng cái gì

Các nhà phát triển thường nhầm lẫn onStart và onResume, đặt mã vào phương thức sai. Quy tắc chính: onStart — cho tài nguyên hoạt động trong khi hiển thị; onResume — cho tài nguyên yêu cầu tiêu điểm đầu vào. Hãy xem các tình huống cụ thể và lựa chọn phương thức chính xác.

Thao tácPhương thứcCơ sở
Đăng ký định vị địa lýonStart / onStopGPS có thể hoạt động với khả năng hiển thị một phần
Mở cameraonResume / onPauseCamera là tài nguyên độc quyền
BroadcastReceiveronStart / onStopSự kiện hệ thống không yêu cầu tiêu điểm
Phát videoonResume / onPauseVideo phải hiển thị với người dùng
Quét BluetoothonStart / onStopQuét có thể chạy nền
Máy ghi âm (MediaRecorder)onResume / onPauseGhi âm yêu cầu giao diện hoạt động
Trình lắng nghe cảm biếnonResume / onPauseCảm biến cho trò chơi và cử chỉ
Cập nhật dữ liệuonStartCần dữ liệu mới khi xuất hiện

Quy tắc thực tế: nếu một thao tác nên bị gián đoạn khi hộp thoại xuất hiện — hãy sử dụng onResume/onPause. Nếu một thao tác có thể tiếp tục khi màn hình bị che một phần — hãy sử dụng onStart/onStop. Ví dụ: trình phát video nên tạm dừng video khi mở hộp thoại (onPause), trong khi định vị địa lý có thể tiếp tục cập nhật (vẫn ở onStart).

Quản lý tài nguyên độc quyền

Tài nguyên độc quyền là các thành phần thiết bị chỉ một ứng dụng có thể sử dụng tại một thời điểm nhất định. Camera, micrô, đầu ra video (MediaProjection), bộ chuyển đổi NFC ở chế độ đọc, thiết bị USB ở chế độ phụ kiện — tất cả các tài nguyên này phải được mở trong onResume và giải phóng trong onPause.

Làm việc với MediaRecorder

MediaRecorder được sử dụng để ghi âm và ghi hình. Yêu cầu quyền và chuẩn bị MediaRecorder được thực hiện trong onCreate, trong khi bắt đầu ghi trong onResume. Nếu người dùng chuyển sang ứng dụng khác, onPause tạm dừng ghi và onResume tiếp tục. Đây là hành vi tiêu chuẩn cho máy ghi âm và ứng dụng quay video.

kotlin
private var mediaRecorder: MediaRecorder? = null
private var isRecording = false

override fun onResume() {
    super.onResume()
    if (isRecording) {
        mediaRecorder?.resume()
    }
}

override fun onPause() {
    if (isRecording) {
        mediaRecorder?.pause()
    }
    super.onPause()
}

BiometricPrompt và onResume

Xác thực sinh trắc học (BiometricPrompt) chỉ nên được gọi khi Activity ở trong onResume. Nếu gọi nó trong onCreate hoặc onStart, hộp thoại sinh trắc học có thể xuất hiện trước khi Activity hoàn tất khởi tạo, dẫn đến xử lý kết quả không chính xác. Gọi nó trong onResume đảm bảo cửa sổ sinh trắc học được hiển thị trong ngữ cảnh chính xác.

Mẫu và khuyến nghị

Hãy xem ba mẫu đã được kiểm chứng để làm việc với onResume được sử dụng trong các dự án thương mại: đặt lại bộ đếm thời gian không hoạt động, cập nhật dữ liệu hiển thị và tích hợp với Jetpack Navigation.

Đặt lại bộ đếm thời gian không hoạt động

Trong các ứng dụng có dữ liệu nhạy cảm (ngân hàng, hồ sơ y tế), onResume được sử dụng để đặt lại bộ đếm thời gian tự động đăng xuất. Nếu người dùng đang tương tác tích cực với ứng dụng, onResume được gọi ở mỗi lần chuyển màn hình và bộ đếm được đặt lại. Nếu người dùng thu nhỏ ứng dụng, onPause dừng bộ đếm và onResume khi quay lại sẽ đặt lại bộ đếm hoặc yêu cầu xác thực lại.

Cập nhật dữ liệu khi quay lại

Danh sách cần hiển thị dữ liệu cập nhật mỗi khi quay lại màn hình được cập nhật trong onResume. Ví dụ: nếu người dùng đã tạo một mục mới trong Activity khác và điều hướng trở lại, onResume tải lại danh sách từ cơ sở dữ liệu cục bộ hoặc bộ nhớ đệm ViewModel. Điều này đảm bảo tính nhất quán của dữ liệu mà không cần gọi notifyDataSetChanged thủ công.

kotlin
override fun onResume() {
    super.onResume()
    // ActivityResultLauncher đã trả về kết quả — đang cập nhật danh sách
    viewModel.refreshList()
    // Đặt lại bộ đếm thời gian không hoạt động
    inactivityTimer.reset()
}

Jetpack Navigation và onResume

Trong Jetpack Navigation, onResume của một fragment được gọi mỗi khi bạn quay lại fragment đó thông qua điều hướng quay lại. Thuộc tính này được sử dụng để đặt lại trạng thái giao diện: ẩn bàn phím, xóa trường tìm kiếm, cập nhật tiêu đề thanh công cụ. OnBackPressedCallback kết hợp với onResume mang lại toàn quyền kiểm soát điều hướng mà không trùng lặp mã.

Các câu hỏi thường gặp

Sự khác biệt giữa onResume và onStart bằng những từ đơn giản là gì?

onStart — màn hình hiển thị. onResume — màn hình hoạt động và sẵn sàng tương tác. Hãy tưởng tượng: bạn đang xem TV (onStart), nhưng bạn cầm điều khiển từ xa (onResume). TV luôn hiển thị, nhưng tương tác chỉ bắt đầu với điều khiển từ xa. Nếu ai đó che TV bằng rèm — màn hình không còn hiển thị (onStop). Nếu ai đó lấy điều khiển từ xa khỏi bạn — tương tác dừng lại (onPause), nhưng TV vẫn hiển thị.

onResume được gọi thường xuyên như thế nào?

onResume được gọi mỗi khi Activity nhận được tiêu điểm đầu vào. Tối thiểu là một lần (khi khởi chạy). Tối đa phụ thuộc vào các tình huống sử dụng: chuyển đổi giữa các màn hình, mở hộp thoại, khóa và mở khóa thiết bị nhanh — mỗi tình huống như vậy gọi onResume khi quay lại màn hình.

Tại sao onResume là nơi tốt nhất để mở camera?

Camera là tài nguyên độc quyền chỉ có sẵn cho một ứng dụng tại một thời điểm. Nếu bạn mở camera trong onCreate hoặc onStart, nó sẽ vẫn bị khóa đối với các ứng dụng khác ngay cả khi ứng dụng của bạn không hoạt động. onResume đảm bảo camera chỉ mở khi Activity ở tiền cảnh và onPause đóng nó ngay lập tức. Đây là tiêu chuẩn phát triển Android, được thiết lập trong tài liệu CameraX và Camera2 API.

onResume có thể không được gọi sau onStart không?

Có, onResume có thể không xảy ra nếu một Activity bị che bởi một Activity khác ngay sau khi xuất hiện. Ví dụ: Activity A khởi động Activity B trong phương thức onCreate hoặc onStart. Trong trường hợp này, A nhận onStart → onPause → onStop, bỏ qua onResume. Hệ thống không gọi onResume vì Activity A chưa bao giờ nhận được tiêu điểm đầu vào.

Không nên làm gì trong onResume?

Trong onResume, không nên thực hiện các thao tác đồng bộ dài: tải dữ liệu lớn từ mạng, truy vấn SQL phức tạp, xử lý hình ảnh. onResume chạy trong luồng giao diện và bất kỳ sự chặn nào lâu hơn 100–200 ms đều gây ra độ trễ phản hồi giao diện. Tất cả các thao tác nặng phải không đồng bộ — thông qua coroutines, RxJava hoặc WorkManager. Cũng không nên gọi finish() trong onResume mà không kiểm tra — điều này có thể dẫn đến vòng lặp vô hạn tạo lại.

Tóm tắt

  • onResume — trạng thái tiền cảnh với tiêu điểm đầu vào; Activity sẵn sàng tương tác với người dùng
  • Tài nguyên độc quyền — camera, micrô, ghi hình được mở trong onResume và đóng trong onPause
  • onResume so với onStart — onStart cho tài nguyên hiển thị, onResume cho tài nguyên hoạt động; hộp thoại làm gián đoạn onResume nhưng không làm gián đoạn onStart
  • Hiệu suất — onResume phải nhẹ; tất cả thao tác nặng không đồng bộ
  • Fragment.onResume — phụ thuộc vào khả năng hiển thị trong ViewPager; kiểm tra userVisibleHint hoặc sử dụng setMaxLifecycle
  • Nhiệm vụ điển hình — đặt lại bộ đếm, cập nhật dữ liệu khi quay lại, quản lý BiometricPrompt
  • Cặp onResume/onPause — tài nguyên có quyền truy cập độc quyền chỉ được quản lý bởi cặp này

Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay

IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.

Thảo luận dự án

Đọc thêm