ข้อขัดข้อง

แอป Android จะขัดข้องทุกครั้งที่มีการออกโดยไม่คาดคิดซึ่งเกิดจาก ข้อยกเว้นหรือสัญญาณที่ไม่ได้จัดการ แอปที่เขียนโดยใช้ Java หรือ Kotlin จะ หยุดทำงานหากมีการส่งข้อยกเว้นที่ไม่ได้จัดการ ซึ่งแสดงโดยคลาส Throwable แอปที่เขียนโดยใช้รหัสเครื่องหรือ C++ จะขัดข้อง หากมีสัญญาณที่ไม่ได้จัดการ เช่น SIGSEGV ในระหว่างการดำเนินการ

เมื่อแอปขัดข้อง Android จะสิ้นสุดกระบวนการของแอปและแสดงกล่องโต้ตอบ เพื่อให้ผู้ใช้ทราบว่าแอปหยุดทำงานแล้ว ดังที่แสดงในรูปที่ 1

แอปขัดข้องในอุปกรณ์ที่ใช้ Android
รูปที่ 1 แอปขัดข้องในอุปกรณ์ที่ใช้ Android

แอปไม่จำเป็นต้องทำงานในเบื้องหน้าจึงจะเกิดข้อขัดข้อง คอมโพเนนต์ของแอป แม้แต่คอมโพเนนต์อย่าง Broadcast Receiver หรือ Content Provider ที่ ทำงานในเบื้องหลังก็อาจทำให้แอปขัดข้องได้ การขัดข้องเหล่านี้มักทำให้ผู้ใช้สับสนเนื่องจากไม่ได้มีส่วนร่วมกับแอปของคุณอย่างต่อเนื่อง

หากแอปของคุณขัดข้อง คุณสามารถใช้คำแนะนำในหน้านี้เพื่อ วินิจฉัยและแก้ไขปัญหาได้

ตรวจหาปัญหา

คุณอาจไม่ทราบเสมอไปว่าผู้ใช้ประสบปัญหาแอปขัดข้องเมื่อใช้แอปของคุณ หากเผยแพร่แอปแล้ว คุณสามารถใช้ Android Vitals เพื่อดูอัตราการขัดข้องของแอปได้

Android Vitals

Android Vitals ช่วยให้คุณตรวจสอบและปรับปรุงอัตราการขัดข้องของแอปได้ Android Vitals จะวัดอัตราการขัดข้องหลายอย่าง ดังนี้

  • อัตราการขัดข้อง: เปอร์เซ็นต์ของผู้ใช้ที่ใช้งานอยู่รายวันซึ่งพบการขัดข้องประเภทใดก็ตาม
  • อัตราการขัดข้องที่ผู้ใช้รับรู้: เปอร์เซ็นต์ของผู้ใช้ที่ใช้งานอยู่รายวัน ซึ่งพบการขัดข้องอย่างน้อย 1 ครั้งขณะที่ใช้งานแอปของคุณอยู่ (การขัดข้องที่ผู้ใช้รับรู้) ระบบจะถือว่าแอปมีการใช้งานอยู่ หากแอปแสดงกิจกรรมหรือเรียกใช้บริการที่ทำงานอยู่เบื้องหน้า

  • อัตราการขัดข้องหลายครั้ง: เปอร์เซ็นต์ของผู้ใช้ที่ใช้งานอยู่รายวันซึ่งพบการขัดข้องอย่างน้อย 2 ครั้ง

ผู้ใช้ที่ใช้งานอยู่รายวันคือผู้ใช้ที่ไม่ซ้ำซึ่งใช้แอปของคุณใน 1 วันบนอุปกรณ์ 1 เครื่อง ซึ่งอาจมีหลายเซสชัน หากผู้ใช้ใช้แอปของคุณบนอุปกรณ์มากกว่า 1 เครื่องใน 1 วัน อุปกรณ์แต่ละเครื่องจะนับเป็นจำนวนผู้ใช้ที่ใช้งานอยู่ของวันนั้นๆ หากผู้ใช้หลายคนใช้อุปกรณ์เครื่องเดียวกันใน 1 วัน ก็จะนับเป็นผู้ใช้ที่ใช้งานอยู่ 1 คน

