ทำงานกับรายการข้อมูลบน Android

เอกสารนี้ครอบคลุมถึงการทำงานกับรายการข้อมูลใน Firebase หากต้องการเรียนรู้พื้นฐานของการอ่านและเขียนข้อมูล Firebase โปรดดูที่ อ่านและเขียนข้อมูลบน Android

รับฐานข้อมูลอ้างอิง

หากต้องการอ่านและเขียนข้อมูลจากฐานข้อมูล คุณต้องมีอินสแตนซ์ของ DatabaseReference :

Java

private DatabaseReference mDatabase;
// ...
mDatabase = FirebaseDatabase.getInstance().getReference();

Kotlin+KTX

private lateinit var database: DatabaseReference
// ...
database = Firebase.database.reference

อ่านเขียนรายการ

ผนวกเข้ากับรายการข้อมูล

ใช้เมธอด push() เพื่อผนวกข้อมูลเข้ากับรายการในแอพพลิเคชั่นแบบผู้ใช้หลายคน เมธอด push() จะสร้างคีย์ที่ไม่ซ้ำทุกครั้งที่มีการเพิ่มลูกใหม่ในการอ้างอิง Firebase ที่ระบุ ด้วยการใช้คีย์ที่สร้างขึ้นอัตโนมัติเหล่านี้สำหรับองค์ประกอบใหม่แต่ละรายการในรายการ ลูกค้าหลายรายสามารถเพิ่มรายการย่อยไปยังตำแหน่งเดียวกันได้ในเวลาเดียวกันโดยไม่มีข้อขัดแย้งในการเขียน คีย์เฉพาะที่สร้างโดย push() ขึ้นอยู่กับการประทับเวลา ดังนั้นรายการจะถูกเรียงลำดับตามลำดับเวลาโดยอัตโนมัติ

คุณสามารถใช้การอ้างอิงถึงข้อมูลใหม่ที่ส่งคืนโดยเมธอด push() เพื่อรับค่าของคีย์ที่สร้างโดยอัตโนมัติของเด็กหรือชุดข้อมูลสำหรับเด็ก การเรียก getKey() ในการอ้างอิง push() จะคืนค่าของคีย์ที่สร้างขึ้นโดยอัตโนมัติ

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

ฟังเหตุการณ์สำหรับเด็ก

เมื่อทำงานกับรายการ แอปพลิเคชันของคุณควรรับฟังเหตุการณ์ลูกมากกว่าเหตุการณ์ค่าที่ใช้สำหรับวัตถุเดี่ยว

เหตุการณ์ลูกถูกทริกเกอร์เพื่อตอบสนองต่อการดำเนินการเฉพาะที่เกิดขึ้นกับลูกของโหนดจากการดำเนินการ เช่น เด็กใหม่ที่เพิ่มผ่านเมธอด push() หรือรายการย่อยที่ได้รับการอัปเดตผ่าน updateChildren() สิ่งเหล่านี้ร่วมกันมีประโยชน์สำหรับการฟังการเปลี่ยนแปลงโหนดเฉพาะในฐานข้อมูล

ในการฟังเหตุการณ์ย่อยบน DatabaseReference ให้แนบ ChildEventListener :

ผู้ฟัง โทรกลับเหตุการณ์ การใช้งานทั่วไป
ChildEventListener onChildAdded() ดึงข้อมูลรายการหรือฟังรายการเพิ่มเติมในรายการ การเรียกกลับนี้ถูกทริกเกอร์หนึ่งครั้งสำหรับเด็กที่มีอยู่แต่ละคน และอีกครั้งทุกครั้งที่มีการเพิ่มลูกใหม่ไปยังเส้นทางที่ระบุ DataSnapshot ที่ส่งไปยังผู้ฟังมีข้อมูลของลูกใหม่
onChildChanged() ฟังการเปลี่ยนแปลงของรายการในรายการ เหตุการณ์นี้เริ่มทำงานทุกครั้งที่มีการแก้ไขโหนดย่อย รวมถึงการดัดแปลงใด ๆ กับลูกหลานของโหนดย่อย DataSnapshot ที่ส่งไปยังตัวฟังเหตุการณ์มีข้อมูลที่อัปเดตสำหรับเด็ก
onChildRemoved() ฟังรายการที่ถูกลบออกจากรายการ DataSnapshot ที่ส่งผ่านไปยังการโทรกลับของเหตุการณ์ประกอบด้วยข้อมูลสำหรับเด็กที่ถูกลบ
onChildMoved() ฟังการเปลี่ยนแปลงลำดับของรายการในรายการที่สั่งซื้อ เหตุการณ์นี้จะถูกทริกเกอร์เมื่อใดก็ตามที่การเรียกกลับ onChildChanged() ถูกทริกเกอร์โดยการอัปเดตที่ทำให้เกิดการเรียงลำดับใหม่ของรายการย่อย ใช้กับข้อมูลที่สั่งซื้อด้วย orderByChild หรือ orderByValue

