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

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

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

Logcat มันอยู่ตรงไหน?

การจะแสดง Logcat จากอุปกรณ์แอนดรอยด์ใดๆก็ตาม จะต้องเปิด USB Debugging แล้วลง ADB Driver ให้เรียบร้อย แล้วต่ออุปกรณ์แอนดรอยด์เข้ากับคอมพิวเตอร์ซะ (ถ้าใช้ Emulator ข้ามขั้นตอนลงไดรเวอร์ไปได้เลย)

ใน Android Studio จะมี Logcat ให้ใช้งานอยู่แล้ว โดยแถบเมนูจะอยู่ที่บริเวณด้านล่างของหน้าจอโปรแกรม มีชื่อแถบเมนูว่า Logcat

สามารถกดเปิด/ปิดหน้าต่าง Logcat ได้แบบง่ายๆด้วยการกดปุ่ม Ctrl + 6 (สำหรับ Windows และ Linux) หรือ Command + 6 (สำหรับ macOS)

เมื่อกดเปิดแถบเมนู Logcat ก็จะมีหน้าต่างแสดงขึ้นมาประมาณนี้

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

การใช้งาน Logcat บนแอนดรอยด์

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

โดยคำสั่งแสดงข้อความใน Logcat จะใช้คลาส Log (android.util.Log) ที่มีรูปแบบการเรียกใช้งานแบบนี้

Log.v("Tag", "Message")
Log.d("Tag", "Message")
Log.i("Tag", "Message")
Log.w("Tag", "Message")
Log.e("Tag", "Message")
Log.wtf("Tag", "Message")

คำสั่ง Log ทุกตัวจะประกอบไปด้วย Parameter หลักๆ 2 ตัวด้วยกันคือ Tag และ Message โดยเป็น String ทั้งคู่

  • Tag คือหัวข้อกำกับของ Log นั้นๆ กำหนดเป็นอะไรก็ได้ไม่มีข้อจำกัดตายตัว
  • Message คือข้อความของ Log นั้นๆ กำหนดเป็นอะไรก็ได้เช่นกัน ไม่ต่างจาก Tag

โดยนักพัฒนาจะต้องเป็นคนกำหนดเองว่าจะให้แสดงข้อความอะไร ซึ่ง Tag จะนิยมกำหนดเป็นหัวข้อของ Log นั้นๆ ส่วน Message ก็คือข้อความและข้อมูลการทำงานของโค้ดที่อยากจะให้แสดงออกมา

ตัวอย่างการใช้คำสั่ง Log

Concept ของการใช้คำสั่ง Log คือ “อยากจะรู้อะไร ก็ให้แสดงมันออกมาซะ” ดังนั้นจึงไม่มีการกำหนดรูปแบบตายตัวว่าจะต้องแสดงอะไรใน Logcat

ลองยกตัวอย่างง่ายๆก่อน

// MainActivity.kt
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        Log.d("Awesome Tag", "Very useful message")
    }
}

เมื่อแอปทำงานก็จะแสดง Log ทันทีแบบนี้

โดย Log ที่แสดงใน Logcat จะประกอบไปด้วยดังนี้

จะเห็นว่า Tag กับ Message ไปแสดงอยู่ช่วงท้ายๆของ Log นั่นเอง

ทีนี้ลองแสดงข้อมูลบางอย่างดู

// MainActivity.kt
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        Log.d("MainActivity", "Package Name : $packageName")
    }
}

จะเห็นว่าเจ้าของบล็อกลองใช้ packageName เพื่อให้ Log แสดง Package Name ของแอปตัวนี้

คำสั่ง Log สามารถใช้แสดงค่าอะไรก็ได้ในโค้ด ขอแค่เพียงทำให้มันเป็น String ก็พอ

ไม่ว่าจะเป็นค่าของตัวแปร Integer ยัน JSON ที่ส่งกลับมาจากเซิฟเวอร์ ก็สามารถแสดงใน Logcat ได้ด้วยการแปลงให้เป็น String แล้วใช้คำสั่ง Log