อัตราการขัดข้องที่ผู้ใช้รับรู้เป็น Vitals หลัก ซึ่งหมายความว่าเมตริกนี้ส่งผลกระทบต่อ การค้นพบได้ของแอปคุณใน Google Play และมีความสำคัญเนื่องจากการขัดข้องที่เมตริกนี้นับอาจเกิดขึ้นทุกครั้งที่ผู้ใช้มีส่วนร่วมกับแอป ซึ่งทำให้เกิดการหยุดชะงักมากที่สุด

Play กำหนดเกณฑ์ลักษณะการทำงานที่ไม่ถูกต้อง 2 รายการในเมตริกนี้ ดังนี้

  • เกณฑ์ลักษณะการทำงานที่ไม่ถูกต้องโดยรวม: ผู้ใช้ที่ใช้งานอยู่รายวันอย่างน้อย 1.09% พบการขัดข้องที่ผู้ใช้รับรู้ในอุปกรณ์ทุกรุ่น
  • เกณฑ์ลักษณะการทำงานที่ไม่ถูกต้องในแต่ละอุปกรณ์: ผู้ใช้ที่ใช้งานอยู่รายวันอย่างน้อย 8% พบการขัดข้องที่ผู้ใช้รับรู้ในอุปกรณ์รุ่นใดรุ่นหนึ่ง

หากแอปของคุณมีเมตริกเกินเกณฑ์ลักษณะการทำงานที่ไม่ถูกต้องโดยรวม ก็มีแนวโน้มที่จะค้นพบได้น้อยลงในทุกอุปกรณ์ หากแอปของคุณทํางานเกินเกณฑ์ลักษณะการทํางานที่ไม่ถูกต้องต่ออุปกรณ์ในอุปกรณ์บางเครื่อง ก็มีแนวโน้มที่จะค้นพบได้น้อยลงในอุปกรณ์เหล่านั้น และอาจมีคําเตือนแสดงในข้อมูลสินค้าใน Store ของคุณ

Android Vitals จะแจ้งเตือนคุณใน Play Console เมื่อแอป เกิดข้อขัดข้องมากเกินไป

ดูข้อมูลเกี่ยวกับวิธีที่ Google Play รวบรวมข้อมูล Android Vitals ได้ในเอกสารประกอบของ Play Console

วิเคราะห์ข้อขัดข้อง

เมื่อทราบว่าแอปรายงานข้อขัดข้องแล้ว ขั้นตอนถัดไปคือ การวินิจฉัยข้อขัดข้อง การแก้ปัญหาข้อขัดข้องอาจเป็นเรื่องยาก อย่างไรก็ตาม หากคุณระบุสาเหตุหลักของการขัดข้องได้ คุณก็น่าจะหาวิธีแก้ปัญหาได้

มีหลายสถานการณ์ที่อาจทำให้แอปขัดข้อง สาเหตุบางอย่างก็ ชัดเจน เช่น การตรวจสอบค่า Null หรือสตริงว่าง แต่สาเหตุอื่นๆ ก็ ซับซ้อนกว่า เช่น การส่งอาร์กิวเมนต์ที่ไม่ถูกต้องไปยัง API หรือแม้แต่การโต้ตอบแบบมัลติเธรดที่ซับซ้อน

ข้อขัดข้องใน Android จะสร้างสแต็กเทรซซึ่งเป็นชุดข้อมูลลำดับของฟังก์ชันที่มีการเรียกใช้ซึ่งฝังอยู่ในโปรแกรมจนถึงเวลาที่เกิดข้อขัดข้อง คุณดูสแต็กเทรซของข้อขัดข้องได้ใน Android Vitals

วิธีอ่านสแต็กเทรซ

ขั้นตอนแรกในการแก้ไขข้อขัดข้องคือการระบุตำแหน่งที่เกิดข้อขัดข้อง คุณสามารถใช้สแต็กเทรซที่มีอยู่ในรายละเอียดรายงานได้หากใช้ Play Console หรือเอาต์พุตของเครื่องมือ logcat หากไม่มีสแต็กเทรซ คุณควรจำลองข้อขัดข้องในเครื่องด้วยการทดสอบแอปด้วยตนเอง หรือติดต่อผู้ใช้ที่ได้รับผลกระทบ แล้วจำลองข้อขัดข้องขณะใช้ Logcat

การติดตามต่อไปนี้แสดงตัวอย่างข้อขัดข้องในแอปที่เขียนโดยใช้ Jetpack Compose