ตัวอย่างเช่น แอปบล็อกโซเชียลอาจใช้วิธีการเหล่านี้ร่วมกันเพื่อติดตามกิจกรรมในความคิดเห็นของโพสต์ดังที่แสดงด้านล่าง:

Java

ChildEventListener childEventListener = new ChildEventListener() {
    @Override
    public void onChildAdded(DataSnapshot dataSnapshot, String previousChildName) {
        Log.d(TAG, "onChildAdded:" + dataSnapshot.getKey());

        // 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) {
        Log.d(TAG, "onChildChanged:" + dataSnapshot.getKey());

        // 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) {
        Log.d(TAG, "onChildRemoved:" + dataSnapshot.getKey());

        // 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) {
        Log.d(TAG, "onChildMoved:" + dataSnapshot.getKey());

        // 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) {
        Log.w(TAG, "postComments:onCancelled", databaseError.toException());
        Toast.makeText(mContext, "Failed to load comments.",
                Toast.LENGTH_SHORT).show();
    }
};
databaseReference.addChildEventListener(childEventListener);

Kotlin+KTX

val childEventListener = object : ChildEventListener {
    override fun onChildAdded(dataSnapshot: DataSnapshot, previousChildName: String?) {
        Log.d(TAG, "onChildAdded:" + dataSnapshot.key!!)

        // A new comment has been added, add it to the displayed list
        val comment = dataSnapshot.getValue<Comment>()

        // ...
    }

    override fun onChildChanged(dataSnapshot: DataSnapshot, previousChildName: String?) {
        Log.d(TAG, "onChildChanged: ${dataSnapshot.key}")

        // A comment has changed, use the key to determine if we are displaying this
        // comment and if so displayed the changed comment.
        val newComment = dataSnapshot.getValue<Comment>()
        val commentKey = dataSnapshot.key

        // ...
    }

    override fun onChildRemoved(dataSnapshot: DataSnapshot) {
        Log.d(TAG, "onChildRemoved:" + dataSnapshot.key!!)

        // A comment has changed, use the key to determine if we are displaying this
        // comment and if so remove it.
        val commentKey = dataSnapshot.key

        // ...
    }

    override fun onChildMoved(dataSnapshot: DataSnapshot, previousChildName: String?) {
        Log.d(TAG, "onChildMoved:" + dataSnapshot.key!!)

        // A comment has changed position, use the key to determine if we are
        // displaying this comment and if so move it.
        val movedComment = dataSnapshot.getValue<Comment>()
        val commentKey = dataSnapshot.key

        // ...
    }

    override fun onCancelled(databaseError: DatabaseError) {
        Log.w(TAG, "postComments:onCancelled", databaseError.toException())
        Toast.makeText(context, "Failed to load comments.",
                Toast.LENGTH_SHORT).show()
    }
}
databaseReference.addChildEventListener(childEventListener)

ฟังเหตุการณ์อันทรงคุณค่า

แม้ว่าการใช้ ChildEventListener จะเป็นวิธีที่แนะนำในการอ่านรายการข้อมูล แต่ก็มีบางสถานการณ์ที่การแนบ ValueEventListener กับการอ้างอิงรายการจะมีประโยชน์

การแนบ ValueEventListener กับรายการข้อมูลจะส่งคืนรายการข้อมูลทั้งหมดเป็น DataSnapshot เดียว ซึ่งคุณสามารถวนซ้ำเพื่อเข้าถึงรายการย่อย

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

Java

// My top posts by number of stars
myTopPostsQuery.addValueEventListener(new ValueEventListener() {
    @Override
    public void onDataChange(DataSnapshot dataSnapshot) {
        for (DataSnapshot postSnapshot: dataSnapshot.getChildren()) {
            // TODO: handle the post
        }
    }

    @Override
    public void onCancelled(DatabaseError databaseError) {
        // Getting Post failed, log a message
        Log.w(TAG, "loadPost:onCancelled", databaseError.toException());
        // ...
    }
});

Kotlin+KTX

