StrictMode: nó là gì, chế độ quy tắc nghiêm ngặt và gỡ lỗi trong Android

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

StrictMode là một công cụ dành cho nhà phát triển được tích hợp trong Android SDK, phát hiện và báo cáo theo thời gian thực các thao tác I/O và cuộc gọi mạng ngẫu nhiên trên luồng chính của ứng dụng. Nó không sửa lỗi mà hoạt động như một máy dò — ném ngoại lệ hoặc ghi vào LogCat khi vi phạm các chính sách đã cấu hình. Theo Google, 2024, cấu hình StrictMode đúng cách có thể phát hiện tới 80% vấn đề hiệu suất trước khi phát hành ứng dụng.

Những điểm chính

  • StrictMode — máy dò vi phạm hiệu suất trên luồng chính Android
  • Chính sách đĩa (disk_read, disk_write) và mạng (network) tạo thành bộ kiểm tra cơ bản
  • Công cụ không sửa lỗi mà thông báo về chúng qua LogCat, hộp thoại hoặc sự cố
  • Cấu hình được thực hiện trong Application.onCreate bằng setThreadPolicy + setVmPolicy
  • Chế độ phạt: ném ngoại lệ (death), ghi nhật ký, thông báo trong dropbox

StrictMode là gì

StrictMode là một API được bao gồm trong Android SDK từ API Level 9 (Android 2.3 Gingerbread). Nhiệm vụ của nó là phát hiện trong thời gian chạy việc thực thi ngẫu nhiên các thao tác nặng trên luồng chính (UI) có thể chặn kết xuất giao diện. Luồng chính xử lý đầu vào của người dùng, tính toán bố cục và kết xuất — bất kỳ sự chặn nào lâu hơn 16 ms đều dẫn đến giảm khung hình.

Triết lý công cụ

StrictMode tuân theo nguyên tắc “thất bại nhanh” — phát hiện vấn đề càng sớm càng tốt, lý tưởng nhất là tại thời điểm nó xuất hiện lần đầu. Thay vì chờ đợi người dùng phàn nàn về độ chậm, nhà phát triển nhận được tín hiệu (nhật ký, hộp thoại hoặc sự cố) ngay ở giai đoạn phát triển. Công cụ này không yêu cầu thư viện bổ sung hoặc cấu hình Gradle — chỉ cần vài dòng mã trong Application.onCreate và nó tự động hoạt động trên tất cả các thiết bị.

Đối tượng mục tiêu

StrictMode được thiết kế cho tất cả nhà phát triển Android, bất kể kinh nghiệm. Đối với người mới bắt đầu, nó giúp hình thành thói quen tốt (không thực hiện yêu cầu mạng trong luồng UI); đối với nhà phát triển có kinh nghiệm, nó tự động hóa kiểm soát chất lượng trong đường ống CI/CD. Các dự án lớn (Google, Uber, Spotify) bao gồm StrictMode trong bản dựng gỡ lỗi với penaltyDeath và vô hiệu hóa nó trong bản dựng phát hành thông qua kiểm tra BuildConfig.DEBUG.

Cách StrictMode hoạt động

StrictMode chặn các cuộc gọi hệ thống có thể chặn luồng và so sánh chúng với tập hợp các chính sách đang hoạt động. Nếu một cuộc gọi khớp với chính sách và được thực thi trên luồng chính, StrictMode sẽ áp dụng hình phạt đã chỉ định. Cơ chế chặn được triển khai thông qua một hook trong quy trình — nó không sử dụng phản chiếu và hoạt động với chi phí tối thiểu.

Cơ chế phát hiện

Khi chính sách StrictMode được kích hoạt, nó sẽ tiêm trình xử lý của mình vào các điểm vào của cuộc gọi hệ thống (FileInputStream, FileOutputStream, Socket, URLConnection). Khi ứng dụng gọi, ví dụ, URLConnection.openStream trên luồng chính, StrictMode kiểm tra luồng hiện tại — nếu là luồng chính, công cụ sẽ kích hoạt. Trong Android 6.0+, cơ chế được tăng cường: các cuộc gọi mạng trên luồng chính tạo ra NetworkOnMainThreadException ngay cả khi không có StrictMode, nhưng StrictMode cũng cho phép kiểm soát I/O đĩa.

