Nguyên nhân chính gây ra ANR trên Android và cách khắc phục triệt để

06 tháng 4, 2026·5 phút đọc

ANR (Application Not Responding) là một trong những vấn đề hiệu suất nghiêm trọng nhất khiến người dùng gỡ bỏ ứng dụng Android. Bài viết này sẽ phân tích nguyên nhân gốc rễ của ANR, từ việc chặn luồng chính đến các lỗi phổ biến, đồng thời cung cấp các giải pháp tối ưu bằng Kotlin Coroutines để đảm bảo trải nghiệm mượt mà.

Nguyên nhân chính gây ra ANR trên Android và cách khắc phục triệt để

Hãy tưởng tượng người dùng mở ứng dụng Android của bạn, chạm vào một nút bấm nhưng không có gì xảy ra. Màn hình bị đóng băng và sau vài giây, hệ thống hiện lên thông báo Application Not Responding (ANR).

Đa số người dùng sẽ không chờ đợi. Họ sẽ đóng ứng dụng và gỡ cài đặt ngay lập tức. Đó là lý do ANR được coi là một trong những vấn đề hiệu suất nguy hiểm nhất đối với các ứng dụng Android.

Theo tài liệu của Android Developers, ANR xảy ra khi luồng chính (main thread) bị chặn và không thể phản hồi đầu vào của người dùng trong một khoảng thời gian cụ thể. Nếu tỷ lệ ANR của bạn trở nên quá cao, Google Play thậm chí có thể giảm độ hiển thị của ứng dụng. Vì vậy, việc hiểu rõ ANR không chỉ là lựa chọn mà còn là yếu tố sống còn.

Ví dụ về ANR trên AndroidVí dụ về ANR trên Android

ANR là gì?

Theo định nghĩa chính thức, ANR xảy ra khi luồng giao diện người dùng (UI thread) bị chặn và không thể xử lý đầu vào của người dùng hoặc vẽ các khung hình (frame). Android sẽ hiển thị thông báo ANR trong các trường hợp sau:

  • Sự kiện đầu vào không được xử lý trong vòng 5 giây.
  • BroadcastReceiver chạy quá lâu.
  • Service mất quá nhiều thời gian để khởi động.
  • Công việc (Job) hoặc dịch vụ nền (foreground service) trì hoãn phản hồi.

Hãy tưởng tượng ứng dụng của bạn giống như một nhà hàng:

  • Luồng UI (Main Thread): Nhân viên phục vụ.
  • Luồng nền (Background Thread): Bếp.
  • Người dùng: Khách hàng.

Luồng hoạt động tốt: Khách gọi món -> Nhân viên chuyển vào bếp -> Bếp chuẩn bị -> Nhân viên phục vụ. Mọi thứ diễn ra suôn sẻ.

Luồng hoạt động xấu (Gây ANR): Khách gọi món -> Nhân viên phục vụ tự vào bếp và bắt đầu tự nấu ăn. Kết quả: Không ai nhận đơn mới, không ai phục vụ món ăn, nhà hàng tê liệt. Điều này tương đương với việc luồng UI thực hiện các tác vụ nặng nề.

Nguyên nhân hàng đầu gây ra ANR

1. Thực hiện tác vụ nặng trên luồng chính ❌

Vấn đề: Chạy các thao tác như gọi mạng (network calls), truy vấn cơ sở dữ liệu, đọc file, phân tích cú pháp JSON hay xử lý ảnh trực tiếp trên luồng UI. Tài liệu Android đã khẳng định rõ: Hãy giữ luồng chính không bị chặn và chuyển các công việc nặng sang luồng làm việc (worker threads).

2. Thao tác cơ sở dữ liệu hoặc mạng kéo dài ❌

Ví dụ, việc gọi API trực tiếp trên luồng chính sẽ chặn giao diện ngay lập tức, dẫn đến ANR.

3. BroadcastReceiver thực hiện quá nhiều việc ❌

BroadcastReceiver chỉ nên thực hiện các tác vụ nhỏ và nhanh chóng. Nếu nó chạy quá lâu, ANR sẽ xảy ra. Android khuyến nghị chuyển công việc sang các luồng nền.

4. Service không khởi động kịp thời ❌

Một dịch vụ nền (foreground service) phải gọi phương thức startForeground() trong vòng 5 giây. Nếu không, hệ thống sẽ kích hoạt ANR.

