บทที่ 2 ทฤษฏแี ละเอกสารทเ่ี กย่ี วขอ้ ง 2.1 ลักษณะการดาเนินการ 2.1.1 ระบบงานเดิม ข้อมูลการจัดการชาระเงินยังไม่มรี ะบบฐานขอ้ มลู การชาระเงินด้ วยมอื ถือจึงทาให้การชาระเงินแบบเดิมน้นั เปน็ การการชาระเงินท่ีล่าช้ ามากจึงทาให้การรวมยอดเงินเกิดการผดิ เพ้ยื นหรอื การคิดเงินใหก้ ับลู กค้าอาจจะผดิ เพ้ียนเหมือนกันและการชาระเงินแบบเดิมไม่สะดวกต่อ กับลูกค้าที่มาซ้ือสนิ ค้าท่ีร้านเป็นอยา่ งมาก 2.1.2 ระบบงานใหม่ ระบบงานการชาระเงินของร้านขายของชาผจู้ ัดทาได้นาโปรแกร ม Kodular มาทาระบบการชาระเงินของรา้ นขายของชา โดยเน้ือหาขอ้ มูลประกอบไปด้วย การชาระเงิน รวมยอดเงิน เพ่อื ใหร้ ะบบมีความทันสมัยต่อการชาระเงิน และอีกยังทาใหเ้ กิดความสะดวกรวดเร็วในการชาระเงิน รวมยอดเงิน 2.2**โปรแกรมเพ่ือการพฒั นาระบบงาน 2.2.1*โปรแกรม KODULAR ฑฌ ฤ 2.2.1.1 สว่ นประกอบของ Kodular
1. Menu เป็ นแหลง่ รวมคำสง่ั ทเี่ อำไวใ้ ชส้ ำหรบั แอปพลเิ คชนั น้ัน ดงั นี้ ● Project เป็ นเมนูสำหรบั จดั กำร Project เชน่ สรำ้ ง Project ใหม่ หรอื ลบ Project เป็ นตน้ ● Test ใชแ้ สดงผลกำรทำงำนเมอื่ จำลองผำ่ นมอื ถอื จรงิ ๆ ● Export สำมำรถนำ Project เรำแปลงเป็ นไฟล ์ APK ไฟลท์ สี่ ำมำรถตดิ ตง้ั บนมอื ถอื ● Help เมอื่ มปี ัญหำสำมำรถแจง้ ได ้ ● My Project แหล่งรวบรวม Project ของเรำทสี่ รำ้ งไว ้ ● Community แหลง่ พูดคยุ แลกเปลย่ี น ถำมปัญหำเกยี่ วกบั Kodular ● Docs อธบิ ำนกำรทำงำนใหผ้ เู้ รยี นศกึ ษำ ● Donate ถำ้ ผใู้ ชต้ อ้ งกำรสนบั สนุนทมี งำนสรำ้ ง Kodular สำมำรถโอนเงนิ ไดห้ ลำกหลำยชอ่ งทำง ● Account ใชจ้ ดั กำรบญั ชี Kodular ของเรำ 2. Design ใชอ้ อกแบบหนำ้ จอแอปพลเิ คชนั ทเี่ รำจะสรำ้ ง 3. Screen แสดงผลกำรออกแบบหนำ้ จอแอปพลเิ คชนั ใชส้ ำหรบั ลำกวำงสว่ นประกอบ (Component) 4. Blocks ใชอ้ อกแบบกำรทำงำนของแอปพลเิ คชนั ลำก - วำง บล็อคกำรทำงำน ทเี่ ปรยี บเสมอื กำรเขยี น Code 5. Property ใชก้ ำหนดคณุ สมบตั ขิ องแอปพลเิ คชนั และกำหนดคณุ สมบตั ขิ องสว่ นประกอบ (Component) ต่ำงๆ
2.2.2 วธิ กี ารใชง้ าน Kodular 1 ลงทะเบยี น เขา้ ใชง้ านที่ Kodular ทาการลงทะเบยี น และเขา้ สูร่ ะบบ 2 สรา้ ง Project คลกิ ที่ Create New Project และทำกำรตง้ั ชอื่ Project
3 ออกแบบหน้าแอปพลเิ คชนั ออกแบบหนำ้ จอแอปพลเิ คชนั ตำมทเี่ รำตอ้ งกำร เพยี งแคล่ ำก-วำงสว่ นประกอบ (Component) ทมี่ ใี น Kodular เทำ่ นน้ั 4 ออกแบบการทางานของแอปพลเิ คชนั ถำ้ ตอ้ งกำรใหแ้ อปพลเิ คชนั ทำงำนตำมตอ้ งกำร สำมำรถลำก-วำงบล็อค มำตอ่ กนั แบบจกิ๊ ซอล ซงึ่ ผพู้ ฒั นำ Kodular มี Kodular Docs และ Kodular Video ทเี่ ป็ นภำษำองั กฤษ เพอื่ อธบิ ำยกำรทำงำนและกำรใชง้ ำนบล็อคแต่ละบล็อคเอำไว ้
5 ตรวจสอบการทางาน Kodular Companion บนโทรศพั ทม์ อื ถอื หรอื แท็บเล็ต เพยี งแคส่ แกน QR code ทเี่ มนู Test และคลกิ ที่ Connect to companion จำก Project ทเี่ รำสรำ้ ง ขอ้ ดขี อง Kodular
1. Kodular เปิ ดใหใ้ ช ้ ฟรี 2. ใชง้ ำนง่ำย และทำควำมเขำ้ ใจกำรทำงำนของ Kodular ไมย่ ำก 3. เมอ่ื มปี ัญหำไมเ่ ขำ้ ใจสำมำรถถำมไดต้ ลอดเวลำที่ Kodular Community 4. Kodular ยงั เหมำะสำหรบั ผูท้ อ่ี ยำกเรยี นรูท้ กั ษะกำรเขยี นโปรแกรมเบอื้ งตน้ อกี ดว้ ย และมแี หล่งเรยี นรู ้ Kodular Docs และ Kodular Video ทเ่ี ป็ นภำษำองั กฤษ 2.2.2 โปรแกรม Firebase 2.2.2.1 ขอ้ มลู Firebase Part 1 กำร Set up Firebase และ Realtime Database SDK Firebase Realtime Database เป็ น NoSQL cloud database ทเี่ กบ็ ขอ้ มูลในรปู แบบของ JSON และมกี ำร sync ขอ้ มลู แบบ realtime กบั ทุก devices ทเี่ ชอื่ มต่อแบบอตั โนมตั ใิ นเสยี้ ววนิ ำที รองรบั กำรทำงำนเมอื่ offline(ขอ้ มูลจะถกู เกบ็ ไวใ้ น local จนกระท่งั กลบั มำ online ก็จะทำกำร sync
ขอ้ มลู ใหอ้ ตั โนมตั )ิ รวมถงึ มี Security Rules ใหเ้ รำสำมำรถออกแบบเงอ่ื นไขกำรเขำ้ ถงึ ขอ้ มลู ทง้ั กำร read และ write ไดด้ งั ใจ ทงั้ Android, iOS และ Web ในการพฒั นา Firebase Realtime Database ขอแยกออกเป็ น 5 parts ดงั นี้ 1. กำร Set up Firebase และ Realtime Database SDK 2. กำรเขยี นขอ้ มลู 3. กำรอ่ำนขอ้ มลู 4. กำรเปิ ดใชง้ ำนโหมด Offline 5. Security & Rules 2.2.2.2 วธิ กี ำรใชง้ ำน เมอื่ Set up Firebase เรยี บรอ้ ยแลว้ ก็ใหเ้ พมิ่ Realtime Database SDK ใน build.gradle ของ app-level แลว้ กด Sync ก็เป็ นอนั จบสว่ นที่ 1 ละ
dependencies { compile 'com.google.firebase:firebase-database:11.8.0' } กำรเขำ้ ถงึ ขอ้ มลู สำหรบั Firebase Realtime Database ทงั้ read และ write โดยปกติ เรำจะตอ้ งทำกำร Authentication ผ่ำน Firebase Authentication ซะกอ่ น แตเ่ พอื่ ใหเ้ รำสำมำรถเขำ้ ใจบทควำมนีไ้ ดโ้ ดยไม่ตอ้ งอำ่ น Firebase Authentication เรำจะมำทำใหม้ นั เขำ้ ถงึ ไดแ้ บบ public กนั โดยใหเ้ ขำ้ ไปที่ Firebase Console เขำ้ ไปทโี่ ปรเจค จำกน้ันเลอื กเมนู Database แลว้ เลอื ก tab ทชี่ อื่ วำ่ RULES จะพบหนำ้ ตำของประมำณนี้
ดำ้ นขวำมอื จะมี simulator ใหล้ องทดสอบ rules ทเี่ รำสรำ้ งขนึ้ ทงั้ แบบ public หรอื แบบ authentication แลว้ กด็ ี ดงั นั้น ลองกด RUN แบบ default rules กอ่ นเลย ผลปรำกฎว่ำถำ้ ไมไ่ ด ้ authentication ก็จะไม่สำมำรถเขำ้ ถงึ ขอ้ มลู ไดน้ ่ันเอง ดงั รูป
เอำหละ ต่อไปเรำมำปรบั ใหม้ นั เป็ น public กนั ดกี ว่ำ โดยใหเ้ ปลยี่ น rules ตำมนี้
Part 2 กำรเขยี นขอ้ มลู (Write) เรมิ่ ดว้ ยกำรประกำศตวั แปร DatabaseReference รบั คำ่ Instance และอำ้ งถงึ path ทเี่ รำตอ้ งกำรใน database DatabaseReference mRootRef = FirebaseDatabase.getInstance().getReference();
จำกน้ันก็อำ้ งองิ ไปที่ path ทเี่ รำตอ้ งกำรจะจดั กำรขอ้ มูล ตวั อย่ำงคอื users และ messages DatabaseReference mUsersRef = mRootRef.child(\"users\"); DatabaseReference mMessagesRef = mRootRef.child(\"messages\"); กำรเขยี นขอ้ มูล (Write) กำร write, update หรอื delete ขอ้ มูลใน Firebase Realtime Database จะรองรบั คำ่ หลำยประเภททง้ั String, Long, Double, Boolean, Map<String, Object> และ List<Object> โดยกำร write จะมดี ว้ ยกนั 4 รปู แบบดงั นี้ 1. setValue() เป็ นกำร write หรอื update ขอ้ มูล ไปยงั path ทเี่ รำอำ้ งถงึ ได ้ เชน่ users/<user-id>/<username> mUsersRef.child(\"id-12345\").setValue(\"Jirawatee\");
เรำสำมำรถดผู ลลพั ธแ์ บบ realtime ไดท้ ี่ Firebase Console โดยไปทเี่ มนู Database แลว้ เลอื ก tab แรก คอื Data เรำกจ็ ะเห็นขอ้ มลู ทงั้ หมดแบบทกุ ลมหำยใจละ 2. push() เป็ นกำรเพมิ่ ชดุ ของขอ้ มลู ในทนี่ ีผ้ มจะสรำ้ ง model object ชอื่ FriendlyMessage ซงึ่ จะบรรจุ text และ username ไว ้ โดยกำร push นั้น Firebase จะสรำ้ ง unique key ของชดุ ขอ้ มูลนั้นๆ เพอื่ ใชอ้ ำ้ งองิ ต่อไปได ้ เชน่ messages/<message-id>/<data-model>
FriendlyMessage friendlyMessage = new FriendlyMessage(\"Hello World!\", \"Jirawatee\"); mMessageRef.push().setValue(friendlyMessage); 3. updateChildren() เป็ นกำร write หรอื update ขอ้ มลู บำงสว่ น(บำง key) ตำม path ทเี่ รำอำ้ งถงึ โดยไม่ตอ้ งทำกำร replace ขอ้ มูลทงั้ ชดุ และสำมำรถทำพรอ้ มๆกนั ไดห้ ลำย object ตวั อยำ่ งจะเป็ นกำรสรำ้ ง post ใหมข่ นึ้ มำ โดยจะ write ขอ้ มลู ไป 2 ทคี่ อื
/user-messages/Jirawatee/$postid และ /messages/$postid // push เป็ นกำร generate $postid ของ object ชอื่ posts ออกมำกอ่ นเพอื่ ใชใ้ น // /user- posts/$userid/$postid String key = mMessagesRef.push().getKey(); HashMap<String, Object> postValues = new HashMap<>(); postValues.put(\"username\", \"Jirawatee\"); postValues.put(\"text\", \"Hello World!\"); Map<String, Object> childUpdates = new HashMap<>();
childUpdates.put(\"/messages/\" + key, postValues); childUpdates.put(\"/user-messages/Jirawatee/\" + key, postValues); rootRef.updateChildren(childUpdates); สที แี่ สดงใน console จะบอกสถำนะแตกต่ำงกนั ดงั นี ้ สเี หลอื ง: แสดงกำรอพั เดท สเี ขยี ว: แสดงกำรเพมิ่ สแี ดง: แสดงกำรลบ สนี ำ้ เงนิ : แสดงกำรเคลอื่ นยำ้ ย
ในกรณีทตี่ อ้ งกำรอพั เดทขอ้ มูลบำงส่วน ก็สำมำรถทำไดพ้ รอ้ มๆกนั ได ้ โดยจะตอ้ งรู ้ username และ message-id เป็ นตวั ระบุในแต่ละ object
4. runTransaction() เป็ นกำรอพั เดทขอ้ มูล ทมี่ ี concurrent เยอะๆ ทอี่ ำจเกดิ ชนกนั เกดิ ขอ้ ผดิ พลำดได ้ ตวั อยำ่ งเชน่ กำรกด like และกด unlike ทโี่ พสเดยี วกนั เวลำเดยี วกนั จะตอ้ งมกี ำรนับยอด like ตลอดเวลำ ว่ำชว่ งเวลำน้ันเป็ นเทำ่ ไร postRef.runTransaction(new Transaction.Handler() { @Override public Transaction.Result doTransaction(MutableData mutable) {
Post p = mutable.getValue(Post.class); if (p == null) { return Transaction.success(mutable); } if (p.stars.containsKey(getUid())) { // Unlike the post and remove self from likes p.starCount = p.starCount - 1; p.stars.remove(getUid()); } else { // Like the post and add self to likes
p.starCount = p.starCount + 1; p.stars.put(getUid(), true); } // Set value and report transaction success mutable.setValue(p); return Transaction.success(mutable); } @Override public void onComplete(DatabaseError databaseError, boolean b, DataSnapshot dataSnapshot) {
if (databaseError != null) { databaseError.getMessage() Log.w(TAG, databaseError.getMessage()); } else { Log.d(TAG, \"Transaction successful\"); } } }); กำรลบขอ้ มลู (Delete) กำรลบขอ้ มลู นั้น ใหเ้ รำระบุ path ทเี่ รำตอ้ งกำรจะลบ จำกน้ันกเ็ รยี กคำสง่ั removeValue() ตวั อย่ำงเชน่ ตอ้ งกำรลบขอ้ ควำมทง้ั หมดใน object ชอื่ messages
mMessageRef.removeValue(); นอกจำกนั้นเรำยงั สำมำรถลบขอ้ มลู ไดด้ ว้ ยกำรส่งคำ่ null ไปที่ setValue(null) และสำมำรถใชค้ ่ำ null กบั เทคนิค updateChildren() เพอื่ ลบขอ้ มลู หลำยๆ object ไดด้ ว้ ย // ลบแบบ setValue() mMessageRef.setValue(null); // ลบแบบ updateChildren(); childUpdates.put(\"/messages/\", null); Part 3 กำรอำ่ นขอ้ มูล (Read) เรมิ่ ดว้ ยกำรประกำศตวั แปร DatabaseReference รบั คำ่ Instance และอำ้ งถงึ path ทเี่ รำตอ้ งกำรใน database
DatabaseReference mRootRef = FirebaseDatabase.getInstance().getReference(); กำรอำ่ นขอ้ มลู ใน Firebase Realtime Database จะมี 2 ประเภทแยกตำม Listener ดงั นี้ 1. ValueEventListener จะอำ่ นขอ้ มลู ตง้ั แต่เรมิ่ และ จะอำ่ นขอ้ มลู ทุกครง้ั ทมี่ กี ำรเปลยี่ นแปลงของขอ้ มูลทง้ั หมดภำยใต ้ path ทเี่ รำอำ้ งถงึ วธิ กี ำรคอื ใช ้ object ทเี่ รำอำ้ งถงึ มำ addValueEventListener โดยจะมี callback 2 แบบ ● onDataChange จะถกู เรยี กตอนเรมิ่ และถูกเรยี กทกุ ครง้ั ทขี่ อ้ มลู ภำยใต ้ path ทเี่ รำอำ้ งถงึ มกี ำรเปลยี่ นแปลง ● onCancelled จะถกู เรยี กเมอื่ ไม่สำมำรถอำ่ นขอ้ มูลจำก database ได ้
mRootRef.addValueEventListener(new ValueEventListener() { @Override public void onDataChange(DataSnapshot dataSnapshot) { String value = dataSnapshot.getValue(String.class); mTextView.setText(value); } @Override public void onCancelled(DatabaseError error) { mTextView.setText(\"Failed: \" + databaseError.getMessage()); }
}); ขอ้ ควรระวงั ของ ValueEventListener คอื ไม่ควรอำ้ งถงึ root เนือ่ งจำกมนั จะคอยดูขอ้ มูลทงั้ หมดของ database ซงึ่ มนั อำจมขี นำดใหญ่ และ เปลอื ง bandwidth โดยใชเ่ หตุ เรำจงึ ควรใชว้ ธิ นี ีแ้ บบเจำะจงเฉพำะ หรอื อ่ำนขอ้ มูลทงั้ หมดครงั้ แรก แลว้ removeEventListener() ออกไป 2. ChildEventListener จะคอยรบั ขอ้ มูลจำก กำรเพมิ่ , กำรเปลยี่ นแปลง, กำรลบ และ กำรยำ้ ย เฉพำะของ child ทเี่ รำอำ้ งถงึ วธิ กี ำรคอื ใช ้ object ทเี่ รำอำ้ งถงึ มำ addChildEventListener โดยจะมี callback 5 แบบ ● onChildAdded() จะถกู เรยี กเมอื่ มกี ำรเพมิ่ ชดุ ขอ้ มลู เขำ้ มำใน child ● onChildChanged() จะถกู เรยี กเมอื่ ขอ้ มูลใน child มกี ำรเปลยี่ นแปลง ● onChildRemoved() จะถกู เรยี กเมอื่ ขอ้ มูลใน child ถกู ลบ
● onChildMoved() จะถกู เรยี กเมอื่ มกี ำรเรยี งลำดบั ของขอ้ มูลใน child เกดิ ขนึ้ ● onCancelled() จะถกู เรยี กเมอื่ โหลดขอ้ มูลจำก child ไมส่ ำเรจ็ ตวั อย่ำงกำร addChildEventListener ไปกบั กำร comment ซงึ่ เหมำะมำกกบั RecyclerView ChildEventListener childEventListener = new ChildEventListener() { @Override public void onChildAdded(DataSnapshot dataSnapshot, String previousChildName) { // A new comment has been added, add it to the displayed list Comment comment = dataSnapshot.getValue(Comment.class); // ...
} @Override public void onChildChanged(DataSnapshot dataSnapshot, String previousChildName) { // A comment has changed, use the key // to determine if we are displaying this // comment and if so displayed the changed comment. Comment newComment = dataSnapshot.getValue(Comment.class); String commentKey = dataSnapshot.getKey(); // ... }
@Override public void onChildRemoved(DataSnapshot dataSnapshot) { // A comment has changed, use the key // to determine if we are displaying this // comment and if so remove it. String commentKey = dataSnapshot.getKey(); // ... } @Override
public void onChildMoved(DataSnapshot dataSnapshot, String previousChildName) { // A comment has changed position, // use the key to determine if we are // displaying this comment and if so move it. Comment movedComment = dataSnapshot.getValue(Comment.class); String commentKey = dataSnapshot.getKey(); // ... } @Override public void onCancelled(DatabaseError databaseError) {
Toast.makeText(mContext, \"Failed to load comments.\", Toast.LENGTH_SHORT).show(); } }; ref.addChildEventListener(childEventListener); กำรเพมิ่ listener เขำ้ ไปหลำยตวั นัน่ ก็แปลวำ่ จะตอ้ งมกี ำร call เกดิ ขนึ ้ มำกมำยตำมแต่ event ซงึ่ หำกเรำไม่ไดใ้ ช ้ หรอื ออกจำกหนำ้ ดงั กลำ่ ว กค็ วรจะถอด listener เหลำ่ นัน้ ออกไปดว้ ย เพอื่ กำรใช ้ bandwidth แบบคมุ้ ค่ำมำกทสี่ ดุ โดยสำมำรถถอดออกไดด้ ว้ ยคำสงั่ removeEventListener() @Override protected void onStop() { super.onStop();
if (mValueEventListener != null) { mRootRef.removeEventListener(mValueEventListener); } } กำรอำ่ นขอ้ มูลเพยี งครงั้ เดยี ว บำงครง้ั เรำอำจตอ้ งกำรอ่ำนขอ้ มลู แค่ครง้ั เดยี วและกไ็ มส่ นใจมนั อกี Firebase ไดเ้ ตรยี ม addListenerForSingleValueEvent() ทเี่ มอื่ ได ้ callback แลว้ ก็จะ remove listener ทงิ้ อตั โนมตั ิ ตวั อย่ำงเชน่ ดงึ ขอ้ มลู ผใู้ ชก้ อ่ นทำกำรโพส วำ่ เขำผำ่ นกำร sign-in มำหรอื ยงั หรอื ยงั ขำดขอ้ มลู อะไรทตี่ อ้ งกำร หำกครบถว้ นก็โพสได ้ แต่หำกไม่ครบอำจพำไปหนำ้ sign-in หรอื พำไปหนำ้ กรอกขอ้ มูลใหค้ รบถว้ น mDatabase.child(\"users\").child(userId).addListenerForSingleValue Event(new ValueEventListener() {
@Override public void onDataChange(DataSnapshot dataSnapshot) { User user = dataSnapshot.getValue(User.class); if (user == null) { Toast.makeText(NewPostActivity.this, \"Error: could not fetch user.\", Toast.LENGTH_LONG).show(); } else { writeNewPost(userId, user.username, title, body); } finish(); }
@Override public void onCancelled(DatabaseError databaseError) { Log.e(TAG, databaseError.getMessage()); } }); กำรเรยี งลำดบั และกำรกรองขอ้ มลู (Sorting and Filtering) กำร query ขอ้ มูล ของ Firebase Realtime Database นัน้ รองรบั กำร sort และ filter ได้ อยำ่ งก็ดกี ำร sort และ filter สำมำรถทำใหก้ ำร query นัน้ ชำ้ ได้ ทำงทดี่ คี วรศกึ ษำเรอื่ งกำรทำ index (.indexOn) ไปดว้ ยจะทำใหก้ ำร query นัน้ มปี ระสทิ ธภิ ำพมำกขนึ ้ การ Sort ขอ้ มูล มี 3 รูปแบบ ดงั นี้ (Ordering Function)
● orderByChild() เป็ นกำรเรยี งลำดบั value ของ child key ทถี่ กู เลอื ก (กำรใชง้ ำนคลำ้ ย WHERE ใน SQL) ● orderByKey() เป็ นกำรเรยี งลำดบั child key (ใชเ้ รยี ง PK เหมำะกบั แสดงขอ้ มูลแบบมี limit หรอื แสดง pagination) ● orderByValue() เป็ นกำรเรยี งลำดบั child value (เหมำะกบั กำรเรยี งค่ำตวั เลข) // ตวั อยำ่ งกำรเรยี งลำดบั โพสของฉันทไี่ ดร้ บั คะแนนโหวตมำกทสี่ ุด dbReference.child(\"user- posts\").child(getUid()).orderByChild(\"starCount\"); การ Filter ขอ้ มูล มี 5 รูปแบบ ดงั นี้ (Querying Function) ● limitToFirst() กำรระบุจำนวน item ซงึ่ จะเรยี งลำดบั จำกแถวแรก ● limitToLast() กำรระบจุ ำนวน item ซงึ่ จะเรยี งลำดบั จำกแถวสดุ ทำ้ ย
● startAt() จะดงึ จำนวน item ทมี่ ำกกว่ำ หรอื เท่ำกบั ทรี่ ะบุ key หรอื value โดยขนึ้ อยูก่ บั กำร order-by ● endAt() จะดงึ จำนวน item ทนี่ อ้ ยกวำ่ หรอื เทำ่ กบั ทรี่ ะบใุ น key หรอื value โดยขนึ้ อยกู่ บั กำร order-by ● equalTo() จะดงึ จำนวน item ทเี่ ท่ำกบั ทรี่ ะบใุ น key หรอื value โดยขนึ้ อย่กู บั กำร order-by Part 4 กำรเปิ ดใชง้ ำนโหมด offline ลกั ษณะกำรทำงำน offline (Persistence Behavior) Firebase Realtime Database มกี ำรจดั กำรเรอื่ งกำรเชอื่ มต่อใหแ้ ลว้ เมอื่ ไมส่ ำมำรถเชอื่ มตอ่ อนิ เตอรเ์น็ต หรอื อยใู่ นโหมด offline เรำยงั สำมำรถใชง้ ำนแอพได ้ โดยตวั Firebase จะทำกำร cache ขอ้ มลู ไวท้ ุกกำรกระทำ และจะทำกำร sync ขอ้ มลู ใหอ้ ตั โนมตั เิ มอื่ เรำกลบั เขำ้ สโู่ หมด online
แมว้ ่ำเรำจะปิ ดแลว้ เปิ ดแอพใหม่ก็ตำม มนั ยอดมำกเลย แค่ประกำศใชง้ ำนง่ำยๆเพยี งบรรทดั เดยี ว FirebaseDatabase.getInstance().setPersistenceEnabled(true); ปัญหำทหี่ ลำยคนประกำศ .setPersistenceEnabled แลว้ เมอื่ ออกและเขำ้ มำใหมป่ รำกฏวำ่ crash โดยแสดง error ประมำณนี ้ com.google.firebase.database.DatabaseException: Calls to setPersistenceEnabled() must be made before any other usage of FirebaseDatabase instance ผเู้ ขยี นแนะนำใหแ้ กป้ ัญหำนีโ้ ดย
1. สรำ้ ง class ที่ extends Application แลว้ ไปประกำศ setPersistenceEnabled(true) ทนี่ ัน่ class MyApp extends android.app.Application @Override public void onCreate() { super.onCreate(); FirebaseDatabase.getInstance().setPersistenceEnable d(true); } 2. ใน AndroidManifest ใหไ้ ปประกำศ class ในขอ้ 1 ใน <application> ซะ
android:name=\"com.example.MyApp\" ผเู้ ขยี นทดสอบแลว้ ประกำศทเี่ ดยี ว จบ ************************************************* ****************** แอบ sync ขอ้ มูลแบบลบั ๆ (Keeping Data Fresh) Firebase Realtime Database จะ sync และเกบ็ ขอ้ มลู ใน local ของ client เฉพำะขอ้ มลู ทตี่ วั listener ทำงำนอยู่ แตเ่ รำสำมำรถจะ sync ขอ้ มลู สว่ นอนื่ ทยี่ งั ไม่ได ้ active ได ้ โดยใหอ้ ำ้ งถงึ path ทตี่ อ้ งกำร sync ตวั อยำ่ งเชน่ DatabaseReference scoresRef = FirebaseDatabase.getInstance().getReference(\"scores \"); scoresRef.keepSynced(true);
และโดยทว่ั ไปเรำจะเกบ็ ขอ้ มูลลง cache ไดไ้ ม่เกนิ 10MB แต่หำกแอพมขี อ้ มลู ทตี่ อ้ งกำร cache มำกกว่ำนั้น Firebase Realtime Database จะ purge cache ออก แตข่ อ้ มลู ทเี่ ก็บแบบ keepSynced จะไม่ถกู purge นะเออ กำร Query ขอ้ มูลในขณะที่ Offline เมอื่ แอพ sync ขอ้ มลู ตอน online มำไวใ้ นเครอื่ งแลว้ และเมอื่ แอพเขำ้ สโู่ หมด offline เรำกย็ งั สำมำรถ query ขอ้ มูลที่ sync มำแลว้ ได ้ ตวั อยำ่ งเชน่ // ตอน online ไดผ้ ลลพั ธ ์ 4 แถว จำกกำร query scoresRef.orderByValue().limitToLast(4) // เมอื่ offline หำกตอ้ งกำร query ผลลพั ธ ์ 2 แถว กท็ ำได ้
scoresRef.orderByValue().limitToLast(2) กำร run ตวั Transactions ขณะ Offline จรงิ ๆแลว้ กเ็ หมอื นกบั กรณี offline ปกติ คอื เมอื่ run ตวั transaction ในขณะ offline ขอ้ มลู ต่ำงๆกจ็ ะถกู เขำ้ ควิ ไว ้ แต่หำกมกี ำรปิ ดแลว้ เปิ ดแอพใหม่ควิ จะไม่ถกู เกบ็ ไว ้ โดยเรำควรแจง้ ผใู้ ชใ้ หท้ รำบว่ำ transaction ไมส่ ำเรจ็ ในกรณีทปี่ ิ ดแลว้ เปิ ดแอพอกี ครงั้ Part 5 Security & Rules Firebase Realtime Database Rules เป็ นกำรกำหนดกำรเขำ้ ถงึ database ทงั้ กำร read, write และกำรทำ index โดย rules ทง้ั หมดจะอยบู่ น Firebase server แลว้ เรำสำมำรถปรบั เปลยี่ นและมผี ลทนั ทไี ดต้ ลอดเวลำ ซงึ่ มี syntax เป็ น Javascript-like โดยจะแบง่ rules ออกเป็ น 4 ประเภทดงั นี้
1. .read คอื กำรอนุญำตใหอ้ ำ่ น 2. .write คอื กำรอนุญำตใหเ้ ขยี น 3. .validate คอื กำรตรวจสอบขอ้ มูลทเี่ ขำ้ มำ 4. .indexOn คอื กำรทำ index เพอื่ ควำมรวดเรว็ ในกำรเรยี งลำดบั และกำร query โดยกำรกำหนด rules ตำ่ งๆใหเ้ ขำ้ ไปที่ Firebase Console เลอื กโปรเจค จำกนั้นเลอื กเมนู Database แลว้ เลอื ก tab ชอื่ RULES สำหรบั รำยละเอยี ด Security & Rules นัน้ มมี ำกมำย เชน่ กำรกำหนด rules สำหรบั กำร read และ write ของแต่ละ path ทยี่ ดึ ตำมโครงสรำ้ งของ database, กำรกำหนด rules สำหรบั กำร validate กำรเพมิ่ ขอ้ มูล หรอื อพั เดทขอ้ มลู และกำรใหเ้ ขำ้ ถงึ ขอ้ มลู ตวั เองของผูใ้ ชค้ นนัน้ ๆ เป็ นตน้
แนะนำใหไ้ ปศกึ ษำเพมิ่ เตมิ ตำมลงิ คน์ ีค้ รบั https://firebase.google.com/docs/database/security/ รบั รองวำ่ มนั ไมย่ ำก แค่มนั ไม่ง่ำยแค่นัน้ เอง เคล็ดไม่ลบั รบั Callback หลงั จำก setValue() หรอื updateChildren() เรำสำมำรถ add completion callback ไดท้ ง้ั setValue() และ updateChildren() ได ้ เผอื่ ใครอยำกจะ handle กรณีทไี่ ม่สำเรจ็ ดงั ตวั อย่ำงดำ้ นล่ำง mUsersRef.child(\"id-12345\").setValue(\"Jirawatee\", new DatabaseReference.CompletionListener() { @Override public void onComplete(DatabaseError databaseError, DatabaseReference databaseReference) {
if (databaseError != null) { Log.w(TAG, \"setValue() failure\"); } else { Log.d(TAG, \"setValue() successful\"); } } }); กำร Minify หำกทำกำร minify กบั โปรเจค ทมี่ กี ำรใชง้ ำน model object กบั ฟังกช์ นั DataSnapshot.getValue(Class) หรอื DatabaseReference.setValue(Object) เรำจำเป็ นตอ้ งเพมิ่ code ดำ้ นลำ่ งนีใ้ นไฟล ์ proguard- rules.pro ดว้ ยนะ
# Add this global rule -keepattributes Signature # This rule will properly ProGuard all the model classes in # the package com.yourcompany.models. Modify to fit the structure # of your app. -keepclassmembers class com.yourcompany.models.** { *; } Best practice ในกำรออกแบบโครงสรำ้ งของขอ้ มูล เรำควรหลกี เลยี่ งกำรซอ้ นกนั ของ data เพรำะ Firebase Realtime Database อนุญำตใหเ้ รำออกแบบ data
ซอ้ นกนั ไดไ้ ม่เกนิ 32 ชน้ั เพรำะหำกเรำ fetch ขอ้ มลู จำก database ทมี่ ขี อ้ มูลซอ้ นกนั เยอะ เรำอำจตอ้ ง fetch ขอ้ มูลจำนวนมำก ทำใหเ้ ปลอื ง bandwidth ได ้ ควรออกแบบโครงสรำ้ งแบบ flat เพอื่ ขนำด data ทเี่ ล็กและเรว็ ในกำร fetch ซงึ่ ประสทิ ธภิ ำพทสี่ งู กว่ำนั้น เรำจะตอ้ งแลกดว้ ยกำรไม่ทำ normalize ขอ้ มลู หรอื เรยี กว่ำ denormalize น่ันเอง ขอแค่อยำ่ ลมื วำ่ เมอื่ มกี ำรอพั เดทหรอื ลบขอ้ มูลกต็ อ้ งทำใหค้ รบทุกทที่ เี่ รำเพมิ่ ขอ้ มู ลเหล่ำน้ันเขำ้ ไปดว้ ย
จำกตวั อย่ำงเรำมกี ำร add key มำกกว่ำ 1 ที่ โดยสำมำรถแยกกำร query ออกเป็ น 3 มมุ มองดงั นี้ 1. Query ขอ้ มูลของ user ทง้ั หมดจำก users 2. Query ขอ้ มูลของ content ทง้ั หมดจำก posts
3. Query ขอ้ มูล content ของ user แต่ละคนจำก user-posts ทง้ั 3 แบบเรำสำมำรถ query ขอ้ มลู ไดอ้ ย่ำงมอี ย่ำงรวดเรว็ และยงั สำมำรถทำ offset และ limit ของ data ไดด้ ว้ ย ประเมนิ คำ่ ใชจ้ ำ่ ย Firebase Realtime Database น้ันเป็ นบรกิ ำรทฟี่ รแี บบจำกดั โดยจะมเี รอื่ งใหพ้ จิ ำรณำดว้ ยกนั 4 เรอื่ งคอื 1. จำนวนกำรใชง้ ำนพรอ้ มๆกนั (Connection) 2. พนื้ ทที่ เี่ กบ็ ขอ้ มูล (Storage) 3. ขนำดของกำรสง่ รบั ขอ้ มูล (Bandwidth) 4. กำรสำรองขอ้ มลู (Backup)
โดยสถติ กิ ำรใชง้ ำนทงั้ หมดเรำสำมำรถเชค็ ไดท้ ี่ Firebase Console โดยเขำ้ ไปทเี่ มนู Database แลว้ เลอื ก tab ชอื่ USAGE
และในหนำ้ Pricing ของ Firebase เรำสำมำรถจะประมำณกำรใชง้ ำนไดว้ ำ่ ใชเ้ ทำ่ ไรคดิ เงนิ เทำ่ ไร ตวั อย่ำง ผมจะจำลองกำรใชง้ ำนของแพ็คเกจ SPARK ซงึ่ เป็ นแพ็คเกจฟรี โดย storage 1 GB เกบ็ ขอ้ มูลไดป้ ระมำณ 20 ลำ้ นขอ้ ควำม และ bandwidth 10 GB จะ read และ write ไดป้ ระมำณ 200 ลำ้ นขอ้ ควำม เรยี กไดว้ ำ่ แค่แพ็คเกจฟรี ก็ใชง้ ำนไดเ้ ยอะเลยทเี ดยี ว
หำกแอพของเรำมขี อ้ มูลเยอะ transaction เยอะ กเ็ ลอื กใชต้ ำมแพ็คทมี่ คี ำ่ ใชจ้ ำ่ ยทเี่ หมำะสมตอ่ ไป ดจู ำกรำคำแลว้ เรยี กว่ำคมุ้ คำ่ กบั ควำมเสถยี ร, ควำมปลอดภยั และเวลำในกำรพฒั นำแน่นอน ตวั อยำ่ งพรอ้ มแลว้ ใน GitHub ตวั อยำ่ งมที งั้ แบบกำรใชง้ ำนแบบ authentication กบั public เลยครบั โดยตวั public จะเป็ นตวั basic และตวั authentication จะ advance หน่อย มตี วั อยำ่ งทงั้ แอพ quickstart ของ Firebase ทเี่ อำมำ modify และตวั อยำ่ ง chat app ดว้ ย โหลดเลย
Search