--------- beginning of crash
AndroidRuntime: FATAL EXCEPTION: main
Process: com.android.developer.crashsample, PID: 3686
java.lang.NullPointerException
    at com.android.developer.crashsample.ComposableSingletons$MainActivityKt.lambda$0(MainActivity.kt:27)
    at androidx.compose.foundation.ClickableNode.handleUpEvent(Clickable.kt:958)
    at androidx.compose.foundation.ClickableNode.onPointerEvent-H0pRuoY(Clickable.kt:895)
    at androidx.compose.ui.input.pointer.Node.dispatchMainEventPass(HitPathTracker.kt:446)
    at androidx.compose.ui.input.pointer.HitPathTracker.dispatchChanges(HitPathTracker.kt:181)
    at androidx.compose.ui.input.pointer.PointerInputEventProcessor.process-BIzXfog(PointerInputEventProcessor.kt:118)
    at androidx.compose.ui.platform.AndroidComposeView.dispatchTouchEvent(AndroidComposeView.android.kt:2650)
    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2969)
    at android.app.Activity.dispatchTouchEvent(Activity.java:4683)
    at android.os.Looper.loop(Looper.java:398)
    at android.app.ActivityThread.main(ActivityThread.java:9569)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:918)

สแต็กเทรซจะแสดงข้อมูล 2 ส่วนที่สําคัญต่อการแก้ไขข้อบกพร่องของข้อขัดข้อง ดังนี้

  • ประเภทของข้อยกเว้นที่ส่ง
  • ส่วนของโค้ดที่เกิดข้อยกเว้น

โดยปกติแล้ว ประเภทของข้อยกเว้นที่เกิดขึ้นจะเป็นคำใบ้ที่ชัดเจนมากว่าเกิดอะไรขึ้น ดูว่าข้อผิดพลาดเป็น IOException, OutOfMemoryError หรือข้อผิดพลาดอื่นๆ แล้วค้นหาเอกสารประกอบเกี่ยวกับคลาสข้อยกเว้น

คลาส เมธอด ไฟล์ และหมายเลขบรรทัดของไฟล์ต้นฉบับที่เกิดข้อยกเว้น จะแสดงในบรรทัดที่ 2 ของ Stack Trace สำหรับแต่ละฟังก์ชันที่ เรียกใช้ บรรทัดอื่นจะแสดงตำแหน่งการเรียกใช้ก่อนหน้า (เรียกว่าสแต็กเฟรม)

การเดินขึ้นสแต็กและตรวจสอบโค้ดอาจช่วยให้คุณพบตำแหน่งที่ ส่งค่าที่ไม่ถูกต้อง หากโค้ดไม่ปรากฏในสแต็กเทรซ แสดงว่าคุณน่าจะส่งพารามิเตอร์ที่ไม่ถูกต้องไปยังการดำเนินการแบบไม่พร้อมกันที่ใดที่หนึ่ง คุณมักจะทราบสิ่งที่เกิดขึ้นได้โดยการตรวจสอบแต่ละบรรทัดของ สแต็กเทรซ, ค้นหาคลาส API ที่คุณใช้ และยืนยันว่า พารามิเตอร์ที่คุณส่งนั้นถูกต้อง และคุณเรียกใช้จากตำแหน่งที่ ได้รับอนุญาต

Stack Trace สำหรับแอปที่มีโค้ด C และ C++ จะทำงานในลักษณะเดียวกัน

*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
Build fingerprint: 'google/foo/bar:10/123.456/78910:user/release-keys'
ABI: 'arm64'
Timestamp: 2020-02-16 11:16:31+0100
pid: 8288, tid: 8288, name: com.example.testapp  >>> com.example.testapp <<<
uid: 1010332
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0
Cause: null pointer dereference
    x0  0000007da81396c0  x1  0000007fc91522d4  x2  0000000000000001  x3  000000000000206e
    x4  0000007da8087000  x5  0000007fc9152310  x6  0000007d209c6c68  x7  0000007da8087000
    x8  0000000000000000  x9  0000007cba01b660  x10 0000000000430000  x11 0000007d80000000
    x12 0000000000000060  x13 0000000023fafc10  x14 0000000000000006  x15 ffffffffffffffff
    x16 0000007cba01b618  x17 0000007da44c88c0  x18 0000007da943c000  x19 0000007da8087000
    x20 0000000000000000  x21 0000007da8087000  x22 0000007fc9152540  x23 0000007d17982d6b
    x24 0000000000000004  x25 0000007da823c020  x26 0000007da80870b0  x27 0000000000000001
    x28 0000007fc91522d0  x29 0000007fc91522a0
    sp  0000007fc9152290  lr  0000007d22d4e354  pc  0000007cba01b640