ดังนั้น Logcat จึงมีประโยชน์มากสำหรับนักพัฒนาแอนดรอยด์ เรียกได้ว่าไม่มีนักพัฒนาแอนดรอยด์คนไหนที่ไม่รู้จัก Logcat เลยก็ว่าได้ เพราะถือว่าเป็นเครื่องมือพื้นฐานประจำตัวอย่างหนึ่งที่จะขาดไปไม่ได้ (เวอร์มะ)

ประเภทของ Log (Log Type)

นอกจากการแสดง Log เพื่อให้รู้ข้อมูลที่ต้องการแล้ว Log ยังมีการแบ่งประเภทออกเป็นหลายประเภท เพื่อให้นักพัฒนาแยกประเภทของ Log ได้ง่ายขึ้น ลดความวุ่นวายของ Log ที่แสดงออกมายาวเป็นหางว่าวให้น้อยลง

Log จะถูกแบ่งย่อยออกเป็น 6 ประเภทดังนี้

  • Verbose (V) ใช้แสดงข้อความที่ไม่ค่อยสำคัญมากนัก
  • Debug (D) ใช้แสดงข้อความสำหรับการ Debug
  • Info (I) ใช้แสดงข้อความสำหรับแสดงข้อมูลการทำงานบางอย่าง
  • Warning (W) ใช้แสดงข้อผิดพลาดในการทำงานของโค้ดที่ไม่ร้ายแรง (แค่เตือน)
  • Error (E) ใช้แสดงข้อผิดพลาดในการทำงานของโค้ด
  • Assert (A) ใช้แสดงข้อผิดพลาดที่ไม่ควรจะเกิดขึ้นในโค้ด

โดยหลักๆจะใช้กันอยู่ 4 ตัวด้วยกันคือ Debug, Info, Warning และ Error ซึ่ง Logcat ใน Android Studio จะดูค่อนข้างลำบาก เพราะสีคล้ายๆกัน และดู Log Type ได้ยาก ถ้าเป็นไปได้ก็ให้ลองเปลี่ยนสีของ Log แต่ละประเภทดู จะได้ดูได้ง่ายขึ้น เปลี่ยนสีให้กับ Logcat บน Android Studio

Logcat แสดงสาเหตุของปัญหาให้ทุกครั้งที่แอปพัง (Force Close)

เขียนโค้ดถูกต้อง แต่รันแล้วอยู่ก็ปิดตัวลง เป็นเรื่องปกติที่นักพัฒนาแอนดรอยด์ทุกๆคนต้องเจอ (เจ้าของบล็อกก็ด้วย)

สิ่งที่สำคัญที่นักพัฒนาจะต้องรู้คือ “เป็นเพราะอะไร?” ซึ่ง Logcat เนี่ยแหละ จะเป็นตัวบอกให้นักพัฒนารู้ จึงเป็นเรื่องปกติที่เวลาเจอปัญหานี้แล้วนักพัฒนาทุกคนจะพากันเปิดดู Logcat

อันนี้เป็นตัวอย่างที่ Logcat จะแสดงข้อความให้เห็นว่าแอปปิดตัวลงและปิดเพราะอะไร ซึ่งข้อความเหล่านี้จริงๆจะเรียกกันว่า Stacktrace และที่สำคัญมีบอกให้รู้ด้วยว่าบรรทัดไหนที่เป็นต้นเหตุ

โดยจะเห็นว่าสาเหตุคือ NullPointerException หรือก็คือมีอะไรบางอย่างเป็น Null

ว่าแต่มันเป็น Null ที่ตรงไหนของโค้ดล่ะ?

ให้ลองสังเกตได้จากตัวหนังสือสีน้ำเงินที่โดดเด่นอยู่ท่ามกลางสีแดงในหน้าต่าง Logcat นั่นแหละคนร้ายที่แท้จริง!! กดเลย!!!

