01 สิงหาคม 2558

[Android Dev Tips] Logcat พื้นฐานสำคัญที่ Android Developer ต้องรู้จัก



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

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


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

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

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


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



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


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

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

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

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

Log.v(String tag, String message);
Log.d(String tag, String message);
Log.i(String tag, String message);
Log.w(String tag, String message);
Log.e(String tag, String message);
Log.wtf(String tag, String message);

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


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

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

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

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

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

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

import android.os.Bundle;
import android.util.Log;

public class MainActivity extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Log.d("Awesome Tag", "Very useful message");
    }
}

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



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


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

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

import android.os.Bundle;
import android.util.Log;

public class MainActivity extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Log.d("MainActivity", "Package Name : " + getPackageName());
    }
}

        จะเห็นว่าเจ้าของบล็อกลองใช้คำสั่ง getPackageName เพื่อให้ 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) ใช้แสดงข้อผิดพลาดที่ไม่ควรจะเกิดขึ้นในโค๊ด

Log.v("TAG", "Message");        \\ Verbose
Log.d("TAG", "Message");        \\ Debug
Log.i("TAG", "Message");        \\ Info
Log.w("TAG", "Message");        \\ Warning
Log.e("TAG", "Message");        \\ Error
Log.wtf("TAG", "Message")       \\ Assert

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


Logcat บอกให้ทุกครั้งที่แอปพลิเคชันเด้ง/ปิดตัวลง (Force Close)

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

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


        อันนี้เป็นตัวอย่างที่ Logcat จะแสดงข้อความให้เห็นว่าแอปพลิเคชันปิดตัวลงและปิดเพราะอะไร ซึ่งข้อความเหล่านี้จริงๆจะเรียกกันว่า Stack Trace

        และที่สำคัญมีบอกให้รู้ด้วยว่าบรรทัดไหนที่เป็นต้นเหตุ


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

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

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


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

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

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

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

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

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

import android.os.Bundle;
import android.util.Log;

import com.akexorcist.localizationactivity.LocalizationActivity;

public class Main2Activity extends LocalizationActivity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);
        Log.d("MainActivity", "onCreate");
    }

    @Override
    protected void onStart() {
        super.onStart();
        Log.d("MainActivity", "onStart");
    }

    @Override
    protected void onResume() {
        super.onResume();
        Log.d("MainActivity", "onResume");
    }

    @Override
    protected void onPause() {
        super.onPause();
        Log.d("MainActivity", "onPause");
    }

    @Override
    protected void onStop() {
        super.onStop();
        Log.d("MainActivity", "onStop");
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.d("MainActivity", "onDestroy");
    }

    @Override
    protected void onRestart() {
        super.onRestart();
        Log.d("MainActivity", "onRestart");
    }
}


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

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

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

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

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