// My top posts by number of stars
myTopPostsQuery.addValueEventListener(object : ValueEventListener {
    override fun onDataChange(dataSnapshot: DataSnapshot) {
        for (postSnapshot in dataSnapshot.children) {
            // TODO: handle the post
        }
    }

    override fun onCancelled(databaseError: DatabaseError) {
        // Getting Post failed, log a message
        Log.w(TAG, "loadPost:onCancelled", databaseError.toException())
        // ...
    }
})

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

แยกผู้ฟัง

การเรียกกลับจะถูกลบออกโดยการเรียกเมธอด removeEventListener() บนการอ้างอิงฐานข้อมูล Firebase ของคุณ

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

การเรียก removeEventListener() บนตัวฟังหลักจะไม่ลบผู้ฟังที่ลงทะเบียนบนโหนดย่อยออกโดยอัตโนมัติ removeEventListener() ยังต้องถูกเรียกบนตัวฟังย่อยเพื่อลบการเรียกกลับ

การเรียงลำดับและการกรองข้อมูล

คุณสามารถใช้คลาส Realtime Database Query เพื่อดึงข้อมูลที่จัดเรียงตามคีย์ ตามค่า หรือตามค่าของเด็ก คุณยังสามารถกรองผลลัพธ์ที่จัดเรียงตามจำนวนผลลัพธ์ที่ต้องการ หรือช่วงของคีย์หรือค่าต่างๆ ได้อีกด้วย

จัดเรียงข้อมูล

ในการดึงข้อมูลที่จัดเรียง ให้เริ่มต้นด้วยการระบุวิธีเรียงลำดับตามวิธีใดวิธีหนึ่งเพื่อกำหนดวิธีการเรียงลำดับผลลัพธ์:

วิธี การใช้งาน
orderByChild() เรียงลำดับผลลัพธ์ตามค่าของคีย์ย่อยที่ระบุหรือพาธย่อยที่ซ้อนกัน
orderByKey() เรียงลำดับผลลัพธ์ด้วยคีย์ย่อย
orderByValue() เรียงลำดับผลลัพธ์ตามค่าลูก

คุณสามารถใช้ วิธี สั่งซื้อได้ครั้งละหนึ่งวิธีเท่านั้น การเรียกวิธีการเรียงลำดับหลายครั้งในแบบสอบถามเดียวกันทำให้เกิดข้อผิดพลาด

ตัวอย่างต่อไปนี้แสดงให้เห็นว่าคุณสามารถดึงรายการโพสต์ยอดนิยมของผู้ใช้ที่จัดเรียงตามจำนวนดาวได้อย่างไร:

Java

// My top posts by number of stars
String myUserId = getUid();
Query myTopPostsQuery = databaseReference.child("user-posts").child(myUserId)
        .orderByChild("starCount");
myTopPostsQuery.addChildEventListener(new ChildEventListener() {
    // TODO: implement the ChildEventListener methods as documented above
    // ...
});

Kotlin+KTX

// My top posts by number of stars
val myUserId = uid
val myTopPostsQuery = databaseReference.child("user-posts").child(myUserId)
    .orderByChild("starCount")

myTopPostsQuery.addChildEventListener(object : ChildEventListener {
    // TODO: implement the ChildEventListener methods as documented above
    // ...
})

สิ่งนี้กำหนดแบบสอบถามที่เมื่อรวมกับ ผู้ฟังย่อย จะซิงโครไนซ์ไคลเอนต์กับโพสต์ของผู้ใช้จากพาธในฐานข้อมูลตาม ID ผู้ใช้ของพวกเขา เรียงลำดับตามจำนวนดาวที่แต่ละโพสต์ได้รับ เทคนิคการใช้ ID เป็นคีย์ดัชนีนี้เรียกว่า data fan out คุณสามารถอ่านเพิ่มเติมเกี่ยวกับมันได้ใน โครงสร้างฐานข้อมูลของคุณ

การเรียกใช้ orderByChild() ระบุคีย์ย่อยเพื่อเรียงลำดับผลลัพธ์ ในกรณีนี้ โพสต์จะถูกจัดเรียงตามค่าของ "starCount" ที่เกี่ยวข้อง คิวรียังสามารถเรียงลำดับโดยลูกที่ซ้อนกันได้ ในกรณีที่คุณมีข้อมูลที่มีลักษณะดังนี้:

"posts": {
  "ts-functions": {
    "metrics": {
      "views" : 1200000,
      "likes" : 251000,
      "shares": 1200,
    },
    "title" : "Why you should use TypeScript for writing Cloud Functions",
    "author": "Doug",
  },
  "android-arch-3": {
    "metrics": {
      "views" : 900000,
      "likes" : 117000,
      "shares": 144,
    },
    "title" : "Using Android Architecture Components with Firebase Realtime Database (Part 3)",
    "author": "Doug",
  }
},

