05 July 2016

สรุปเนื้อหาและสิ่งที่ไม่ได้พูดใน Firebase Android Codelabs จากงาน I/O Extended Bangkok [ตอนที่ 2]

Updated on


       สำหรับบทความนี้ก็เป็นตอนที่ 2 ของเนื้อหาที่เจ้าของบล็อกพูดใน Session : Firebase Android Codelabs ที่งาน I/O Extended Bangkok ซึ่งเนื้อหาบทความยาวพอสมควรจึงขอแบ่งออกมาเป็น 2 ตอนแทนจ้า

        จากบทความที่แล้วเจ้าของบล็อกได้พูดถึงเรื่อง Analytics, Crash Reporting และ Authentication ไปแล้ว คราวนี้ก็เป็น Realtime Database กับ Remote Config กันต่อ

        สำหรับผู้ที่หลงเข้ามาอ่านที่ยังไม่ได้อ่านตอนที่ 1 สามารถไปอ่านก่อนได้ที่ สรุปเนื้อหาและสิ่งที่ไม่ได้พูดใน Firebase Android Codelabs จากงาน I/O Extended Bangkok [ตอนที่ 1]

รับส่งข้อมูลง่ายๆแต่โคตรไวด้วย Firebase Realtime Database

        จริงๆแล้วบริการนี้ไม่ใช่อะไรแปลกใหม่เลย เพราะมันคือบริการดั้งเดิมของ Firebase อยู่แล้ว ซึ่งเป็นบริการที่ทำให้นักพัฒนาสามารถสร้างฐานข้อมูลแบบ Realtime ได้ (ค่าในฐานข้อมูลเปลี่ยนเมื่อไร แอปฯที่เปิดใช้งานอยู่จะสามารถรับรู้ได้ทันที)

        ข้ามการอธิบายไปละกัน

        เริ่มจากเพิ่ม Realtime Database เข้าไปในโปรเจคผ่านหน้าต่าง Firebase Assistant เช่นเคย



        ก่อนจะเริ่มโค้ด คงต้องทำความเข้าใจกับฐานข้อมูลที่เจ้าของบล็อกออกแบบไว้ใน Realtime Database ก่อนดีกว่านะ เพราะเจ้าของบล็อกออกแบบไว้แบบนี้


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

        ดังนั้นก็มาใส่โค้ดกันเลยดีกว่า โดย Firebase Realtime Database จะใช้ชื่อคลาสว่า FirebaseDatabase ก็ประกาศแล้วกำหนดค่าให้เรียบร้อยซะ

FirebaseDatabase firebaseDatabase;

...

firebaseDatabase = FirebaseDatabase.getInstance();

        ในการจัดการกับฐานข้อมูลนั้นไม่ได้เรียกใช้คำสั่งจาก FirebaseDatabase โดยตรง แต่จะเรียกใช้งานผ่านคลาสที่ชื่อว่า DatabaseReference ที่ดึงออกมาจากคลาส FirebaseDatabase อีกที

DatabaseReference rootDbRef = firebaseDatabase.getReference();

        จะเห็นว่าเจ้าของบล็อกตั้งชื่อว่า rootDatabase ทั้งนี้ก็เพราะว่า DatabaseReference ตัวนี้จะชี้ไปที่ตำแหน่งนอกสุดของฐานข้อมูล


        แต่ว่าเจ้าของบล็อกต้องการชี้เข้าไปที่ global_room ซึ่งเป็นห้องๆหนึ่งที่เจ้าของบล็อกเตรียมไว้ โดยห้องนี้จะเอาไว้คุยรวมทั้งหมด ใครก็ตามที่ล็อกอินเข้ามาในแอปฯนี้ จะสามารถคุยรวมกันได้ในห้องเดียวเลย (ไม่มีตัวอย่างการสร้างห้องแชทแยกนะ มีห้องคุยรวมแค่ห้องเดียว)

        ดังนั้นเจ้าของบล็อกจะต้องใช้คำสั่ง child เพื่อขยับให้มาชี้ที่ global_room