โปรแกรมก็จะเปิดไฟล์ที่เป็นต้นเหตุ แล้วเลื่อนเคอร์เซอร์ไปอยู่ตรงบรรทัดเจ้าปัญหาให้โดยอัตโนมัติ จากนั้นก็ตรวจสอบแล้วแก้ไขปัญหาให้เรียบร้อยซะ

Logcat จะบอกแค่ว่าสาเหตุคืออะไร และเกิดขึ้นที่ตรงไหนของโค้ด แต่นักพัฒนาก็ต้องไปตรวจสอบดูเองว่าปัญหาจริงๆเกิดจากคำสั่งไหน ทำไม แล้วแก้ไขด้วยตัวเอง ซึ่งระหว่างการแก้ไขนี้ก็ลองใช้ Log ช่วยในการหาสาเหตุได้เหมือนกันนะ เช่น ใช้ Log ตรวจสอบว่า Object นั้นๆเป็น Null หรือไม่ เป็นต้น

ปกติใช้ Logcat ทำอะไรกันบ้าง?

Logcat จะไร้ค่าไปเลยถ้านักพัฒนาใช้งานมันไม่เป็น แต่เมื่อเขียนแอปไปซักพักหนึ่งยังไงก็ต้องใช้ Logcat อยู่ดีนั่นแหละ ดังนั้นมาดูกันบ้างว่าส่วนใหญ่ใช้ Logcat ทำอะไรกัน

ศึกษา/ตรวจสอบลำดับการทำงานของโค้ด

เมื่อใดที่อยากรู้ว่าโค้ดตรงไหนทำงานก่อน-หลัง โค้ดตรงไหนทำงานบ้าง ให้ใส่คำสั่ง Log แบบง่ายๆไปเลยครับ แล้วตอนที่ทดสอบก็จะเห็นได้เองจากใน Logcat

// MainActivity.kt
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        Log.d("MainActivity", "onCreate")
    }

    override fun onStart() {
        super.onStart()
        Log.d("MainActivity", "onStart")
    }

    override fun onResume() {
        super.onResume()
        Log.d("MainActivity", "onResume")
    }

    override fun onPause() {
        super.onPause()
        Log.d("MainActivity", "onPause")
    }

    override fun onStop() {
        super.onStop()
        Log.d("MainActivity", "onStop")
    }

    override fun onDestroy() {
        super.onDestroy()
        Log.d("MainActivity", "onDestroy")
    }

    override fun onRestart() {
        super.onRestart()
        Log.d("MainActivity", "onRestart")
    }
}

ตรวจสอบ Error หรือ Exception

ดังที่อธิบายไปก่อนหน้านี้แล้วว่าเวลาที่แอปมีปัญหาแล้วปิดตัวลง Logcat จะช่วยแจ้ง Log ออกมาให้นักพัฒนาสามารถตรวจสอบปัญหาได้ง่ายขึ้น

ดังนั้นเวลาที่นักพัฒนามือใหม่เจอปัญหาแล้วแก้ไขด้วยตัวเองไม่ได้ แล้วเอาไปถามนักพัฒนาคนอื่นๆ สิ่งที่นักพัฒนาคนอื่นๆอยากจะรู้ก็คือข้อความที่แสดงใน Logcat นั่นเอง เพื่อที่จะได้ไล่หาสาเหตุไปเรื่อยๆ

ตรวจสอบเงื่อนไข/ค่าต่างๆในโค้ด

บ่อยครั้งที่มีการเรียกใช้งานคำสั่งอย่าง if-else แล้วกลับไม่รู้ว่าตอนที่แอปทำงานจริง มันทำงานที่เงื่อนไขไหน หรือว่าค่าที่อยู่ในตัวแปรนั้นๆมีค่าเป็นอะไร ก็ลองแสดงออกมาผ่าน Logcat ดูสิ