ในตัวอย่างนี้ เราสามารถจัดลำดับองค์ประกอบรายการของเราตามค่าที่ซ้อนอยู่ภายใต้คีย์ metrics โดยการระบุพาธสัมพัทธ์ไปยังรายการย่อยในการเรียก orderByChild()

Java

// Most viewed posts
Query myMostViewedPostsQuery = databaseReference.child("posts")
        .orderByChild("metrics/views");
myMostViewedPostsQuery.addChildEventListener(new ChildEventListener() {
    // TODO: implement the ChildEventListener methods as documented above
    // ...
});

Kotlin+KTX

// Most viewed posts
val myMostViewedPostsQuery = databaseReference.child("posts")
        .orderByChild("metrics/views")
myMostViewedPostsQuery.addChildEventListener(object : ChildEventListener {
    // TODO: implement the ChildEventListener methods as documented above
    // ...
})

สำหรับข้อมูลเพิ่มเติมเกี่ยวกับวิธีจัดลำดับข้อมูลประเภทอื่นๆ โปรดดู วิธีจัดลำดับข้อมูลการสืบค้น

การกรองข้อมูล

ในการกรองข้อมูล คุณสามารถรวมวิธีการจำกัดหรือช่วงใดๆ กับวิธีการเรียงลำดับเมื่อสร้างแบบสอบถาม

วิธี การใช้งาน
limitToFirst() ตั้งค่าจำนวนสูงสุดของรายการที่จะส่งคืนจากจุดเริ่มต้นของรายการผลลัพธ์ที่เรียงลำดับ
limitToLast() ตั้งค่าจำนวนสินค้าสูงสุดที่จะส่งคืนจากรายการผลลัพธ์ที่เรียงลำดับ
startAt() ส่งกลับรายการที่มากกว่าหรือเท่ากับคีย์หรือค่าที่ระบุขึ้นอยู่กับวิธีการเรียงลำดับที่เลือก
startAfter() ส่งกลับรายการที่มากกว่าคีย์หรือค่าที่ระบุขึ้นอยู่กับวิธีการสั่งซื้อที่เลือก
endAt() ส่งคืนสินค้าที่น้อยกว่าหรือเท่ากับคีย์หรือค่าที่ระบุ ขึ้นอยู่กับวิธีการเรียงลำดับที่เลือก
endBefore() ส่งคืนสินค้าที่น้อยกว่าคีย์หรือค่าที่ระบุ ขึ้นอยู่กับวิธีการสั่งซื้อที่เลือก
equalTo() ส่งกลับรายการเท่ากับคีย์หรือค่าที่ระบุขึ้นอยู่กับวิธีการสั่งซื้อที่เลือก

ต่างจากวิธีการเรียงตามลำดับ คุณสามารถรวมฟังก์ชันขีดจำกัดหรือช่วงได้หลายแบบ ตัวอย่างเช่น คุณสามารถรวม startAt() และ endAt() เพื่อจำกัดผลลัพธ์ให้อยู่ในช่วงค่าที่ระบุ

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

Java

// My top posts by number of stars
myTopPostsQuery.addValueEventListener(new ValueEventListener() {
    @Override
    public void onDataChange(DataSnapshot dataSnapshot) {
        for (DataSnapshot postSnapshot: dataSnapshot.getChildren()) {
            // TODO: handle the post
        }
    }

    @Override
    public void onCancelled(DatabaseError databaseError) {
        // Getting Post failed, log a message
        Log.w(TAG, "loadPost:onCancelled", databaseError.toException());
        // ...
    }
});

Kotlin+KTX

// My top posts by number of stars
myTopPostsQuery.addValueEventListener(object : ValueEventListener {
    override fun onDataChange(dataSnapshot: DataSnapshot) {
        for (postSnapshot in dataSnapshot.children) {
            // TODO: handle the post
        }
    }

    override fun onCancelled(databaseError: DatabaseError) {
        // Getting Post failed, log a message
        Log.w(TAG, "loadPost:onCancelled", databaseError.toException())
        // ...
    }
})

จำกัดจำนวนผลลัพธ์

คุณสามารถใช้ limitToFirst() และ limitToLast() เพื่อกำหนดจำนวนชายน์สูงสุดที่จะซิงค์สำหรับการโทรกลับที่กำหนด ตัวอย่างเช่น หากคุณใช้ limitToFirst() เพื่อตั้งค่าขีดจำกัด 100 ครั้ง คุณจะได้รับการเรียกกลับ onChildAdded() สูงสุด 100 ครั้งเท่านั้น หากคุณมีน้อยกว่า 100 รายการที่เก็บอยู่ในฐานข้อมูล Firebase การเรียกกลับ onChildAdded() จะเริ่มทำงานสำหรับแต่ละรายการ