DatabaseReference globalRoomDbRef = rootDatabaseReference.child("global_room");


        จากนั้นอยากจะเพิ่ม/ลบข้อมูล หรือทำอะไรที่เกี่ยวกับข้อมูลในฐานข้อมูลตัวนี้ ก็จะเรียกใช้งานได้ผ่าน globalRoomDbRef แล้ว

        ความสะดวกของ Realtime Database ก็คือข้อมูลที่อยากจะเพิ่ม เจ้าของบล็อกสามารถสร้าง Model Class ขึ้นมาแล้วโยนไปทั้งก้อนได้เลย แล้ว Firebase ก็จะไปสร้างฐานข้อมูลที่มีโครงสร้างตาม Model Class ที่โยนไปให้

        ยกตัวอย่างเช่น

public class Message {
    String text;
    String user;

    ...
}

        คลาส Message ตัวนี้เป็นคลาสที่เจ้าของบล็อกสร้างขึ้นมาเพื่อใช้เวลาที่ต้องการจะโยนข้อมูลเพิ่มเข้าไปใน Firebase นั่นเอง ดังนั้นเวลาเจ้าของบล็อกอยากจะเพิ่มข้อมูลเข้าไปในฐานข้อมูลก็แค่สร้าง Message Instance ขึ้นมา กำหนดค่าให้เรียบร้อย แล้วโยนเข้าไปในฐานข้อมูลนั่นเอง

Message message = new Message("How are you?", "asd@gmail.com");
globalRoomDbRef.push().setValue(message);

        ในการเพิ่มข้อมูลเข้าไปในฐานข้อมูลจะต้องใช้คำสั่ง push ก่อนคำสั่ง setValue ทุกครั้ง เพราะเป็นคำสั่งที่จะบอกให้ฐานข้อมูลรู้ว่าเป็นการเพิ่มข้อมูลใหม่เข้าไป


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

        เดี๋ยวก่อนนะ แล้ว -KLIPPiAGA0caFKKt2x6 มาจากไหน?

        จะเห็นว่าในข้อมูลแต่ละชุดจะมีค่าพวกนี้กำกับอยู่ข้างบนทุกอัน ซึ่งพวกนี้เรียกว่า Key ซึ่งเอาไว้กำกับข้อมูลแต่ละตัว ไม่ได้ระบุเป็น Index (เพราะมันเป็น Realtime การใช้ Index ระบุก็คงไม่เหมาะซักเท่าไรนัก)


        ดังนั้นเวลาจะลบข้อมูลตัวไหนทิ้ง จะต้องระบุด้วย Key ทุกครั้ง

globalRoomDbRef.child("-KLIPPiAGA0caFKKt2x6").removeValue();

        ส่วน Event เวลาข้อมูลมีการเปลี่ยนแปลงก็จะใช้ Listener อยู่สองแบบ คือ ValueEventListener กับ ChildEventListener ซึ่งมีรูปแบบการทำงานต่างกัน

private ValueEventListener valueEventListener = new ValueEventListener() {
    @Override
    public void onDataChange(DataSnapshot dataSnapshot) {
     ...
    }

    @Override
    public void onCancelled(DatabaseError databaseError) {
        ...
    }
};