Hình phạt

Mỗi chính sách có thể có loại hình phạt hoặc kết hợp riêng: penaltyLog — ghi vào LogCat với dấu vết ngăn xếp, penaltyDialog — hiển thị hộp thoại cho người dùng (chỉ gỡ lỗi), penaltyDeath — ném ngoại lệ và làm sự cố ứng dụng, penaltyDropBox — lưu dữ liệu vào DropBoxManager để phân tích sau. Đối với đường ống CI/CD, penaltyDeath được khuyến nghị — nó đảm bảo rằng không có sự hợp nhất nào vi phạm bị bỏ qua.

kotlin
class App : Application() {
    override fun onCreate() {
        super.onCreate()
        if (BuildConfig.DEBUG) {
            StrictMode.setThreadPolicy(
                StrictMode.ThreadPolicy.Builder()
                    .detectDiskReads()
                    .detectDiskWrites()
                    .detectNetwork()
                    .penaltyLog()
                    .penaltyDeath()
                    .build()
            )
        }
    }
}

Chính sách StrictMode

StrictMode chia các chính sách thành hai cấp độ: ThreadPolicy (cấp độ luồng — những gì không thể làm trên luồng chính) và VmPolicy (máy ảo — rò rỉ bộ nhớ và tài nguyên). Cả hai cấp độ được cấu hình độc lập và hoạt động song song.

ThreadPolicy: Đĩa và Mạng

Ở cấp độ luồng, StrictMode kiểm soát bốn loại vi phạm: đọc đĩa (detectDiskReads), ghi đĩa (detectDiskWrites), thao tác mạng (detectNetwork) và cuộc gọi chậm tùy chỉnh (detectCustomSlowCalls). disk_read kích hoạt khi đọc SharedPreferences, SQLite hoặc tệp trên luồng chính. network kích hoạt khi có yêu cầu HTTP, WebSocket và kết nối Socket. Trong Android 11+, detectUnbufferedIO đã được thêm vào để phát hiện I/O không có bộ đệm.

VmPolicy: Rò rỉ Bộ nhớ

VmPolicy kiểm soát rò rỉ ở cấp độ máy ảo ART: detectActivityLeaks (các Activity không bị hủy), detectLeakedClosableObjects (Cursor, Stream, Socket chưa đóng), detectLeakedRegistrationObjects (BroadcastReceiver, ServiceConnection chưa hủy đăng ký). Nếu VmPolicy phát hiện rằng một Activity đã được tạo nhưng không bị hủy sau khi gọi onDestroy, nó sẽ xuất ra dấu vết ngăn xếp đầy đủ — tiết kiệm hàng giờ gỡ lỗi rò rỉ bộ nhớ.

Chính sáchCấp độPhát hiện
detectDiskReadsThreadĐọc SharedPrefs, SQLite, tệp trong luồng UI
detectDiskWritesThreadGhi vào SharedPrefs, SQLite, tệp trong luồng UI
detectNetworkThreadBất kỳ thao tác mạng nào trong luồng UI
detectActivityLeaksVMActivity sống sót sau onDestroy
detectLeakedClosableObjectsVMCursor, Stream, Socket chưa đóng

Cuộc gọi chậm tùy chỉnh

Thông qua detectCustomSlowCalls, bạn có thể đánh dấu các phương thức của riêng mình là “đáng ngờ” và nhận cảnh báo khi vượt quá ngưỡng đã chỉ định. Ví dụ: nếu phương thức loadUserProfile() của bạn thường mất 5 ms nhưng đôi khi mất 200 ms — hãy bọc nó trong StrictMode.noteSlowCall(“loadUserProfile”). Nếu thời lượng vượt quá ngưỡng (mặc định 2000 ms), StrictMode sẽ tạo ra một hình phạt. Ngưỡng được cấu hình qua setSlowCallDurationThreshold.

Cách cấu hình StrictMode

Cấu hình cơ bản của StrictMode mất 10 dòng mã và được thực hiện trong phương thức onCreate của lớp Application tùy chỉnh. Quy tắc chính: StrictMode chỉ được kích hoạt trong bản dựng gỡ lỗi — trong bản dựng phát hành, nó làm chậm ứng dụng và có thể tạo ra kết quả dương tính giả.

