在 Android 上使用資料清單

本文件介紹如何在 Firebase 中使用資料清單。如要瞭解 讀取及寫入 Firebase 資料的基本概念 在 Android 上讀取及寫入資料

取得 DatabaseReference

如要從資料庫讀取及寫入資料,您需要 DatabaseReference 的例項:

Kotlin+KTX

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

Java

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

讀取及寫入清單

將資料附加至資料清單

使用 push() 方法,將資料附加至多使用者應用程式中的清單。 push() 方法會在每次新金鑰時產生專屬金鑰 子元素會加入指定的 Firebase 參考資料。只要在清單中的每個新元素使用這些自動產生的鍵,多個用戶端就能同時將子項新增至相同位置,而不會發生寫入衝突。 push() 產生的不重複索引鍵依據時間戳記,所以清單項目會 會自動依時間排序。

您可以使用 push() 方法傳回新資料的參照,取得 由子項自動產生的鍵值,或為子項設定資料的值。撥號中 push() 參照上的 getKey() 會傳回 自動產生的金鑰

您可以使用這些自動產生的金鑰來簡化資料彙整作業 成本中心的架構詳情請參閱資料擴散傳遞 example)。

監聽子事件

使用清單時,應用程式應監聽子事件 而非單一物件的價值事件。

如果容器中發生特定作業, 節點的子項,例如透過 push() 方法或透過 updateChildren() 方法更新的子項。 兩者都適合用來監聽特定節點的變更 資料庫內的 Pod 數量

如要在 DatabaseReference 上監聽子事件,請附加 ChildEventListener:

監聽器 事件回呼 一般用量
ChildEventListener onChildAdded() 擷取項目清單,或監聽項目清單的新增項目。每個現有的子項都會觸發此回呼一次,然後再次 每次將新子項加入指定路徑時,都會產生事件。傳遞至事件監聽器的 DataSnapshot 包含 新的孩童資料
onChildChanged() 監聽清單中項目的變更。只要子節點經過修改,就會觸發這項事件,包括對子節點後代所做的任何修改。傳遞至事件監聽器的 DataSnapshot 包含子項的更新資料。
onChildRemoved() 監聽已從清單中移除的項目。傳遞至事件回呼的 DataSnapshot 包含已移除子項的資料。
onChildMoved() 監聽已排序清單中項目順序的變更。 每當 onChildChanged() 回呼會由更新觸發回呼,並重新排序子項。 可與依 orderByChildorderByValue 排序的資料搭配使用。

舉例來說,社群網站的部落格應用程式可能會同時使用這些方法,監控貼文留言中的活動,如下所示:

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)

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);

監聽價值事件

建議使用 ChildEventListener 讀取 資料,在某些情況下,將 ValueEventListener 附加至清單 相當實用。

ValueEventListener 附加至資料清單會傳回整個 做為單一 DataSnapshot 的資料清單,接著您可以經由 存取個別子項

即使查詢只有一個相符結果,快照仍為 清單;只會包含單一項目如要存取該項目,就必須進行迴圈 套用到結果:

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())
        // ...
    }
})

Java

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

    @Override
    public void onCancelled(@NonNull 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() 按照子項值排序結果。

一次只能使用「一個」排序方法。依訂單呼叫方法 重複計算錯誤。

以下範例說明如何擷取使用者的 熱門訊息會按照星級評等排序:

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
    // ...
})

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
    // ...
});

這會定義一項查詢,在與子事件監聽器合併使用時 將用戶端與資料庫路徑中的使用者貼文同步處理 排序依據為每則貼文獲得的星星數量。 使用 ID 做為索引鍵的技術稱為「資料擴散傳遞」,您可以 深入瞭解 建立資料庫

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",
  }
},

在這個範例中,我們可以指定 orderByChild() 呼叫中巢狀子項的相對路徑,依據 metrics 鍵下巢狀結構的值排序清單元素。

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
    // ...
})

Java

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

如要進一步瞭解其他資料類型的排序方式,請參閱 查詢資料的排序方式

篩選資料

如要篩選資料,可以使用任何限製或範圍方法, 依順序排序查詢。

方法 用量
limitToFirst() 設定從 結果的排序清單。
limitToLast() 設定從訂單結尾傳回的項目數量上限 結果清單。
startAt() 傳回大於或等於指定鍵或值的項目 視所選的訂單而定
startAfter() 傳回大於指定鍵或值的項目 視所選的訂單而定
endAt() 傳回小於或等於指定鍵或值的項目,具體取決於所選排序方法。
endBefore() 傳回的項目小於指定的鍵或值,具體取決於所選排序方法。
equalTo() 傳回與指定鍵或值相等的項目 視所選的訂單而定

有別於逐一排序的方法,您可以合併多個限製或範圍函式。 例如,您可以結合 startAt()endAt() 方法 將結果傳回指定範圍內的值

即使查詢只有一個相符項目,快照仍會是清單,只是只包含單一項目。如要存取該項目,您需要完成 循環處理結果:

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())
        // ...
    }
})

Java

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

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

限制結果數量

您可以使用 limitToFirst()limitToLast() 方法設定 要為特定回呼同步處理的子項數量上限。舉例來說 您使用 limitToFirst() 將限制設為 100,一開始只會接收 至 100 個 onChildAdded() 回呼。如果 Firebase 資料庫中儲存的項目少於 100 個,系統會針對每個項目觸發 onChildAdded() 回呼。

當項目變更時,系統會針對輸入值的項目接收 onChildAdded() 回呼 以及 onChildRemoved() 回呼, 總數會維持在 100

以下範例說明如何定義查詢,以便擷取所有使用者最近 100 篇貼文的清單:

Kotlin+KTX

// Last 100 posts, these are automatically the 100 most recent
// due to sorting by push() keys.
databaseReference.child("posts").limitToFirst(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);

這個範例只會定義一項查詢,且會實際同步處理需要的資料 已連結事件監聽器

依鍵或值篩選

您可以使用 startAt()startAfter()endAt()endBefore()equalTo() 可選擇任意的起始、結束和等分點 舉個簡單的例子,您可以定義情境 並指示 AI 如何回應服務中心查詢這有助於分頁資料或尋找含有子項的項目 都有特定的值

查詢資料的排序方式

本節會說明 Query 類別。

orderByChild

使用 orderByChild() 時,包含指定子鍵的資料會依下列順序排序:

  1. 針對指定子項鍵有 null 值的子項,如下所示 首先。
  2. 指定子鍵值為 false 的子項 取得下一個提示如果多個子項的值為 false,這些子項就會 依鍵字母順序排序。
  3. 指定子鍵值為 true 的子項 取得下一個提示如果多個子項的值為 true,這些子項就會 依索引鍵字母順序排列
  4. 含有數值的子項接著會以遞增順序排序。如果多個子項的數值與指定子項節點相同,系統會依鍵值排序。
  5. 字串會排在數字之後,並依字典順序遞增排列。如果多個子項具有相同的值 節點的節點,按照鍵的字母順序排列。
  6. 物件在最後,且依索引鍵順序排列,以遞增順序排列。

orderByKey

使用 orderByKey() 排序資料時,系統會依鍵值以遞增順序傳回資料。

  1. 如果子項的索引鍵可剖析為 32 位元整數,其內容會先以遞增順序排序。
  2. 具有字串值做為索引鍵的下一個子項,並依字母順序遞增排序。

orderByValue

使用 orderByValue() 時,子項會依值排序。順序 條件與 orderByChild() 相同,但節點值 ,而不是指定子鍵的值。

後續步驟