private ChildEventListener childEventListener = new ChildEventListener() {
    @Override
    public void onChildAdded(DataSnapshot dataSnapshot, String s) {
        ...
    }

    @Override
    public void onChildChanged(DataSnapshot dataSnapshot, String s) {
  ...
    }

    @Override
    public void onChildRemoved(DataSnapshot dataSnapshot) {
        ...
    }

    @Override
    public void onChildMoved(DataSnapshot dataSnapshot, String s) {
  ...
    }

    @Override
    public void onCancelled(DatabaseError databaseError) {
     ...
    }
};

        onDataChanged ของ ValueEventListener จะถูกเรียกทุกครั้งที่ข้อมูลในฐานข้อมูลมีการเปลี่ยนแปลง โดยข้อมูลที่ส่งมาให้จะเป็นข้อมูลทั้งหมดของฐานข้อมูลนั้น ซึ่งคงไม่ค่อยเหมาะเท่าไรถ้าข้อมูลมีจำนวนเยอะๆ เพราะมันจะเปลืองเนตมากๆ

        onChildAdded, onChildChanged, onChildRemoved และ onChildMoved นั้นจะเป็น Event ที่เกิดขึ้นกับข้อมูลตัวใดตัวหนึ่งเท่านั้น (ซึ่ง Firebase เรียกข้อมูลแต่ละตัวว่า Child) ซึ่ง Listener ตัวนี้จะช่วยประหยัดเนตกว่า ValueEventListener มากกว่า เพราะมันจะส่งข้อมูลมาเฉพาะตัวที่มีการเปลี่ยนแปลงเท่านั้น ดังน้ันเจ้าของบล็อกจึงสามารถยัดเก็บไว้เป็น Array เพื่อแสดงใน List View หรือ Recycler View ได้เลย โดยที่ทุกๆ Method นั้นจะมีการส่งคลาสที่ชื่อว่า DataSnapshot มาให้​ เพื่อดึงข้อมูลจากคลาสตัวนี้

Message message = dataSnapshot.getValue(Message.class);

        ซึ่งความสะดวกก็คือเจ้าของบล็อกสามารถโยนคลาส Message เข้าไปในคำสั่ง getValue ได้เลย เพื่อให้มัน Map ข้อมูลกลับมาเป็นคลาส Message ให้ (คล้ายๆตอนใช้ GSON น่ะแหละ)

        เวลาเรียกใช้งาน Listener ทั้งสองตัวนี้จะใช้คำสั่งใน Database Reference ที่ต้องการ (เพื่อให้ Listener สามารถทำงานกับข้อมูลเฉพาะบาง Child ที่ต้องการได้)

globalRoomDbRef.addValueEventListener(valueEventListener);
globalRoomDbRef.addChildEventListener(childEventListener);

        และเวลาใช้งานเสร็จแล้วก็ควรเคลียร์ Listener ด้วย

globalRoomDbRef.removeEventListener(childEventListener);
globalRoomDbRef.removeEventListener(valueEventListener);

        แต่ที่ต้องทำเพิ่มเติมเล็กน้อยก็คือคลาส Message เวลาเก็บอยู่บนฐานข้อมูลจะมี Key กำกับอยู่ด้วย และเวลาส่งเข้าใน ValueEventListener หรือ ChildEventListener ก็ตาม มันก็จะส่ง Key มาด้วยทุกครั้ง ดังนั้นโค้ดสำหรับคลาส Message เวลาเอาไปใช้งานก็คงจะไม่เพียงพอ เพราะควรจะเก็บค่า Key ของแต่ละตัวด้วย

        ดังนั้นเจ้าของบล็อกก็เลยสร้างคลาสที่ชื่อว่า MessageData ขึ้นมาเพื่อเก็บค่า Key และ Message ไว้ในนั้นอีกทีหนึ่ง

public class MessageData {
    String key;
    Message message;

    ...
}

        เพื่อไม่ให้สับสนขอสรุปแบบนี้นะครับ


        เวลาส่งข้อมูลไปที่ฐานข้อมูล เจ้าของบล็อกจะสร้างคลาส Message ขึ้นมา กำหนดค่าที่ต้องการให้เรียบร้อยแล้วจึงส่งข้อมูลไป แต่เมื่อข้อมูลมีการอัพเดทจากฐานข้อมูลก็จะเอาข้อมูลมาทำเป็นคลาส MessageData (จะได้เก็บ Key ได้ด้วย) เพื่อเอาไปแสดงในแอปฯ

        ซึ่งการแสดงข้อมูลใน List View หรือ Recycler View เจ้าของบล็อกก็ใช้ ChildEventListener ในการอัปเดตข้อมูลน่ะแหละ แต่ปัญหาอย่างหนึ่งก็คือ Realtime Database มันไม่มีคำสั่ง Fetch เพื่อดึงข้อมูลในครั้งแรกที่เปิดแอปฯ เพราะการเปิดแอปฯตอนแรกควรไปดึงข้อมูลจากฐานข้อมูลก่อนว่ามีข้อมูลเก่าอะไรบ้าง เพื่อเอามาแสดงในแอปฯ

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

        ดังนั้นเจ้าของบล็อกจึงใช้ ValueEventListener สำหรับอัปเดตข้อมูลในตอนแรกเท่านั้น นอกจากนั้นก็จะใช้ ChildEventListener สำหรับข้อมูลแทน

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