เมื่อรายการเปลี่ยนไป คุณจะได้รับการเรียกกลับ onChildAdded() สำหรับรายการที่ป้อนการสืบค้นและการเรียกกลับ onChildRemoved() สำหรับรายการที่เลื่อนออกไปเพื่อให้จำนวนทั้งหมดอยู่ที่ 100

ตัวอย่างต่อไปนี้สาธิตวิธีที่แอปบล็อกตัวอย่างกำหนดแบบสอบถามเพื่อดึงรายการโพสต์ล่าสุด 100 รายการโดยผู้ใช้ทั้งหมด:

Java

// Last 100 posts, these are automatically the 100 most recent
// due to sorting by push() keys
Query recentPostsQuery = databaseReference.child("posts")
        .limitToFirst(100);

Kotlin+KTX

// Last 100 posts, these are automatically the 100 most recent
// due to sorting by push() keys.
databaseReference.child("posts").limitToFirst(100)

ตัวอย่างนี้กำหนดเฉพาะการสืบค้น เพื่อซิงโครไนซ์ข้อมูลจริง ๆ จำเป็นต้องมี Listener ที่แนบมา

กรองตามคีย์หรือค่า

คุณสามารถใช้ startAt() , startAfter() , endAt() , endBefore() และ equalTo() เพื่อเลือกจุดเริ่มต้น สิ้นสุด และจุดสมมูลโดยอำเภอใจสำหรับการสืบค้น ซึ่งจะเป็นประโยชน์สำหรับการแบ่งหน้าข้อมูลหรือค้นหารายการที่มีลูกที่มีค่าเฉพาะ

วิธีเรียงลำดับข้อมูลการสืบค้น

ส่วนนี้อธิบายวิธีการจัดเรียงข้อมูลตามวิธีการเรียงลำดับตามแต่ละวิธีในคลาส Query

orderByChild

เมื่อใช้ orderByChild() ข้อมูลที่มีคีย์ย่อยที่ระบุจะถูกเรียงลำดับดังนี้:

  1. รายการย่อยที่มี null สำหรับคีย์ย่อยที่ระบุมาก่อน
  2. รายการย่อยที่มีค่า false สำหรับคีย์ย่อยที่ระบุอยู่ถัดไป หากชายด์หลายคนมีค่า false พวกเขาจะถูกจัดเรียงตาม พจนานุกรม ตามคีย์
  3. รายการย่อยที่มีค่า true สำหรับคีย์ย่อยที่ระบุอยู่ถัดไป หากชายน์หลายคนมีค่า true พวกเขาจะถูกจัดเรียงตามพจนานุกรมตามคีย์
  4. เด็กที่มีค่าตัวเลขมาถัดไปโดยเรียงลำดับจากน้อยไปมาก หากโหนดชายน์หลายคนมีค่าตัวเลขเหมือนกันสำหรับโหนดชายน์ที่ระบุ พวกเขาจะถูกจัดเรียงตามคีย์
  5. สตริงมาหลังตัวเลขและจัดเรียงตามลำดับศัพท์จากน้อยไปหามาก หากชายน์หลายคนมีค่าเท่ากันสำหรับโหนดชายน์ที่ระบุ พวกเขาจะถูกจัดลำดับตามพจนานุกรมโดยคีย์
  6. ออบเจ็กต์จะอยู่ท้ายสุดและจัดเรียงตามพจนานุกรมโดยคีย์ในลำดับจากน้อยไปมาก

orderByKey

เมื่อใช้ orderByKey() เพื่อจัดเรียงข้อมูลของคุณ ข้อมูลจะถูกส่งคืนโดยเรียงลำดับจากน้อยไปมากตามคีย์

  1. รายการย่อยที่มีคีย์ที่สามารถแยกวิเคราะห์เป็นจำนวนเต็ม 32 บิตจะมาก่อน โดยเรียงลำดับจากน้อยไปมาก
  2. รายการย่อยที่มีค่าสตริงเป็นคีย์ถัดไป โดยเรียงลำดับจากน้อยไปหามาก

orderByValue

เมื่อใช้ orderByValue() ลูกจะถูกจัดเรียงตามมูลค่า เกณฑ์การสั่งซื้อจะเหมือนกับใน orderByChild() ยกเว้นว่าจะใช้ค่าของโหนดแทนค่าของคีย์ย่อยที่ระบุ

ขั้นตอนถัดไป