backtrace:
  #00  pc 0000000000042f89  /data/app/com.example.testapp/lib/arm64/libexample.so (com::example::Crasher::crash() const)
  #01  pc 0000000000000640  /data/app/com.example.testapp/lib/arm64/libexample.so (com::example::runCrashThread())
  #02  pc 0000000000065a3b  /system/lib/libc.so (__pthread_start(void*))
  #03  pc 000000000001e4fd  /system/lib/libc.so (__start_thread)

หากไม่เห็นข้อมูลระดับคลาสและฟังก์ชันใน Stack Trace แบบเนทีฟ คุณอาจต้องสร้างไฟล์สัญลักษณ์สำหรับการแก้ไขข้อบกพร่องแบบเนทีฟและอัปโหลดไปยัง Google Play Console ดูข้อมูลเพิ่มเติมได้ที่ถอดรหัสสแต็กเทรซข้อขัดข้อง ดูข้อมูลทั่วไปเกี่ยวกับการขัดข้องของเนทีฟได้ที่การวินิจฉัยการขัดข้องของเนทีฟ

เคล็ดลับในการจำลองข้อขัดข้อง

คุณอาจไม่สามารถจำลองปัญหาได้เพียงแค่เริ่ม โปรแกรมจำลองหรือเชื่อมต่ออุปกรณ์กับคอมพิวเตอร์ สภาพแวดล้อมในการพัฒนา มักจะมีทรัพยากรมากกว่า เช่น แบนด์วิดท์ หน่วยความจำ และพื้นที่เก็บข้อมูล ใช้ข้อยกเว้นประเภท เพื่อพิจารณาว่าทรัพยากรใดอาจขาดแคลน หรือ ค้นหาความสัมพันธ์ระหว่างเวอร์ชันของ Android, ประเภทอุปกรณ์ หรือเวอร์ชัน ของแอป

ข้อผิดพลาดเกี่ยวกับหน่วยความจำ

หากมี OutOfMemoryError คุณจะสร้างโปรแกรมจำลองที่มี ความจุหน่วยความจำต่ำเพื่อใช้ทดสอบได้ รูปที่ 2 แสดงการตั้งค่า AVD Manager ซึ่งคุณ ควบคุมปริมาณหน่วยความจำในอุปกรณ์ได้

การตั้งค่าหน่วยความจำใน AVD Manager
รูปที่ 2 การตั้งค่าหน่วยความจำใน AVD Manager

ข้อยกเว้นด้านเครือข่าย

เนื่องจากผู้ใช้มักจะเข้าและออกจากพื้นที่ครอบคลุมเครือข่ายมือถือหรือเครือข่าย Wi-Fi อยู่เสมอ ดังนั้นข้อยกเว้นเครือข่ายในแอปพลิเคชันจึงไม่ควรได้รับการพิจารณาเป็นข้อผิดพลาด แต่ควรพิจารณาเป็นสภาวะการทำงานปกติที่เกิดขึ้นโดยไม่คาดคิด

หากต้องการจำลองข้อยกเว้นของเครือข่าย เช่น UnknownHostException ให้ลองเปิดโหมดบนเครื่องบินขณะที่แอปพลิเคชันพยายามใช้เครือข่าย

อีกตัวเลือกหนึ่งคือการลดคุณภาพของเครือข่ายในโปรแกรมจำลองโดย เลือกการจำลองความเร็วเครือข่าย ความล่าช้าของเครือข่าย หรือทั้ง 2 อย่าง คุณใช้การตั้งค่าความเร็วและเวลาในการตอบสนองใน AVD Manager หรือจะเริ่มโปรแกรมจำลอง ด้วยแฟล็ก -netdelay และ -netspeed ก็ได้ ดังที่แสดงใน ตัวอย่างบรรทัดคำสั่งต่อไปนี้

emulator -avd [your-avd-image] -netdelay 20000 -netspeed gsm