globalRoomDbRef.addValueEventListener(valueEventListener);
globalRoomDbRef.addChildEventListener(childEventListener);

...

private ValueEventListener valueEventListener = new ValueEventListener() {
    @Override
    public void onDataChange(DataSnapshot dataSnapshot) {
        // อยากรู้แค่ว่าอัปเดตข้อมูลตอนแอพเปิดขึ้นมาในตอนแรกเสร็จแล้วหรือยัง แล้วลบ ValueEventListener ทิ้ง
        globalRoomDbRef.removeEventListener(valueEventListener);

        ...
    }

    ...
};

private ChildEventListener childEventListener = new ChildEventListener() {
    @Override
    public void onChildAdded(DataSnapshot dataSnapshot, String s) {
        // ข้อมูลทุกตัวจากฐานข้อมูลจะถูกส่งมาทั้งหมดในตอนแรก (และครั้งต่อๆไปเมื่อมีการอัปเดต)

        ...
    }

    ...
};

        เนื่องจากเจ้าของบล็อกจะทำให้เข้าหน้า Chat Room แล้วทำการอัปเดตข้อมูลที่มีอยู่ในฐานข้อมูลทันที ซึ่งตรงนี้จะใช้ ValueEventListener ส่วนข้อมูลที่ส่งมาจากฐานข้อมูลจะอยู่ใน onChildAdded แทน


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


        จากภาพข้างบน ไม่ว่าเครื่องไหนก็ตามที่ใช้งานฐานข้อมูลตัวนี้อยู่ ถ้ามีการเปลี่ยนแปลงใดๆก็ตามก็จะเกิด Event ทุกครั้ง ถึงแม้ว่าเครื่อง User 2 จะเป็นคนเพิ่มข้อมูลเข้าไปในฐานข้อมูล แต่ทั้ง User 1 และ User 2 ก็จะเกิด Event ที่ชื่อว่า onChildAdded เหมือนกันทั้งคู่

        ดังนั้นเจ้าของบล็อกจึงใช้คำสั่งอัปเดตข้อมูลใน List View หรือ Recycler View ก็ต่อเมื่อ onChildAdded หรือ onChildRemoved เท่านั้น กดเพิ่มข้อมูลไปแล้วอาจจะไม่ได้อัปเดตใน List View หรือ Recycler View ทันที จะอัปเดตก็ต่อเมื่อ Realtime Database ส่ง Event มาบอกเท่านั้น

        ในที่สุดแอปฯตัวนี้ก็สามารถรับส่งข้อความระหว่างผู้ใช้หลายๆคนด้วยกันได้แล้ว จะเห็นว่าโค้ดที่เขียนนั้นไม่ยากเลย ใช้คำสั่งแค่ไม่กี่อย่าง แต่สิ่งที่สำคัญคือผู้ที่หลงเข้ามาอ่านต้องทำความเข้าใจในการทำงานของ Realtime Database ก่อนจะเอาไปใช้งานนะ

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

