با لیست داده ها در اندروید کار کنید

این سند کار با لیست های داده در Firebase را پوشش می دهد. برای یادگیری اصول اولیه خواندن و نوشتن داده های Firebase به خواندن و نوشتن داده ها در Android مراجعه کنید.

یک مرجع پایگاه داده دریافت کنید

برای خواندن و نوشتن داده ها از پایگاه داده، به یک نمونه از 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() استفاده کنید تا مقدار کلید تولید شده خودکار کودک را بدست آورید یا داده هایی را برای فرزند تنظیم کنید. فراخوانی getKey() روی یک مرجع push() مقدار کلید تولید شده خودکار را برمی گرداند.

شما می توانید از این کلیدهای تولید شده خودکار برای ساده سازی ساختار داده خود استفاده کنید. برای اطلاعات بیشتر، به مثال فن خروجی داده مراجعه کنید.

به رویدادهای کودک گوش دهید

هنگام کار با لیست ها، برنامه شما باید به رویدادهای فرزند گوش دهد تا رویدادهای ارزشی که برای اشیاء منفرد استفاده می شود.

رویدادهای فرزند در پاسخ به عملیات خاصی که برای فرزندان یک گره از عملیاتی مانند فرزند جدید اضافه شده از طریق متد push() یا فرزندی که از طریق متد updateChildren() به روز می شود، راه اندازی می شوند. هر یک از اینها با هم می توانند برای گوش دادن به تغییرات یک گره خاص در پایگاه داده مفید باشند.

به منظور گوش دادن به رویدادهای کودک در DatabaseReference ، یک ChildEventListener را پیوست کنید:

شنونده پاسخ تماس رویداد استفاده معمولی
ChildEventListener onChildAdded() لیستی از آیتم ها را بازیابی کنید یا برای اضافات به لیستی از موارد گوش دهید. این فراخوانی یک بار برای هر فرزند موجود و سپس هر بار که فرزند جدیدی به مسیر مشخص شده اضافه می شود، راه اندازی می شود. DataSnapshot ارسال شده به شنونده حاوی داده های فرزند جدید است.
onChildChanged() به تغییرات در موارد موجود در یک لیست گوش دهید. این رویداد هر زمان که یک گره فرزند اصلاح شود، از جمله هر گونه تغییری در نوادگان گره فرزند، فعال می شود. DataSnapshot ارسال شده به شنونده رویداد حاوی داده های به روز شده برای کودک است.
onChildRemoved() به مواردی که از لیست حذف می شوند گوش دهید. DataSnapshot ارسال شده به رویداد برگشتی حاوی داده های فرزند حذف شده است.
onChildMoved() به تغییرات در ترتیب اقلام در یک لیست مرتب گوش دهید. این رویداد زمانی فعال می‌شود که پاسخ تماس onChildChanged() توسط یک به‌روزرسانی که باعث ترتیب مجدد فرزند می‌شود، فعال می‌شود. با داده هایی که با orderByChild یا orderByValue مرتب شده اند استفاده می شود.

به عنوان مثال، یک برنامه وبلاگ نویسی اجتماعی ممکن است از این روش ها برای نظارت بر فعالیت در نظرات یک پست استفاده کند، همانطور که در زیر نشان داده شده است:

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

این یک پرس و جو را تعریف می کند که وقتی با یک شنونده کودک ترکیب می شود، مشتری را با پست های کاربر از مسیر موجود در پایگاه داده بر اساس شناسه کاربری آن ها، به ترتیب بر اساس تعداد ستاره هایی که هر پست دریافت کرده است، همگام می کند. این تکنیک استفاده از شناسه‌ها به‌عنوان کلیدهای فهرست، فن‌آوری داده‌ها نامیده می‌شود، می‌توانید اطلاعات بیشتری درباره آن در Structure Your Database بخوانید.

فراخوانی متد 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() دریافت می کنید. اگر کمتر از 100 مورد در پایگاه داده Firebase ذخیره شده است، برای هر آیتم یک callback onChildAdded() فعال می شود.

همانطور که آیتم ها تغییر می کنند، برای مواردی که وارد پرس و جو می شوند، callbacks onChildAdded() و برای مواردی که از آن خارج می شوند، callbacks 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() برای انتخاب نقاط شروع، پایان و معادل دلخواه برای کوئری ها استفاده کنید. این می‌تواند برای صفحه‌بندی داده‌ها یا یافتن مواردی با کودکان که دارای ارزش خاصی هستند مفید باشد.

نحوه سفارش داده های پرس و جو

این بخش توضیح می‌دهد که چگونه داده‌ها بر اساس هر یک از روش‌های مرتب‌سازی در کلاس Query مرتب می‌شوند.

orderByChild

هنگام استفاده از orderByChild() ، داده هایی که حاوی کلید فرزند مشخص شده هستند به صورت زیر مرتب می شوند:

  1. کودکان با مقدار null برای کلید فرزند مشخص شده اول هستند.
  2. کودکان با مقدار false برای کلید فرزند مشخص شده در مرحله بعدی قرار می گیرند. اگر چند فرزند مقدار false داشته باشند، از نظر واژگانی بر اساس کلید مرتب می شوند.
  3. کودکان با مقدار true برای کلید فرزند مشخص شده در مرحله بعدی قرار می گیرند. اگر چند فرزند مقدار true داشته باشند، از نظر واژگانی بر اساس کلید مرتب می شوند.
  4. کودکان با مقدار عددی در مرحله بعدی قرار می گیرند که به ترتیب صعودی مرتب شده اند. اگر چندین فرزند مقدار عددی یکسانی برای گره فرزند مشخص شده داشته باشند، آنها بر اساس کلید مرتب می شوند.
  5. رشته ها بعد از اعداد آمده و از نظر واژگانی به ترتیب صعودی مرتب شده اند. اگر چندین فرزند مقدار یکسانی برای گره فرزند مشخص شده داشته باشند، آنها از نظر واژگانی بر اساس کلید مرتب می شوند.
  6. اشیاء در آخر قرار می گیرند و از نظر واژگانی بر اساس کلید به ترتیب صعودی مرتب می شوند.

orderByKey

هنگام استفاده از orderByKey() برای مرتب‌سازی داده‌های خود، داده‌ها به ترتیب صعودی بر اساس کلید برگردانده می‌شوند.

  1. کودکان دارای کلیدی که می تواند به عنوان یک عدد صحیح 32 بیتی تجزیه شود، در درجه اول قرار می گیرند و به ترتیب صعودی مرتب می شوند.
  2. کودکان با مقدار رشته به عنوان کلید در مرحله بعدی قرار می گیرند که از نظر واژگانی به ترتیب صعودی مرتب شده اند.

orderByValue

هنگام استفاده از orderByValue() ، فرزندان بر اساس مقدارشان مرتب می شوند. معیارهای ترتیب مانند orderByChild() است، با این تفاوت که مقدار گره به جای مقدار کلید فرزند مشخص شده استفاده می شود.

مراحل بعدی