@Override
public boolean onTouchEvent(MotionEvent event) {
    int action = event.getAction();
    float x = event.getX();
    float y = event.getY();
    
    Log.d("MainFragment", "Position : " + x + ", " + y);

    if(action == MotionEvent.ACTION_DOWN) {
        Log.d("MainFragment", "Action Down");
    } else if(action == MotionEvent.ACTION_MOVE) {
        Log.d("MainFragment", "Action Move");
    } else if(action == 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 จะมีแถบเครื่องมือยิบย่อยเล็กน้อยเพื่อให้นักพัฒนาสามารถจัดการกับ Logcat ได้ดียิ่งขึ้น


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



        2. สามารถเลือกได้ว่าจะให้แสดงแค่เฉพาะ Log ของแอปพลิเคชันตัวไหน (อิงจาก PID) แต่เลือกได้เฉพาะแอปพลิเคชันที่เป็น Debug เท่านั้น ไม่สามารถเลือกตัวที่เป็น Production ได้ ดังนั้นถ้าใช้ Emulator ก็อาจจะเห็นว่ามีให้เลือกเกือบทั้งเครื่อง



        แต่ถ้าใช้อุปกรณ์แอนดรอยด์จริงๆจะมีให้เลือกเฉพาะแอปพลิเคชันของผู้ที่หลงเข้ามาอ่านเท่านั้น



        3. Log Level จะเป็นการกำหนดว่าจะให้แสดง Log ประเภทไหนบ้าง แต่ทว่าจะเป็นรูปแบบของ Level แทน ไม่ได้แสดงแค่อย่างใดอย่างหนึ่ง



        โดยที่ Log จะมีการเรียง Level ดังนี้

        สมมติว่าเจ้าของบล็อกเลือก Log Level เป็น Info จะทำให้ Log ที่แสดงใน Logcat เหลือแค่ Info, Warning, Error และ Assert

        และถ้าเลือกเป็น Error ก็จะแสดงแค่ Error และ Assert เท่านั้น

        ตัวอย่าง ในขณะที่เลือกเป็น Verbose


        เมื่อเลือกเป็น Info ก็จะกรอง Verbose กับ Debug ออกไป จึงเหลือแค่ Log ที่เป็นแบบ Info, Warning, Error และ Assert เท่านั้น



        4. ช่องค้นหา Log ในเวลาที่ต้องการดูแค่ Log ที่ต้องการเท่านั้น โดยจะมีผลกับข้อความทั้งหมดที่แสดงใน Log ไม่ว่าจะวันที่/เวลา, PID/TID หรือ Tag ก็ตาม ก็สามารถค้นหาด้วยการพิมพ์ในช่องนี้ได้ แล้ว Logcat ก็จะแสดงแค่อันที่ตรงกับที่ค้นหาเท่านั้น



        5. หรือจะ Custom Filter เองตามใจชอบก็ได้เหมือนกัน อยากจะกำหนดว่าให้แสดงเฉพาะ Log แบบไหนก็สร้างขึ้นมาเป็น Profile เอาไว้ใช้งานบ่อยๆก็ได้


        ให้กดเลือก Edit Filter Configuration เพื่อสร้าง Custom Filter Profile แล้วจะมีหน้าต่างให้กำหนดว่าจะ Filter อะไรบ้าง โดยจะเป็นการกำหนดแยกแต่ละอย่าง ไม่จำเป็นต้องใส่ทุกช่อง ให้ใส่เฉพาะอันที่ต้องการ Filter เท่านั้น เช่น เอาเฉพาะ Log ที่มี Tag ว่า  Check เป็นต้น เสร็จแล้วอย่าลืมตั้งชื่อ Filter ด้วยนะ แล้วก็กด OK ได้เลย



        พอเสร็จแล้วก็จะมี Filter ที่สร้างเมื่อครู่นี้ให้เลือก และจะแสดงเฉพาะ Log ที่ตรงกับที่กำหนดไว้



        6. และมีแถบเครื่องมือเล็กน้อยเพื่อจัดการกับ Log ที่แสดงอยู่


       แต่ละปุ่มก็จะมีหน้าที่ดังนี้ (เรียงจากบนลงล่าง)

       • ลบ Log ทั้งหมดที่แสดงอยู่
       • เลื่อนไปยัง Log ตัวล่าสุด (เลื่อนหน้าต่างไปข้างล่างสุด)
       • เลื่อนขึ้นไปยังเหตุการณ์ที่มีปัญหาก่อนหน้า
       • เลื่อนลงไปยังเหตุการณ์ที่มีปัญหาถัดไป
       • เปิด/ปิด Soft Wrap (ตัดข้อความที่ยาวเกินไปขึ้นบรรทัดใหม่)
       • สั่งปริ้น Log ทั้งหมด
       • ทำการแสดง Log ทั้งหมดใหม่อีกครั้ง (Restart)

สรุป

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

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

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

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

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




เหล่าพันธมิตรแอนดรอยด์

Devahoy Layer Net NuuNeoI The Cheese Factory Somkiat CC Mart Routine Artit-K Arnondora Kamonway Try to be android developer Oatrice Benz Nest Studios Kotchaphan@Medium Jirawatee@Medium Travispea@Medium