Cấu hình cơ bản

Tạo một lớp mở rộng Application, đăng ký nó trong AndroidManifest.xml qua thuộc tính android:name và thêm cấu hình StrictMode. ThreadPolicy.Builder bao gồm tất cả các máy dò và tất cả các loại hình phạt (ngoại trừ dialog — nó chỉ hoạt động khi có trình gỡ lỗi được đính kèm). VmPolicy.Builder thêm các máy dò cho rò rỉ Activity và đối tượng Closable. Đối với các dự án lớn (100+ màn hình), nên cấu hình VmPolicy với penaltyDeath trên máy dò rò rỉ Activity — nghiêm ngặt nhưng hiệu quả.

kotlin
class App : Application() {
    override fun onCreate() {
        super.onCreate()
        if (BuildConfig.DEBUG) {
            StrictMode.setVmPolicy(
                StrictMode.VmPolicy.Builder()
                    .detectActivityLeaks()
                    .detectLeakedClosableObjects()
                    .detectLeakedRegistrationObjects()
                    .penaltyLog()
                    .penaltyDeath()
                    .build()
            )
        }
    }
}

Tích hợp CI/CD

Để kiểm soát tự động trong CI/CD, hãy sử dụng penaltyDeath — nếu bất kỳ bài kiểm tra nào vi phạm chính sách, ứng dụng sẽ sự cố với một ngoại lệ. Kết hợp với Android Test Orchestrator để mỗi bài kiểm tra chạy trong một quy trình sạch. Đối với kiểm tra UI (Espresso, Compose Test), hãy viết TestRule tùy chỉnh chặn các vi phạm StrictMode và biến chúng thành lỗi xác nhận. Ví dụ: trong @Before, kích hoạt StrictMode và trong @After, kiểm tra xem không có vi phạm nào.

Cấu hình ngưỡng

Theo mặc định, ngưỡng cho customSlowCall là 2000 ms, cho disk_read và disk_write — không có ngưỡng (bất kỳ thao tác nào cũng kích hoạt). Thông qua setSlowCallDurationThresholdsetSlowIoDurationThreshold, bạn có thể đặt giá trị riêng của mình bằng mili giây. Nếu ứng dụng của bạn đọc SharedPreferences một cách hợp pháp trên luồng chính (cấu hình nhỏ), hãy tăng ngưỡng lên 10–20 ms — điều này sẽ lọc các lần đọc nhanh trong khi giữ lại các lần đọc chậm.

Thực hành tốt nhất StrictMode

StrictMode là một công cụ mạnh mẽ nhưng tinh tế. Cấu hình không đúng dẫn đến hàng triệu kết quả dương tính giả, khiến các nhà phát triển ngừng chú ý đến chúng. Dưới đây là các thực hành đã được kiểm chứng từ kinh nghiệm của các nhóm Android lớn.

Chỉ kích hoạt trong bản dựng gỡ lỗi

Đây là một quy tắc cứng: StrictMode KHÔNG BAO GIỜ được kích hoạt trong bản dựng phát hành. Sử dụng cờ BuildConfig.DEBUG hoặc buildConfigField tùy chỉnh. Trong bản dựng phát hành, nhiều thư viện bên thứ ba thực hiện hợp pháp các thao tác trên luồng chính (khởi tạo SDK, ghi bộ nhớ đệm) và StrictMode sẽ tạo ra kết quả dương tính giả. Hơn nữa, penaltyDialog trong bản dựng phát hành sẽ hiển thị hộp thoại cho người dùng cuối — điều không thể chấp nhận được.

Sử dụng ba cấp độ nghiêm ngặt

Đối với dự án nhỏ (1–10 màn hình), cấu hình penaltyLog — nhật ký đủ để phân tích thủ công. Đối với dự án trung bình (10–50 màn hình), thêm penaltyDeath trên mạng và customSlowCalls. Đối với dự án lớn (50+ màn hình), kích hoạt toàn bộ tập hợp chính sách với penaltyDeath trong CI/CD và penaltyLog cho phát triển cục bộ. Sự phân cấp này ngăn chặn việc làm quá tải nhà phát triển với các sự cố giả trong khi kiểm soát chặt chẽ chất lượng trong đường ống.

