มารู้จัก UI Thread และการแก้ปัญหา Network on Main Thread Exception

มารู้จัก UI Thread และการแก้ปัญหา Network on Main Thread Exception Cover Image

บทความสอนเขียนแอพ Android วันนี้ขอนำเสนอเรื่อง UI Thread, ปัญหา NetworkOnMainThreadException และวิธีแก้ปัญหา จริงๆบทความนี้ไม่ได้ Draft ไว้หรือว่าอะไรทั้งสิ้น (ขอลัดคิวบทความอื่นก่อนละกัน) เนื่องจากเห็นจากหลายๆที่นำเสนอวิธีแก้ปัญหาแบบใช้ ThreadPolicy ซึ่งส่วนตัวคิดว่า เป็นการซ่อนปัญหามากกว่าการแก้ปัญหา

Network Exception

โดยส่วนมาก LogCat จะมี Error ลักษณะประมาณนี้ (ต่างกันที่ Package Name และชื่อ Activity)

06-18 08:38:52.690    1970-1970/com.devahoy.sample.AhoyMainThreadActivity E/AndroidRuntime﹕ FATAL EXCEPTION: main
    java.lang.RuntimeException: Unable to start activity ComponentInfo{com.devahoy.sample.AhoyMainThreadActivity/com.devahoy.sample.AhoyMainThreadActivity}

...
Caused by: android.os.NetworkOnMainThreadException
            at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1133)

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

สาเหตุของปัญหา

บางคนอาจจะสงสัยว่าทำไมแอพพลิเคชันของตัวเอง สามารถรันบน Android 2.3.3 หรือต่ำกว่านั้นได้อย่างไม่มีปัญหา แต่พอเวลาไปรันบนเครื่องที่เวอร์ชันสูงกว่า 3.0 เช่น Jelly Bean (4.0+) กับพบว่าเจอปัญหา NetworkOnMainThreadException ก็เพราะว่า ตั้งแต่ Android เวอร์ชัน 3.0 จะไม่อนุญาตให้มีการเชื่อมต่อ Network ภายใน Main Thread (หน้า UI ของแอพ) ส่วนใหญ่เลย เกิดจากกรณีดังนี้

  • เชื่อมต่อ HTTP ผ่าน HTTPClient, HTTPUrlConnection หรือ URL
  • ทำการดาวน์โหลดไฟล์ เช่น Downloader.downloadFile()
  • ติดต่อ Socket
  • ติดต่อฐานข้อมูล MySQL จากเครื่องเซิฟเวอร์
  • ดึงข้อมูลจากเว็บไซต์ด้วย XML, JSON

UI Thread คืออะไร?

ทุกๆครั้งที่แอพพลิเคชันถูกเปิดขึ้นมา ระบบจะสร้าง Thread ขึ้นมา เราเรียกมันว่า Main Thread หรืออีกชื่อหนึ่งคือ UI Thread มันคือ Thread ที่เป็นส่วนที่เอาไว้ติดต่อสื่อสารกับผู้ใช้ เช่นพวก View หรือ Widget ต่างๆ ลองนึกถึงขั้นตอนการทำงานของ Thread ในกรณีที่ผู้ใช้กดปุ่ม Button กันครับ ตามลำดับดังนี้

  1. ผู้ใช้กด Button, UI Thread จะส่ง TouchEvent ไปที่ View
  2. View รับ Event ว่าเป็น State อะไร เช่นการกดปุ่ม (Press State)
  3. View ทำการเปลี่ยน State และทำการ redraw View ใหม่ตามการเปลี่ยนแปลงของ State
  4. รอการตอบสนองจาก Event Action

จากตัวอย่างข้างตนมันคือการทำงานของ MainThread โดยแต่ละ Step มันใช้เวลาน้อยมาก แต่ว่าเกิดว่าเราใช้คำสั่งเพื่อเชื่อมต่อ Network ใน MainThread แทรกเค้าไป สมมติดึงเนื้อหาหน้าเว็บไซต์ใช้เวลาซัก 5 วินาที มันก็จะ Error Application Not Responding ฉะนั้น UI Thread ของเราควรจะมีการ Process ให้น้อยที่สุดและหลีกเลี่ยงการทำงานที่ต้องใช้เวลาดีกว่า (1 วินาที ก็ถือว่าแอพพลิเคชันมีการตอบสนองช้าแล้ว) แล้วอีกอย่าง เมื่อเราเขียนแอพ Android เราก็ควรจะใช้ Asynchronous เพื่อทำหลายๆ Process พรอ้มกัน ดีกว่ามานั่งรอผู้ใช้กดปุ่ม แล้วรอประมวลผล จากนั้นถึงจะตอบสนองต่อผู้ใช้งานอีก

วิธีแก้ปัญหาด้วย StrictMode

StrictMode นั้นเป็นคลาสพิเศษสำหรับ Developer ที่เอาไว้ช่วยในการ Debug แอพพลิเคชัน เช่นการ ยืนยัน/ตรวจสอบ ว่าแอพพลิเคชันของเราไม่ได้ติดต่อ Disk/IO หรือ Network ใน UI Thread

สำหรับวิธีแก้ปัญหาที่พบส่วนมากเลย คือจะใช้ StrictMode.ThreadPolicy ด้วยคำสั่งประมาณนี้

StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build(); StrictMode.setThreadPolicy(policy);

จริงๆวิธีนี้ก็เป็นวิธีแก้ปัญหาในระดับหนึ่ง แต่มันไม่ใช่วิธีแก้ปัญหาที่ถูกต้องซักเท่าไหร่ มันเหมือนเป็นการหลบซ่อนปัญหาจริงๆไว้เท่านั้น ไม่ใช่วิธีแก้ปัญหาที่ดีเลย

StrictMode สามารถใช้ได้ในกรณีที่แอพพลิเคชันเรายังอยู่ในขั้นตอนการพัฒนา แต่ไม่ควรใช้ในแอพพลิเคชันจริง

วิธีแก้ปัญหาด้วย AsyncTask

หลักการของการใช้ AsyncTask คือการใช้ Thread อีก Thread นึงมาช่วยในการ Process ต่างๆครับ มีผลให้มันสามารถทำงานพร้อมๆกันได้ โดยส่วนงานที่ทำมันจะถูกทำอยู่ฉากหลัก (Background) นั่นเอง เมื่อทำการประมวลผลเสร็จ ถึงค่อยมีการอัพเดทหน้า UI ครับ สำหรับวิธีการใช้งาน AsyncTask แบบละเอียด ติดตามได้จากบทความก่อนที่ผมเขียนไว้ครับ ที่นี่ การใช้งาน AsyncTask

สรุป

บทความนี้เป็นการพูดถึง UI Thread และปัญหา NetworkOnMainThreadException โดยเน้นไปที่ทฤษฎีและหลักการซะส่วนใหญ่ครับ จะเน้นว่าการใช้ AsyncTask นั้นมีข้อดีและประโยชน์กว่าการใช้ StrictMode ครับ

References:

Chai

Chai Phonbopit : Developer แห่งหนึ่ง • ผู้ชายธรรมดาๆ ที่ชื่นชอบ Node.js, JavaScript และ Open Source มีงานอดิเรกเป็น Acoustic Guitar และ Football

บทความล่าสุด