ตัวอย่างนี้กำหนดการหน่วงเวลา 20 วินาทีในคำขอเครือข่ายทั้งหมด และความเร็วในการอัปโหลด และดาวน์โหลดที่ 14.4 Kbps ดูข้อมูลเพิ่มเติมเกี่ยวกับตัวเลือกบรรทัดคำสั่ง สำหรับโปรแกรมจำลองได้ที่ เริ่มโปรแกรมจำลองจากบรรทัดคำสั่ง

การอ่านด้วย Logcat

เมื่อทำซ้ำข้อขัดข้องได้แล้ว คุณสามารถใช้เครื่องมืออย่าง logcat เพื่อดูข้อมูลเพิ่มเติม

เอาต์พุต Logcat จะแสดงข้อความบันทึกอื่นๆ ที่คุณพิมพ์ พร้อมกับข้อความจากระบบ อย่าลืมปิดคำสั่ง Log เพิ่มเติมที่คุณเพิ่มไว้ เนื่องจากคำสั่งเหล่านี้จะทำให้ CPU และแบตเตอรี่สิ้นเปลือง ขณะที่แอปทำงาน

ป้องกันข้อขัดข้องที่เกิดจากข้อยกเว้นตัวชี้ Null

ข้อยกเว้นตัวชี้แบบ Null (ระบุโดยประเภทข้อผิดพลาดรันไทม์ NullPointerException) จะเกิดขึ้นเมื่อคุณพยายามเข้าถึงออบเจ็กต์ที่เป็น Null โดยปกติจะเกิดขึ้นเมื่อเรียกใช้เมธอดหรือเข้าถึงสมาชิกของออบเจ็กต์ ข้อยกเว้น Null Pointer เป็นสาเหตุที่ใหญ่ที่สุดที่ทำให้แอปขัดข้องใน Google Play จุดประสงค์ของ null คือการระบุว่าไม่มีออบเจ็กต์ เช่น ยังไม่ได้ สร้างหรือกำหนด

หากต้องการหลีกเลี่ยงข้อยกเว้นตัวชี้เป็น Null คุณต้องตรวจสอบว่า การอ้างอิงออบเจ็กต์ที่คุณกำลังทำงานด้วยไม่ใช่ Null ก่อนที่จะเรียกใช้ เมธอดในออบเจ็กต์เหล่านั้นหรือพยายามเข้าถึงสมาชิกของออบเจ็กต์ หากการอ้างอิงออบเจ็กต์เป็น Null ให้จัดการกรณีนี้อย่างเหมาะสม (เช่น ออกจากเมธอดก่อนที่จะดำเนินการ ใดๆ กับการอ้างอิงออบเจ็กต์และเขียนข้อมูลลงในบันทึกแก้ไขข้อบกพร่อง)

เนื่องจากคุณไม่ต้องการตรวจสอบค่า Null สำหรับพารามิเตอร์ทุกรายการของทุกเมธอดที่เรียกใช้ คุณจึงสามารถใช้ IDE หรือประเภทของออบเจ็กต์เพื่อระบุความสามารถในการเว้นว่างได้

Kotlin

ใน Kotlin nullability เป็นส่วนหนึ่งของระบบประเภท เช่น ต้องประกาศตัวแปรตั้งแต่ เริ่มต้นเป็นแบบ Nullable หรือ Non-Nullable ประเภทที่กำหนดให้เป็น Null ได้จะมีเครื่องหมาย ? กำกับอยู่

// non-null
var s: String = "Hello"

// null
var s: String? = "Hello"

ตัวแปรที่กำหนดให้ต้องมีค่าจะกำหนดค่าเป็น Null ไม่ได้ และตัวแปรที่กำหนดให้มีค่าเป็น Null ได้ ต้องได้รับการตรวจสอบความสามารถในการเว้นว่างก่อนที่จะนำไปใช้เป็นตัวแปรที่กำหนดให้ต้องมีค่า

หากไม่ต้องการตรวจสอบค่า Null อย่างชัดเจน คุณสามารถใช้ตัวดำเนินการ?.การเรียกที่ปลอดภัย ได้

val length: Int? = string?.length  // length is a nullable int
                                   // if string is null, then length is null

แนวทางปฏิบัติแนะนำคือให้ตรวจสอบกรณีที่ออบเจ็กต์ที่กำหนดให้เป็น Null ได้เป็น Null มิฉะนั้นแอปอาจเข้าสู่สถานะที่ไม่คาดคิด หากแอปพลิเคชันไม่ขัดข้องอีกต่อไปเมื่อใช้ NullPointerException คุณจะไม่ทราบว่ามีข้อผิดพลาดเหล่านี้อยู่