ถึงเวลาของ Remote Config ซะที

        จริงๆแล้วเนื้อหาที่เจ้าของบล็อกพูดใน Firebase Android Codelabs นั้นตั้งใจว่าจะพูดเรื่องนี้ด้วย แต่ด้วยเวลาที่จำกัดก็เลยทำให้พูดเรื่องนี้แบบเร่งรัดไปหน่อย

        แต่ไม่เป็นไร มาอ่านในนี้แทนละกันเนอะ

        ซึ่ง Remote Config จะมาช่วยให้นักพัฒนาสามารถกำหนดค่าต่างๆภายในแอปฯผ่าน Firebase ได้ ซึ่งหลักการของมันก็คือให้ไปเขียนโค้ดเตรียมไว้ โดยผูกกับค่าที่อยู่บน Firebase และบนหน้า Console ก็จะสามารถปรับเปลี่ยนค่าได้ตามต้องการ ดังนั้นจึงทำให้นำไปประยุกต์ใช้งานได้หลายแบบ

        สำหรับค่าที่กำหนดนั้นให้นึกถึงว่ามันมีหน้าตาคล้ายกับ Shared Preference น่ะแหละ แต่ค่าที่เก็บไว้สามารถ Sync กับ Server ได้เพื่ออัปเดตค่าล่าสุดที่นักพัฒนากำหนดไว้

         ก่อนอื่นก็ตั้งค่าใช้งาน Remote Config ให้เรียบร้อยซะ



        การเรียกใช้งาน Remote Config ก็เหมือนกับตัวอื่นๆของ Firebase น่ะแหละ

FirebaseRemoteConfig firebaseRemoteConfig;

...

firebaseRemoteConfig = FirebaseRemoteConfig.getInstance();

        และอย่างที่บอกไว้ว่ามันคล้ายกับ Shared Preference นั่นก็เพราะว่ามันเก็บเป็น XML ซึ่งของ Remote Config จะกำหนดว่าต้องไปสร้างไฟล์ XML ไว้ใน res/xml/... เพื่อกำหนดค่า Default สำหรับ Remote Config ไว้


        สร้างเป็นไฟล์ชื่ออะไรก็ได้ เพราะเดี๋ยวจะต้องไปกำหนดเองในโค้ด Java อยู่ดี

<?xml version="1.0" encoding="utf-8"?>
<defaultMap>
    <entry>
        <key>is_special_user</key>
        <value>false</value>
    </entry>

    ...

</defaultMap>

        อันนี้คือตัวอย่างค่า XML ที่เจ้าของบล็อกเตรียมไว้ในแอปฯ มีค่าเป็น Boolean และถ้ามีค่าหลายๆตัวก็ต้องสร้าง <entry></entry> หลายๆตัว

        พอสร้าง XML ไว้แล้วก็มากำหนดไว้ในโค้ดซะ

firebaseRemoteConfig.setDefaults(R.xml.remote_config_default);

        โดยปกติแล้ว Remote Config จะกำหนดไว้ Default ว่าให้อัปเดตค่าจาก Server ทุกๆ 12 ชั่วโมง ซึ่งก็คงไม่สนุกซักเท่าไร ถ้าเจ้าของบล็อกกำลังพัฒนาแอปฯตัวนี้อยู่ ดังนั้นตัว SDK จึงสามารถกำหนดเป็น Developer Mode ชั่วคราวได้ เพื่อให้มันอัปเดตข้อมูลจาก Server โดยทันที

FirebaseRemoteConfigSettings remoteConfigSettings = new FirebaseRemoteConfigSettings.Builder()
        .setDeveloperModeEnabled(true)
        .build();
firebaseRemoteConfig.setConfigSettings(remoteConfigSettings);

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

        เมื่อ Fetch แล้วพบว่ามีการอัปเดตจาก Server ถึงแม้ว่ารับค่าจาก Server มาแล้ว แต่ค่าเหล่านั้นจะยังไม่มีผลในทันที ต้องใช้คำสั่ง activateFetched ก่อนทุกครั้ง ค่าที่อัปเดตใหม่ถึงจะมีผล ซึ่งตรงนี้เจ้าของบล็อกเข้าใจว่าทำเผื่อในกรณีที่แอปฯอยากจะให้ผู้ใช้เลือกว่าจะอัปเดตค่าบางอย่างของ Remote Config หรือไม่ เช่น ให้ผู้ใช้ยินยอมที่จะทดสอบใช้งานฟีเจอร์ใหม่ที่ยังพัฒนาไม่เสร็จหรือไม่ โดยที่ค่าจาก Remote Config จะไปเปลี่ยน Flag บางอย่างเพื่อให้ผู้ใช้คนนั้นสามารถทดลองใช้งานฟีเจอร์ใหม่ๆที่ว่าได้