fun onTouchEvent(event: MotionEvent): Boolean {
    Log.d("MainFragment", "Position : ${event.x}, ${event.y}")
    when (event.action) {
        MotionEvent.ACTION_DOWN -> Log.d("MainFragment", "Action Down")
        MotionEvent.ACTION_MOVE -> Log.d("MainFragment", "Action Move")
        MotionEvent.ACTION_UP -> Log.d("MainFragment", "Action Up")
    }
    return true
}

Logcat มีไว้ให้คนอ่าน จงทำให้มันเข้าใจง่าย

การแสดงข้อความใดๆผ่านคำสั่ง Log ทุกครั้ง ควรเป็นข้อความที่ “เข้าใจง่าย”, “ดูไม่ยาก” และ “ไม่ปนกันมั่ว” เพราะตัวเราเองนั่นแหละที่ต้องมานั่งดู Logcat

  • อย่าแสดงแค่ตัวเลข แต่ไม่บอกว่ามันคืออะไร
  • อย่าแสดงข้อความที่ไม่จำเป็น หรืออ่านแล้วไม่เข้าใจว่าจะสื่ออะไร
  • แสดง Log ให้น้อยบรรทัดที่สุด
  • พยายามทำให้ดูแล้วเข้าใจง่ายที่สุด
  • แยกประเภทของ Log ให้ถูกต้อง
  • ตอนทำเป็น Production ถ้ามี Log อันไหนไม่อยากให้แสดงก็อย่าลืมเอาออกด้วย

จากภาพตัวอย่างข้างบนที่เจ้าของบล็อกให้แสดง Log จาก onTouchEvent จริงๆแล้วเป็นการแสดง Log ที่ไม่ควรทำ เพราะว่ามีการแสดงพิกัด XY สลับกับ Action มันจะทำให้งงได้ง่ายว่าพิกัด XY บรรทัดไหนเป็นของ Action ตัวไหนกันแน่

การใส่พิกัด XY ต่อท้าย Action ไปเลย ก็จะทำให้ดูง่ายขึ้นมากกว่าเดิม

มาดูที่ Logcat บน Android Studio กันต่อ

สำหรับการใช้งาน Logcat บน Android Studio สามารถอ่านเนื้อหากันแบบเต็ม ๆ กันได้ใน การใช้งาน Logcat v2 บน Android Studio

การใช้งาน Logcat v2 บน Android Studio
นับจาก Android Studio Dolphin เป็นต้นมา ทีมพัฒนาก็ได้ปรับปรุงหน้าต่าง Logcat ที่ว่าใหม่ทั้งหมดจนกลายออกมาเป็น Logcat v2 ที่มีรูปแบบการใช้งานแตกต่างไปจากเดิมพอสมควร

สรุป

Logcat นั้นถ้าใช้เป็นก็จะเกิดประโยชน์มาก ดังนั้นสิ่งที่นักพัฒนาควรรู้คือ

  • ควรจะใช้คำสั่งตรงไหนในโค้ด?
  • ควรจะแสดงข้อมูลอะไร?
  • ควรรู้วิธีดู Log ที่แสดงออกมา

ถ้านักพัฒนาไม่รู้ทั้ง 3 อย่างนี้ Logcat ก็จะไร้ค่า เพราะใช้ไปยังไงก็ดูไม่เป็นอยู่ดี ดังนั้นควรฝึกใช้บ่อยๆนะ เวลารันโค้ดแล้วมีปัญหาอะไรก็ลองใช้ Log ดู สงสัยอะไรในโค้ดก็ลองใช้ Log ดู แล้ว Logcat ก็จะช่วยให้ชีวิตง่ายขึ้นเยอะเลยล่ะ แต่เอาจริงๆแนะนำให้ลองฝึกใช้ Debugger ของ Android Studio ก็ดีนะ เพราะสามารถเช็คการทำงานแบบ Step-by-Step ในแบบที่ Logcat ทำไม่ได้

และสุดท้ายนี้ก็ขอกล่าวจบท้ายบทความนี้ไว้ว่า

“ชนใดไม่เคยใช้ Logcat แปลว่าชนนั้นไม่เคยเขียนโค้ดแอนดรอยด์ด้วยตนเอง”