วิธีตรวจสอบค่า Null มีดังนี้

  • if ครั้ง

    val length = if(string != null) string.length else 0
    

    เนื่องจากการแคสต์อัจฉริยะและการตรวจสอบค่า Null คอมไพเลอร์ Kotlin จึงทราบว่าค่าสตริง ไม่ใช่ค่า Null จึงอนุญาตให้คุณใช้การอ้างอิงโดยตรง โดยไม่ต้องใช้ตัวดำเนินการเรียกที่ปลอดภัย

  • ?: โอเปอเรเตอร์ Elvis

    ตัวดำเนินการนี้ช่วยให้คุณระบุได้ว่า "หากออบเจ็กต์ไม่ใช่ค่าว่าง ให้แสดงผลออบเจ็กต์ ไม่เช่นนั้น ให้แสดงผลค่าอื่น"

    val length = string?.length ?: 0
    

คุณยังคงใช้ NullPointerException ใน Kotlin ได้ สถานการณ์ที่พบบ่อยที่สุดมีดังนี้

  • เมื่อคุณโยน NullPointerException อย่างชัดเจน
  • เมื่อใช้ตัวดำเนินการ การยืนยันค่า Null !! ตัวดำเนินการนี้จะแปลงค่าใดๆ เป็นประเภทที่ไม่ใช่ Null และจะแสดงข้อผิดพลาด NullPointerException หากค่าเป็น Null
  • เมื่อเข้าถึงการอ้างอิงค่าว่างของประเภทแพลตฟอร์ม

ประเภทแพลตฟอร์ม

ประเภทแพลตฟอร์มคือการประกาศออบเจ็กต์ที่มาจาก Java ประเภทเหล่านี้ได้รับการจัดการเป็นพิเศษ การตรวจสอบค่า Null จึงไม่ได้บังคับใช้ ดังนั้นการรับประกันว่าไม่ใช่ค่า Null จึงเหมือนกับใน Java เมื่อเข้าถึงการอ้างอิงประเภทแพลตฟอร์ม Kotlin จะไม่สร้างข้อผิดพลาดขณะคอมไพล์ แต่การอ้างอิงเหล่านี้อาจทำให้เกิดข้อผิดพลาดขณะรันไทม์ได้ ดูตัวอย่างต่อไปนี้จากเอกสารประกอบของ Kotlin

val list = ArrayList<String>() // non-null (constructor result) list.add("Item")
val size = list.size // non-null (primitive int) val item = list[0] // platform
type inferred (ordinary Java object) item.substring(1) // allowed, may throw an
                                                       // exception if item == null

Kotlin อาศัยการอนุมานประเภทเมื่อกำหนดค่าแพลตฟอร์มให้กับตัวแปร Kotlin หรือคุณจะกำหนดประเภทที่คาดไว้ก็ได้ วิธีที่ดีที่สุดในการตรวจสอบสถานะความสามารถในการเว้นว่างที่ถูกต้องของการอ้างอิงที่มาจาก Java คือการใช้คำอธิบายประกอบความสามารถในการเว้นว่าง (เช่น @Nullable) ในโค้ด Java คอมไพเลอร์ Kotlin จะแสดงการอ้างอิงเหล่านี้เป็นประเภทที่ยอมรับค่า Null หรือไม่ยอมรับค่า Null จริง ไม่ใช่เป็น ประเภทแพลตฟอร์ม

เราได้ใส่คำอธิบายประกอบ @Nullable หรือ @NonNull ใน Java Jetpack API ตามความจำเป็น และใช้แนวทางที่คล้ายกันใน Android 11 SDK ประเภทที่มาจาก SDK นี้ซึ่งใช้ใน Kotlin จะแสดงเป็น ประเภทที่ถูกต้องซึ่งเว้นว่างได้หรือไม่เว้นว่างได้

ระบบประเภทของ Kotlin ช่วยลดข้อขัดข้อง NullPointerException ได้อย่างมาก ตัวอย่างเช่น แอป Google Home มีข้อผิดพลาดที่เกิดจากข้อยกเว้น Null Pointer ลดลง 30% ในช่วงปีที่ย้ายการพัฒนาฟีเจอร์ใหม่ไปเป็น Kotlin