Xử lý kết quả dương tính giả

Một số thư viện (Firebase, Crashlytics, Adjust) thực hiện hợp pháp các thao tác nền mà StrictMode có thể phát hiện không chính xác. Giải pháp: thêm thư viện vào danh sách trắng qua penaltyListener, cập nhật thư viện lên phiên bản có chuyển đổi rõ ràng sang luồng nền hoặc sử dụng StrictMode.vmPolicy. Trong Android 11+, StrictMode.OnVmViolationListener đã được giới thiệu để lọc vi phạm theo chương trình bằng dấu vết ngăn xếp.

kotlin
// Lọc kết quả dương tính giả qua penaltyListener
StrictMode.setThreadPolicy(
    StrictMode.ThreadPolicy.Builder()
        .detectAll()
        .penaltyListener { violation ->
            val stack = violation.stackTraceToString()
            if ("com.google.firebase" !in stack) {
                logViolation(violation)
            }
        }
        .build()
)

StrictMode vs Android Lint vs Profiler

StrictMode không phải là công cụ kiểm soát chất lượng duy nhất trong hệ sinh thái Android. Để hiểu vị trí của nó, hãy so sánh nó với Android Lint, Android Profiler và Perfetto theo các tiêu chí chính: thời gian kiểm tra, độ sâu phân tích và tự động hóa.

Tiêu chíStrictModeAndroid LintProfiler / Perfetto
Thời gian kiểm traThời gian chạy (khi ứng dụng đang chạy)Thời gian biên dịch (trước khi khởi chạy)Thời gian chạy (hậu kiểm)
Kiểm tra gìĐĩa, mạng, rò rỉXML, mã, tài nguyênCPU, bộ nhớ, mạng, năng lượng
Tự động hóaCI/CD qua penaltyDeathTác vụ Gradle + lint-baselineYêu cầu phân tích thủ công
Độ sâuChỉ luồng UI và rò rỉPhân tích mã tĩnhBức tranh hiệu suất đầy đủ
Kết quả dương tính giảTrung bình (phụ thuộc vào thư viện)Thấp (quy tắc đã cấu hình)Không có (đo lường thực tế)

Chiến lược tốt nhất là kết hợp cả ba cách tiếp cận: Android Lint phát hiện lỗi rõ ràng tại thời gian biên dịch (ví dụ: IdleHandler bị quên), StrictMode phát hiện vấn đề tại thời gian chạy và Android Profiler / Perfetto được sử dụng để phân tích sâu khi hai công cụ đầu tiên không đưa ra câu trả lời. Trong các dự án thực tế (Google Maps, Instagram), StrictMode được giới thiệu vào tuần thứ hai của quá trình phát triển — ngay sau khi thiết lập kiến trúc cơ bản.

Ví dụ mã với StrictMode

Hãy xem xét hai kịch bản thực tế nơi StrictMode giúp phát hiện và khắc phục vấn đề hiệu suất: đọc SharedPreferences trên luồng chính và rò rỉ Activity qua một callback chưa hủy đăng ký.

Phát hiện SharedPreferences chậm

Khi khởi động ứng dụng, StrictMode với chính sách detectDiskReads sẽ phát hiện việc đọc SharedPreferences trên luồng chính. Giải pháp: tải cấu hình không đồng bộ qua CoroutineScope hoặc lưu vào bộ nhớ đệm khi khởi động. SharedPreferences đọc đồng bộ tệp XML từ đĩa — ngay cả với tệp nhỏ (1–2 KB), thao tác mất 1–5 ms và lên tới 20 ms trên các thiết bị giá rẻ, có thể dẫn đến giảm khung hình.

kotlin
// ❌ Mã có vấn đề — đọc SharedPrefs trong luồng UI
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // StrictMode detectDiskReads → VI PHẠM!
        prefs = getSharedPreferences("config", MODE_PRIVATE)
    }
}

// ✅ Mã đã sửa — đọc qua Coroutine
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        loadConfigAsync()
    }
}

Phát hiện rò rỉ Activity