5. Tắc nghẽn (Deadlock) và chặn luồng ❌

Ví dụ: Luồng A chờ Luồng B, trong khi Luồng B lại chờ Luồng A. Kết quả là ứng dụng bị đóng băng. Tài liệu Android gọi đây là deadlock, một nguyên nhân chính gây ra ANR.

Các sai lầm phổ biến của lập trình viên

Một sai lầm thường gặp là gọi API trực tiếp trong hàm onCreate của Activity:

override fun onCreate() {
    super.onCreate()
    val users = api.getUsers() // Sai lầm: Chặn luồng UI
}

Hoặc phân tích tệp JSON lớn ngay trên luồng UI:

val json = File("data.json").readText() // Nguy cơ gây ANR cao

Các phương pháp tốt nhất để tránh ANRs

✅ Sử dụng Coroutines

Di chuyển các tác vụ ra khỏi luồng chính:

viewModelScope.launch {
    val users = repository.getUsers()
}

✅ Sử dụng Dispatchers.IO

Tối ưu hóa cho các tác vụ I/O (mạng, file):

withContext(Dispatchers.IO) {
    api.getUsers()
}

✅ Sử dụng Flow cho dữ liệu liên tục

Xử lý dòng dữ liệu hiệu quả và không chặn:

repository.getUsersFlow()
    .flowOn(Dispatchers.IO)

✅ Sử dụng WorkManager cho các tác vụ nền

WorkManager rất phù hợp cho: Đồng bộ hóa (Sync), Tải lên (Upload), Tải xuống (Downloads) và Lập lịch (Scheduling).

✅ Giữ cho BroadcastReceiver nhẹ nhàng

Chuyển công việc sang luồng nền ngay lập tức:

override fun onReceive(context: Context, intent: Intent) {
    CoroutineScope(Dispatchers.IO).launch {
        repository.sync()
    }
}

Ví dụ mã nguồn Kotlin hiện đại

Dưới đây là cách triển khai chuẩn để tránh ANR trong kiến trúc hiện đại:

Repository:

class UserRepository(
    private val api: UserApi
) {
    suspend fun getUsers(): List<User> {
        return withContext(Dispatchers.IO) {
            api.getUsers()
        }
    }
}

ViewModel:

@HiltViewModel
class UserViewModel @Inject constructor(
    private val repository: UserRepository
) : ViewModel() {

    private val _users = MutableStateFlow<List<User>>(emptyList())
    val users = _users.asStateFlow()

    init {
        loadUsers()
    }

    private fun loadUsers() {
        viewModelScope.launch {
            _users.value = repository.getUsers()
        }
    }
}

Jetpack Compose UI:

@Composable
fun UserScreen(viewModel: UserViewModel) {
    val users by viewModel.users.collectAsState()

    LazyColumn {
        items(users) { user ->
            Text(user.name)
        }
    }
}

Trường hợp thực tế: Ứng dụng quét video

Kịch bản: Ứng dụng cần quét các video trên thiết bị.

Cách làm sai: Quét tệp trực tiếp trong Activity. Giao diện bị đóng băng và ANR xảy ra.

Cách làm đúng:

  • Màn hình chào (Splash) tải video.
  • Repository sử dụng Dispatchers.IO.
  • Flow phát dữ liệu.
  • Giao diện cập nhật mượt mà.

Kết quả: Không ANR, cuộn mượt mà, tải nhanh và đánh giá trên Play Store tốt hơn.

Kết luận

ANR không chỉ là vấn đề hiệu suất kỹ thuật. Chúng ảnh hưởng trực tiếp đến:

  • Trải nghiệm người dùng.
  • Đánh giá ứng dụng.
  • Xếp hạng trên Play Store.
  • Doanh thu.

Chiến lược an toàn nhất rất đơn giản: Giữ cho luồng UI luôn sạch sẽ và chuyển mọi công việc nặng sang các luồng nền.

Nếu bạn tuân thủ các hướng dẫn chính thức của Android và các thực hành tốt nhất với Kotlin hiện đại, ANR có thể được loại bỏ gần như hoàn toàn.

Bài viết được tổng hợp và biên soạn bằng AI từ các nguồn tin tức công nghệ. Nội dung mang tính tham khảo. Xem bài gốc ↗