firebaseRemoteConfig.fetch().addOnCompleteListener(new OnCompleteListener() {
    @Override
    public void onComplete(@NonNull Task task) {
        if(task.isSuccessful()) {
            firebaseRemoteConfig.activateFetched();
        }
    }
});

        แต่ในกรณีนี้เจ้าของบล็อกจะสั่งให้ Fetch ข้อมูลจาก Server แล้ว กำหนดให้ค่าที่อัปเดตใหม่นั้นมีผลทันที

        และก่อนจะเริ่มคำสั่งต่อไป ให้ไปเปิดหน้า Remote Config ใน Firebase Console เสียก่อน โดยเจ้าของบล็อกจะต้องเพิ่ม Parameter บน Server ให้ตรงกับที่สร้างไว้ใน XML



        หลังจากเพิ่มข้อมูลหรือแก้ไขค่าเสร็จแล้วให้กดปุ่ม Publsh Changes ด้วยทุกครั้ง ไม่เช่นนั้นค่าที่กำหนดไว้ไม่ส่งผลใดๆ

        และเวลาต้องการเรียกใช้งานค่าจาก Remote Config เมื่อใดก็ตามก็แค่เรียกคำสั่งแบบนี้ได้เลย

boolean isSpecialUSer = firebaseRemoteConfig.getBoolean("is_special_user");

        เหมือน Shared Preference ใช่มั้ยล่ะ!! แค่กำหนดคำสั่งให้ถูกต้องว่าค่าเหล่านั้นเป็นชนิดไหน ซึ่งในตัวอย่างเจ้าของบล็อกกำหนดเป็น Boolean และที่สำคัญก็คืออย่าลืมกำหนด Key ให้ถูกต้องด้วย (ในที่นี้คือ is_special_user ซึ่งอิงจากที่สร้างไว้ใน Firebase Console และที่กำหนดไว้ใน XML)

        เพียงเท่านี้ก็ทำให้แอปฯสามารถเปลี่ยนรูปแบบการทำงานตามต้องการได้ เพียงแค่กำหนดโค้ดเหล่านี้เตรียมไว้ และเมื่อใดที่ต้องการเปิดใช้งานก็แค่เข้าไปแก้ไขค่าที่อยู่ใน Firebase Console


        เวลาแก้ไขค่าใดๆในนี้ อย่าลืมกดปุ่ม Publish Changes ด้วยล่ะ!!

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



ครบแล้ววววว!!

        ถึงแม้ว่าจะไม่ใช่การใช้งานบริการ Firebase ทุกตัว แต่ที่หยิบมาพูดใน Session ก็เป็นความสามารถหลักๆของ Firebase ที่ทำให้หลายๆคนรู้สึกว่ามันน่าสนใจมากๆเลยล่ะ ทั้งการใช้งานที่ง่าย โค้ดสั้น มี Assistant ที่แทบไม่ต้องทำอะไรให้ยุ่งยากเหมือนสมัยก่อน และที่สำคัญคือแทบไม่ต้องทำ Web Service เลย สามารถสร้างแอปฯแชทแบบง่ายๆโดยใช้เวลาเพียงไม่กี่นาทีเท่านั้นเอง

        สำหรับ Codelabs ตัวนี้เจ้าของบล็อกก็โยนขึ้น Github ไว้เรียบร้อยแล้ว (ก่อนวันงานอีก) ก็สามารถเข้าไปดูตัวอย่างโค้ดกันได้ที่ Firebase Android Codelabs [Github] และอย่างที่บอกไว้ในบทความตอนที่ 1 ว่า Repository ตัวนี้จะทำไว้ 2 Branch สำหรับผู้ที่หลงเข้ามาอ่านที่อยากดูโค้ดทั้งหมด และอยากจะลองเขียนใช้งาน Firebase ด้วยตัวเอง

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


        แนะนำว่าให้เปิดหน้าต่าง TODO ของ Android Studio เพราะมันจะช่วยให้สามารถหาตำแหน่งที่ต้องใส่โค้ดได้ง่ายขึ้น


        ขอให้สนุกสนานไปกับ Firebase นะครับ :D