StrictMode với VmPolicy.detectActivityLeaks sẽ phát hiện một Activity đã thoát khỏi ngăn xếp (finish đã được gọi) nhưng đối tượng Activity vẫn còn trong bộ nhớ do tham chiếu tĩnh hoặc callback chưa hủy đăng ký. Kịch bản điển hình: đăng ký EventBus hoặc LocationListener trong onResume mà không gọi unregister trong onPause. VmPolicy sẽ xuất ra dấu vết ngăn xếp chỉ ra dòng nơi tham chiếu được tạo.

kotlin
// ❌ Rò rỉ — callback chưa bị hủy
private var locationCallback: LocationCallback? = null

override fun onResume() {
    super.onResume()
    locationCallback = LocationCallback(this::onLocationUpdated)
    locationManager.register(locationCallback) // StrictMode → RÒ RỈ!
}

override fun onPause() {
    super.onPause()
    // Quên: locationManager.unregister(locationCallback)
}

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

StrictMode có làm chậm ứng dụng không?

StrictMode thực sự thêm một chi phí nhỏ — mỗi cuộc gọi hệ thống được kiểm tra theo các chính sách. Tác động hiệu suất là 1–3% trong bản dựng gỡ lỗi và không có trong bản phát hành (nơi StrictMode bị vô hiệu hóa). Khi kích hoạt detectAll trên các thiết bị cũ (Android 6–8), chi phí có thể lên tới 5%, vì vậy chỉ nên cấu hình các chính sách cần thiết.

Có thể sử dụng StrictMode với Jetpack Compose không?

Có, StrictMode hoàn toàn tương thích với Jetpack Compose. Các chính sách đĩa và mạng hoạt động ở cấp độ khung, độc lập với khung UI. Hơn nữa, trong Compose, mức độ nghiêm trọng của các khối UI cao hơn — Compose vẽ lại khung hình ở 120 FPS trên các thiết bị có tốc độ làm mới cao, do đó thêm 5 ms khi đọc tệp trở nên đáng chú ý hơn.

Tại sao StrictMode không kích hoạt khi đọc SharedPreferences?

Từ Android 8.1 (API 27), SharedPreferences có thể sử dụng bộ nhớ đệm — nếu tệp đã được đọc, việc đọc lại sẽ không kích hoạt StrictMode. Đảm bảo bạn gọi getSharedPreferences lần đầu tiên (đọc nguội) và chính sách detectDiskReads đang hoạt động. Cũng kiểm tra xem StrictMode có bị ghi đè trong một fragment không có cha hay không.

Làm thế nào để vô hiệu hóa StrictMode cho các bài kiểm tra riêng lẻ?

Trong kiểm tra JUnit, sử dụng StrictMode.allowThreadDiskReads()StrictMode.allowThreadDiskWrites() trong @Before và khôi phục cài đặt trong @After qua StrictMode.enableDefaults(). Đối với kiểm tra Instrumentation, sử dụng TestRunner tùy chỉnh với bảo toàn tạm thời chính sách gốc. Trong kiểm tra Espresso, thật tiện lợi để bọc mã nhạy cảm với StrictMode trong IdlingResource.

StrictMode có cần thiết trong Kotlin Multiplatform không?

StrictMode chỉ hoạt động trên nền tảng Android thông qua Android SDK. Trong Kotlin Multiplatform (KMP), mã commonMain không thể sử dụng StrictMode, nhưng đối với androidMain, bạn có thể thêm nó như bình thường. Đối với phần iOS, sử dụng một công cụ tương tự — xác nhận DispatchQueue.main.async cho luồng chính.

Tóm tắt

  • StrictMode — máy dò thời gian chạy các vấn đề hiệu suất trên luồng chính Android
  • Các chính sách được chia thành ThreadPolicy (đĩa, mạng) và VmPolicy (rò rỉ bộ nhớ)
  • Cấu hình mất 10 dòng mã trong Application.onCreate với kiểm tra BuildConfig.DEBUG
  • Đối với CI/CD, sử dụng penaltyDeath — vi phạm chính sách làm sự cố ứng dụng
  • StrictMode không thay thế mà bổ sung Android LintPerfetto
  • Lọc kết quả dương tính giả đúng cách là chìa khóa để sử dụng công cụ hiệu quả
  • Nên giới thiệu StrictMode vào tuần thứ hai của quá trình phát triển dự án

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