خواندن و نوشتن داده ها در اندروید

این سند اصول خواندن و نوشتن داده های Firebase را پوشش می دهد.

داده های Firebase در یک مرجع FirebaseDatabase نوشته می شود و با پیوست کردن یک شنونده ناهمزمان به مرجع بازیابی می شود. شنونده یک بار برای وضعیت اولیه داده ها و بار دیگر هر زمان که داده ها تغییر کند فعال می شود.

(اختیاری) نمونه اولیه و آزمایش با Firebase Local Emulator Suite

قبل از صحبت در مورد نحوه خواندن و نوشتن برنامه شما از Realtime Database ، بیایید مجموعه‌ای از ابزارهایی را که می‌توانید برای نمونه‌سازی و آزمایش عملکرد Realtime Database استفاده کنید، معرفی می‌کنیم: Firebase Local Emulator Suite . اگر در حال آزمایش مدل های مختلف داده، بهینه سازی قوانین امنیتی خود هستید یا برای یافتن مقرون به صرفه ترین راه برای تعامل با back-end تلاش می کنید، اینکه بتوانید به صورت محلی بدون استقرار سرویس های زنده کار کنید می تواند ایده خوبی باشد.

شبیه ساز Realtime Database بخشی از Local Emulator Suite است که به برنامه شما امکان می دهد با محتوای پایگاه داده شبیه سازی شده و پیکربندی شما و همچنین به صورت اختیاری منابع پروژه شبیه سازی شده شما (توابع، سایر پایگاه های داده و قوانین امنیتی) تعامل داشته باشد.

استفاده از شبیه ساز Realtime Database فقط شامل چند مرحله است:

  1. افزودن یک خط کد به پیکربندی آزمایشی برنامه برای اتصال به شبیه ساز.
  2. از ریشه دایرکتوری پروژه محلی خود، firebase emulators:start اجرا کنید.
  3. برقراری تماس از کد نمونه اولیه برنامه خود با استفاده از یک SDK پلتفرم Realtime Database به طور معمول، یا با استفاده از Realtime Database REST API.

یک بررسی دقیق شامل Realtime Database و Cloud Functions در دسترس است. همچنین باید نگاهی به معرفی Local Emulator Suite داشته باشید.

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

برای خواندن یا نوشتن داده ها از پایگاه داده، به یک نمونه از DatabaseReference نیاز دارید:

Kotlin+KTX

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

Java

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

داده ها را بنویسید

عملیات نوشتن اولیه

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

  • انواع پاس که با انواع JSON موجود مطابقت دارند به شرح زیر:
    • String
    • Long
    • Double
    • Boolean
    • Map<String, Object>
    • List<Object>
  • یک شی جاوا سفارشی را ارسال کنید، اگر کلاسی که آن را تعریف می‌کند سازنده پیش‌فرضی داشته باشد که هیچ آرگومان نمی‌گیرد و دارای گیرنده‌های عمومی برای ویژگی‌هایی است که باید تخصیص داده شوند.

اگر از یک شی جاوا استفاده می کنید، محتویات شی شما به طور خودکار به مکان های فرزند به صورت تو در تو نگاشت می شود. استفاده از یک شی جاوا نیز معمولاً کد شما را خواناتر و نگهداری آسان تر می کند. به عنوان مثال، اگر یک برنامه با نمایه کاربری اصلی دارید، شیء User شما ممکن است به شکل زیر باشد:

Kotlin+KTX

@IgnoreExtraProperties
data class User(val username: String? = null, val email: String? = null) {
    // Null default values create a no-argument default constructor, which is needed
    // for deserialization from a DataSnapshot.
}

Java

@IgnoreExtraProperties
public class User {

    public String username;
    public String email;

    public User() {
        // Default constructor required for calls to DataSnapshot.getValue(User.class)
    }

    public User(String username, String email) {
        this.username = username;
        this.email = email;
    }

}

می توانید یک کاربر با setValue() به صورت زیر اضافه کنید:

Kotlin+KTX

fun writeNewUser(userId: String, name: String, email: String) {
    val user = User(name, email)

    database.child("users").child(userId).setValue(user)
}

Java

public void writeNewUser(String userId, String name, String email) {
    User user = new User(name, email);

    mDatabase.child("users").child(userId).setValue(user);
}

استفاده از setValue() در این روش داده ها را در مکان مشخص شده، از جمله گره های فرزند، بازنویسی می کند. با این حال، همچنان می‌توانید یک فرزند را بدون بازنویسی کل شی به‌روزرسانی کنید. اگر می خواهید به کاربران اجازه دهید پروفایل های خود را به روز کنند، می توانید نام کاربری را به صورت زیر به روز کنید:

Kotlin+KTX

database.child("users").child(userId).child("username").setValue(name)

Java

mDatabase.child("users").child(userId).child("username").setValue(name);

داده ها را بخوانید

خواندن داده ها با شنوندگان مداوم

برای خواندن داده ها در یک مسیر و گوش دادن به تغییرات، از متد addValueEventListener() برای افزودن یک ValueEventListener به DatabaseReference استفاده کنید.

شنونده پاسخ تماس رویداد استفاده معمولی
ValueEventListener onDataChange() برای تغییرات در کل محتوای یک مسیر بخوانید و گوش دهید.

می‌توانید از متد onDataChange() برای خواندن یک عکس فوری از محتویات در یک مسیر مشخص استفاده کنید، همانطور که در زمان رویداد وجود داشتند. این روش یک بار در زمانی که شنونده متصل می شود و دوباره هر بار که داده ها، از جمله کودکان، تغییر می کنند، فعال می شود. پاسخ تماس رویداد یک عکس فوری حاوی تمام داده‌ها در آن مکان، از جمله داده‌های فرزند ارسال می‌شود. اگر داده ای وجود نداشته باشد، عکس فوری با فراخوانی exists() false و زمانی که getValue() بر روی آن فراخوانی می کنید، null خواهد شد.

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

Kotlin+KTX

val postListener = object : ValueEventListener {
    override fun onDataChange(dataSnapshot: DataSnapshot) {
        // Get Post object and use the values to update the UI
        val post = dataSnapshot.getValue<Post>()
        // ...
    }

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

Java

ValueEventListener postListener = new ValueEventListener() {
    @Override
    public void onDataChange(DataSnapshot dataSnapshot) {
        // Get Post object and use the values to update the UI
        Post post = dataSnapshot.getValue(Post.class);
        // ..
    }

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

شنونده یک DataSnapshot دریافت می کند که حاوی داده ها در مکان مشخص شده در پایگاه داده در زمان رویداد است. فراخوانی getValue() در یک عکس فوری، نمایش شی جاوا از داده ها را برمی گرداند. اگر هیچ داده ای در مکان وجود نداشته باشد، فراخوانی getValue() null برمی گرداند.

در این مثال، ValueEventListener متد onCancelled() را نیز تعریف می کند که در صورت لغو خواندن فراخوانی می شود. برای مثال، اگر کلاینت مجوز خواندن از محل پایگاه داده Firebase را نداشته باشد، می‌توان یک خواندن را لغو کرد. به این روش یک شی DatabaseError ارسال می شود که نشان می دهد چرا خرابی رخ داده است.

داده ها را یک بار بخوانید

یکبار با استفاده از get() بخوانید

SDK برای مدیریت تعاملات با سرورهای پایگاه داده، چه برنامه شما آنلاین یا آفلاین باشد، طراحی شده است.

به طور کلی، شما باید از تکنیک‌های ValueEventListener که در بالا توضیح داده شد برای خواندن داده‌ها استفاده کنید تا از به‌روزرسانی‌های داده‌ها از باطن مطلع شوید. تکنیک‌های شنونده استفاده و صورت‌حساب شما را کاهش می‌دهند، و بهینه‌سازی شده‌اند تا بهترین تجربه را هنگام آنلاین شدن و آفلاین شدن کاربرانتان ارائه دهند.

اگر فقط یک بار به داده ها نیاز دارید، می توانید از get() برای گرفتن عکس فوری از داده ها از پایگاه داده استفاده کنید. اگر به هر دلیلی get() نتواند مقدار سرور را برگرداند، کلاینت حافظه نهان محلی ذخیره‌سازی را بررسی می‌کند و در صورتی که مقدار هنوز پیدا نشد، خطایی را برمی‌گرداند.

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

Kotlin+KTX

mDatabase.child("users").child(userId).get().addOnSuccessListener {
    Log.i("firebase", "Got value ${it.value}")
}.addOnFailureListener{
    Log.e("firebase", "Error getting data", it)
}

Java

mDatabase.child("users").child(userId).get().addOnCompleteListener(new OnCompleteListener<DataSnapshot>() {
    @Override
    public void onComplete(@NonNull Task<DataSnapshot> task) {
        if (!task.isSuccessful()) {
            Log.e("firebase", "Error getting data", task.getException());
        }
        else {
            Log.d("firebase", String.valueOf(task.getResult().getValue()));
        }
    }
});

یک بار با استفاده از شنونده بخوانید

در برخی موارد ممکن است بخواهید به جای بررسی مقدار به روز شده در سرور، مقدار از حافظه نهان محلی بلافاصله برگردانده شود. در این موارد می‌توانید از addListenerForSingleValueEvent برای دریافت فوری داده‌ها از حافظه پنهان دیسک محلی استفاده کنید.

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

به روز رسانی یا حذف داده ها

فیلدهای خاص را به روز کنید

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

هنگام فراخوانی updateChildren() می توانید مقادیر فرزند سطح پایین تر را با تعیین مسیری برای کلید به روز کنید. اگر داده‌ها در مکان‌های مختلف ذخیره می‌شوند تا مقیاس بهتری داشته باشند، می‌توانید تمام نمونه‌های آن داده‌ها را با استفاده از خروجی فن‌آوری داده به‌روزرسانی کنید. به عنوان مثال، یک برنامه وبلاگ نویسی اجتماعی ممکن است یک کلاس Post مانند این داشته باشد:

Kotlin+KTX

@IgnoreExtraProperties
data class Post(
    var uid: String? = "",
    var author: String? = "",
    var title: String? = "",
    var body: String? = "",
    var starCount: Int = 0,
    var stars: MutableMap<String, Boolean> = HashMap(),
) {

    @Exclude
    fun toMap(): Map<String, Any?> {
        return mapOf(
            "uid" to uid,
            "author" to author,
            "title" to title,
            "body" to body,
            "starCount" to starCount,
            "stars" to stars,
        )
    }
}

Java

@IgnoreExtraProperties
public class Post {

    public String uid;
    public String author;
    public String title;
    public String body;
    public int starCount = 0;
    public Map<String, Boolean> stars = new HashMap<>();

    public Post() {
        // Default constructor required for calls to DataSnapshot.getValue(Post.class)
    }

    public Post(String uid, String author, String title, String body) {
        this.uid = uid;
        this.author = author;
        this.title = title;
        this.body = body;
    }

    @Exclude
    public Map<String, Object> toMap() {
        HashMap<String, Object> result = new HashMap<>();
        result.put("uid", uid);
        result.put("author", author);
        result.put("title", title);
        result.put("body", body);
        result.put("starCount", starCount);
        result.put("stars", stars);

        return result;
    }
}

برای ایجاد یک پست و به‌روزرسانی همزمان آن به فید فعالیت اخیر و فید فعالیت کاربر پست کننده، برنامه وبلاگ نویسی از کدی مانند زیر استفاده می‌کند:

Kotlin+KTX

private fun writeNewPost(userId: String, username: String, title: String, body: String) {
    // Create new post at /user-posts/$userid/$postid and at
    // /posts/$postid simultaneously
    val key = database.child("posts").push().key
    if (key == null) {
        Log.w(TAG, "Couldn't get push key for posts")
        return
    }

    val post = Post(userId, username, title, body)
    val postValues = post.toMap()

    val childUpdates = hashMapOf<String, Any>(
        "/posts/$key" to postValues,
        "/user-posts/$userId/$key" to postValues,
    )

    database.updateChildren(childUpdates)
}

Java

private void writeNewPost(String userId, String username, String title, String body) {
    // Create new post at /user-posts/$userid/$postid and at
    // /posts/$postid simultaneously
    String key = mDatabase.child("posts").push().getKey();
    Post post = new Post(userId, username, title, body);
    Map<String, Object> postValues = post.toMap();

    Map<String, Object> childUpdates = new HashMap<>();
    childUpdates.put("/posts/" + key, postValues);
    childUpdates.put("/user-posts/" + userId + "/" + key, postValues);

    mDatabase.updateChildren(childUpdates);
}

این مثال از push() برای ایجاد یک پست در گره حاوی پست برای همه کاربران در /posts/$postid استفاده می کند و همزمان کلید را با getKey() بازیابی می کند. سپس می‌توان از کلید برای ایجاد ورودی دوم در پست‌های کاربر در /user-posts/$userid/$postid استفاده کرد.

با استفاده از این مسیرها، می‌توانید به‌روزرسانی‌های همزمان چندین مکان در درخت JSON را با یک فراخوانی برای updateChildren() انجام دهید، مانند اینکه چگونه این مثال پست جدید را در هر دو مکان ایجاد می‌کند. به‌روزرسانی‌های همزمان ساخته شده به این روش اتمی هستند: یا همه به‌روزرسانی‌ها موفق می‌شوند یا همه به‌روزرسانی‌ها با شکست مواجه می‌شوند.

یک پاسخ به تماس تکمیلی اضافه کنید

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

Kotlin+KTX

database.child("users").child(userId).setValue(user)
    .addOnSuccessListener {
        // Write was successful!
        // ...
    }
    .addOnFailureListener {
        // Write failed
        // ...
    }

Java

mDatabase.child("users").child(userId).setValue(user)
        .addOnSuccessListener(new OnSuccessListener<Void>() {
            @Override
            public void onSuccess(Void aVoid) {
                // Write was successful!
                // ...
            }
        })
        .addOnFailureListener(new OnFailureListener() {
            @Override
            public void onFailure(@NonNull Exception e) {
                // Write failed
                // ...
            }
        });

داده ها را حذف کنید

ساده ترین راه برای حذف داده ها فراخوانی removeValue() بر روی مرجعی به محل آن داده است.

همچنین می توانید با تعیین null به عنوان مقدار برای عملیات نوشتن دیگری مانند setValue() یا updateChildren() حذف کنید. می‌توانید از این تکنیک با updateChildren() برای حذف چند فرزند در یک تماس API استفاده کنید.

شنوندگان را جدا کنید

با فراخوانی متد removeEventListener() در مرجع پایگاه داده Firebase، تماس‌های برگشتی حذف می‌شوند.

اگر شنونده ای چندین بار به یک مکان داده اضافه شده باشد، برای هر رویداد چندین بار فراخوانی می شود و برای حذف کامل باید آن را به همان تعداد دفعه جدا کنید.

فراخوانی removeEventListener() در شنونده والد به طور خودکار شنوندگان ثبت شده در گره های فرزند خود را حذف نمی کند. removeEventListener() همچنین باید در هر شنونده کودک فراخوانی شود تا پاسخ تماس را حذف کند.

ذخیره داده ها به عنوان تراکنش

هنگام کار با داده هایی که ممکن است توسط تغییرات همزمان خراب شوند، مانند شمارنده های افزایشی، می توانید از عملیات تراکنش استفاده کنید. شما به این عملیات دو آرگومان می دهید: یک تابع به روز رسانی و یک فراخوان تکمیل اختیاری. تابع به روز رسانی وضعیت فعلی داده ها را به عنوان آرگومان می گیرد و حالت دلخواه جدیدی را که می خواهید بنویسید برمی گرداند. اگر مشتری دیگری قبل از اینکه مقدار جدید با موفقیت نوشته شود، در مکان بنویسد، تابع به روز رسانی شما دوباره با مقدار فعلی جدید فراخوانی می شود و نوشتن مجدداً امتحان می شود.

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

Kotlin+KTX

private fun onStarClicked(postRef: DatabaseReference) {
    // ...
    postRef.runTransaction(object : Transaction.Handler {
        override fun doTransaction(mutableData: MutableData): Transaction.Result {
            val p = mutableData.getValue(Post::class.java)
                ?: return Transaction.success(mutableData)

            if (p.stars.containsKey(uid)) {
                // Unstar the post and remove self from stars
                p.starCount = p.starCount - 1
                p.stars.remove(uid)
            } else {
                // Star the post and add self to stars
                p.starCount = p.starCount + 1
                p.stars[uid] = true
            }

            // Set value and report transaction success
            mutableData.value = p
            return Transaction.success(mutableData)
        }

        override fun onComplete(
            databaseError: DatabaseError?,
            committed: Boolean,
            currentData: DataSnapshot?,
        ) {
            // Transaction completed
            Log.d(TAG, "postTransaction:onComplete:" + databaseError!!)
        }
    })
}

Java

private void onStarClicked(DatabaseReference postRef) {
    postRef.runTransaction(new Transaction.Handler() {
        @NonNull
        @Override
        public Transaction.Result doTransaction(@NonNull MutableData mutableData) {
            Post p = mutableData.getValue(Post.class);
            if (p == null) {
                return Transaction.success(mutableData);
            }

            if (p.stars.containsKey(getUid())) {
                // Unstar the post and remove self from stars
                p.starCount = p.starCount - 1;
                p.stars.remove(getUid());
            } else {
                // Star the post and add self to stars
                p.starCount = p.starCount + 1;
                p.stars.put(getUid(), true);
            }

            // Set value and report transaction success
            mutableData.setValue(p);
            return Transaction.success(mutableData);
        }

        @Override
        public void onComplete(DatabaseError databaseError, boolean committed,
                               DataSnapshot currentData) {
            // Transaction completed
            Log.d(TAG, "postTransaction:onComplete:" + databaseError);
        }
    });
}

استفاده از تراکنش از نادرست بودن تعداد ستاره‌ها جلوگیری می‌کند، اگر چندین کاربر به طور همزمان یک پست را ستاره‌دار کنند یا مشتری داده‌های قدیمی داشته باشد. اگر تراکنش رد شود، سرور مقدار فعلی را به کلاینت برمی گرداند، که تراکنش را دوباره با مقدار به روز شده اجرا می کند. این کار تا زمانی که تراکنش پذیرفته شود یا تلاش های زیادی انجام شود تکرار می شود.

افزایش سمت سرور اتمی

در مورد استفاده بالا، ما دو مقدار را برای پایگاه داده می نویسیم: شناسه کاربری که پست را ستاره/برداشته ستاره می کند، و تعداد ستاره افزایش یافته. اگر از قبل می‌دانیم که کاربر پست را ستاره‌دار می‌کند، می‌توانیم به جای تراکنش از عملیات افزایش اتمی استفاده کنیم.

Kotlin+KTX

private fun onStarClicked(uid: String, key: String) {
    val updates: MutableMap<String, Any> = hashMapOf(
        "posts/$key/stars/$uid" to true,
        "posts/$key/starCount" to ServerValue.increment(1),
        "user-posts/$uid/$key/stars/$uid" to true,
        "user-posts/$uid/$key/starCount" to ServerValue.increment(1),
    )
    database.updateChildren(updates)
}

Java

private void onStarClicked(String uid, String key) {
    Map<String, Object> updates = new HashMap<>();
    updates.put("posts/"+key+"/stars/"+uid, true);
    updates.put("posts/"+key+"/starCount", ServerValue.increment(1));
    updates.put("user-posts/"+uid+"/"+key+"/stars/"+uid, true);
    updates.put("user-posts/"+uid+"/"+key+"/starCount", ServerValue.increment(1));
    mDatabase.updateChildren(updates);
}

این کد از عملیات تراکنش استفاده نمی‌کند، بنابراین در صورت بروز تضاد به‌روزرسانی، به‌طور خودکار دوباره اجرا نمی‌شود. با این حال، از آنجایی که عملیات افزایش مستقیماً در سرور پایگاه داده انجام می شود، هیچ احتمالی برای تداخل وجود ندارد.

اگر می‌خواهید تضادهای خاص برنامه را شناسایی و رد کنید، مانند ستاره‌دار شدن پستی که قبلاً توسط کاربر ستاره‌دار شده است، باید قوانین امنیتی سفارشی را برای آن مورد استفاده بنویسید.

با داده ها به صورت آفلاین کار کنید

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

هر کلاینت متصل به پایگاه داده Firebase، نسخه داخلی خود را از هر داده ای که شنونده در آن استفاده می شود یا پرچم گذاری شده است تا با سرور همگام شود، حفظ می کند. هنگامی که داده خوانده یا نوشته می شود، ابتدا از این نسخه محلی داده ها استفاده می شود. سپس مشتری Firebase آن داده ها را با سرورهای پایگاه داده راه دور و با سایر مشتریان بر اساس "بهترین تلاش" همگام سازی می کند.

در نتیجه، همه نوشته‌ها در پایگاه داده بلافاصله قبل از هر گونه تعامل با سرور، رویدادهای محلی را راه‌اندازی می‌کنند. این بدان معناست که برنامه شما بدون توجه به تأخیر شبکه یا اتصال، پاسخگو باقی می ماند.

پس از برقراری مجدد اتصال، برنامه شما مجموعه مناسبی از رویدادها را دریافت می کند تا کلاینت بدون نیاز به نوشتن کد سفارشی با وضعیت سرور فعلی همگام شود.

در مورد قابلیت های آنلاین و آفلاین بیشتر بدانید در مورد رفتار آفلاین بیشتر صحبت خواهیم کرد.

مراحل بعدی

،

این سند اصول خواندن و نوشتن داده های Firebase را پوشش می دهد.

داده های Firebase در یک مرجع FirebaseDatabase نوشته می شود و با پیوست کردن یک شنونده ناهمزمان به مرجع بازیابی می شود. شنونده یک بار برای وضعیت اولیه داده ها و بار دیگر هر زمان که داده ها تغییر کند فعال می شود.

(اختیاری) نمونه اولیه و آزمایش با Firebase Local Emulator Suite

قبل از صحبت در مورد نحوه خواندن و نوشتن برنامه شما از Realtime Database ، بیایید مجموعه‌ای از ابزارهایی را که می‌توانید برای نمونه‌سازی و آزمایش عملکرد Realtime Database استفاده کنید، معرفی می‌کنیم: Firebase Local Emulator Suite . اگر در حال آزمایش مدل های مختلف داده، بهینه سازی قوانین امنیتی خود هستید یا برای یافتن مقرون به صرفه ترین راه برای تعامل با back-end تلاش می کنید، اینکه بتوانید به صورت محلی بدون استقرار سرویس های زنده کار کنید می تواند ایده خوبی باشد.

شبیه ساز Realtime Database بخشی از Local Emulator Suite است که به برنامه شما امکان می دهد با محتوای پایگاه داده شبیه سازی شده و پیکربندی شما و همچنین به صورت اختیاری منابع پروژه شبیه سازی شده شما (توابع، سایر پایگاه های داده و قوانین امنیتی) تعامل داشته باشد.

استفاده از شبیه ساز Realtime Database فقط شامل چند مرحله است:

  1. افزودن یک خط کد به پیکربندی آزمایشی برنامه برای اتصال به شبیه ساز.
  2. از ریشه دایرکتوری پروژه محلی خود، firebase emulators:start اجرا کنید.
  3. برقراری تماس از کد نمونه اولیه برنامه خود با استفاده از یک SDK پلتفرم Realtime Database به طور معمول، یا با استفاده از Realtime Database REST API.

یک بررسی دقیق شامل Realtime Database و Cloud Functions در دسترس است. همچنین باید نگاهی به معرفی Local Emulator Suite داشته باشید.

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

برای خواندن یا نوشتن داده ها از پایگاه داده، به یک نمونه از DatabaseReference نیاز دارید:

Kotlin+KTX

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

Java

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

داده ها را بنویسید

عملیات نوشتن اولیه

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

  • انواع پاس که با انواع JSON موجود مطابقت دارند به شرح زیر:
    • String
    • Long
    • Double
    • Boolean
    • Map<String, Object>
    • List<Object>
  • یک شی جاوا سفارشی را ارسال کنید، اگر کلاسی که آن را تعریف می‌کند سازنده پیش‌فرضی داشته باشد که هیچ آرگومان نمی‌گیرد و دارای گیرنده‌های عمومی برای ویژگی‌هایی است که باید تخصیص داده شوند.

اگر از یک شی جاوا استفاده می کنید، محتویات شی شما به طور خودکار به مکان های فرزند به صورت تو در تو نگاشت می شود. استفاده از یک شی جاوا نیز معمولاً کد شما را خواناتر و نگهداری آسان تر می کند. به عنوان مثال، اگر یک برنامه با نمایه کاربری اصلی دارید، شیء User شما ممکن است به شکل زیر باشد:

Kotlin+KTX

@IgnoreExtraProperties
data class User(val username: String? = null, val email: String? = null) {
    // Null default values create a no-argument default constructor, which is needed
    // for deserialization from a DataSnapshot.
}

Java

@IgnoreExtraProperties
public class User {

    public String username;
    public String email;

    public User() {
        // Default constructor required for calls to DataSnapshot.getValue(User.class)
    }

    public User(String username, String email) {
        this.username = username;
        this.email = email;
    }

}

می توانید یک کاربر با setValue() به صورت زیر اضافه کنید:

Kotlin+KTX

fun writeNewUser(userId: String, name: String, email: String) {
    val user = User(name, email)

    database.child("users").child(userId).setValue(user)
}

Java

public void writeNewUser(String userId, String name, String email) {
    User user = new User(name, email);

    mDatabase.child("users").child(userId).setValue(user);
}

استفاده از setValue() در این روش داده ها را در مکان مشخص شده، از جمله گره های فرزند، بازنویسی می کند. با این حال، همچنان می‌توانید یک فرزند را بدون بازنویسی کل شی به‌روزرسانی کنید. اگر می خواهید به کاربران اجازه دهید پروفایل های خود را به روز کنند، می توانید نام کاربری را به صورت زیر به روز کنید:

Kotlin+KTX

database.child("users").child(userId).child("username").setValue(name)

Java

mDatabase.child("users").child(userId).child("username").setValue(name);

داده ها را بخوانید

خواندن داده ها با شنوندگان مداوم

برای خواندن داده ها در یک مسیر و گوش دادن به تغییرات، از متد addValueEventListener() برای افزودن یک ValueEventListener به DatabaseReference استفاده کنید.

شنونده پاسخ تماس رویداد استفاده معمولی
ValueEventListener onDataChange() برای تغییرات در کل محتوای یک مسیر بخوانید و گوش دهید.

می‌توانید از متد onDataChange() برای خواندن یک عکس فوری از محتویات در یک مسیر مشخص استفاده کنید، همانطور که در زمان رویداد وجود داشتند. این روش یک بار در زمانی که شنونده متصل می شود و دوباره هر بار که داده ها، از جمله کودکان، تغییر می کنند، فعال می شود. پاسخ تماس رویداد یک عکس فوری حاوی تمام داده‌ها در آن مکان، از جمله داده‌های فرزند ارسال می‌شود. اگر داده ای وجود نداشته باشد، عکس فوری با فراخوانی exists() false و زمانی که getValue() بر روی آن فراخوانی می کنید، null خواهد شد.

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

Kotlin+KTX

val postListener = object : ValueEventListener {
    override fun onDataChange(dataSnapshot: DataSnapshot) {
        // Get Post object and use the values to update the UI
        val post = dataSnapshot.getValue<Post>()
        // ...
    }

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

Java

ValueEventListener postListener = new ValueEventListener() {
    @Override
    public void onDataChange(DataSnapshot dataSnapshot) {
        // Get Post object and use the values to update the UI
        Post post = dataSnapshot.getValue(Post.class);
        // ..
    }

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

شنونده یک DataSnapshot دریافت می کند که حاوی داده ها در مکان مشخص شده در پایگاه داده در زمان رویداد است. فراخوانی getValue() در یک عکس فوری، نمایش شی جاوا از داده ها را برمی گرداند. اگر هیچ داده ای در مکان وجود نداشته باشد، فراخوانی getValue() null برمی گرداند.

در این مثال، ValueEventListener متد onCancelled() را نیز تعریف می کند که در صورت لغو خواندن فراخوانی می شود. برای مثال، اگر کلاینت مجوز خواندن از محل پایگاه داده Firebase را نداشته باشد، می‌توان یک خواندن را لغو کرد. به این روش یک شی DatabaseError ارسال می شود که نشان می دهد چرا خرابی رخ داده است.

داده ها را یک بار بخوانید

یکبار با استفاده از get() بخوانید

SDK برای مدیریت تعاملات با سرورهای پایگاه داده، چه برنامه شما آنلاین یا آفلاین باشد، طراحی شده است.

به طور کلی، شما باید از تکنیک‌های ValueEventListener که در بالا توضیح داده شد برای خواندن داده‌ها استفاده کنید تا از به‌روزرسانی‌های داده‌ها از باطن مطلع شوید. تکنیک‌های شنونده استفاده و صورت‌حساب شما را کاهش می‌دهند، و بهینه‌سازی شده‌اند تا بهترین تجربه را هنگام آنلاین شدن و آفلاین شدن کاربرانتان ارائه دهند.

اگر فقط یک بار به داده ها نیاز دارید، می توانید از get() برای گرفتن عکس فوری از داده ها از پایگاه داده استفاده کنید. اگر به هر دلیلی get() نتواند مقدار سرور را برگرداند، کلاینت حافظه نهان محلی ذخیره‌سازی را بررسی می‌کند و در صورتی که مقدار هنوز پیدا نشد، خطایی را برمی‌گرداند.

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

Kotlin+KTX

mDatabase.child("users").child(userId).get().addOnSuccessListener {
    Log.i("firebase", "Got value ${it.value}")
}.addOnFailureListener{
    Log.e("firebase", "Error getting data", it)
}

Java

mDatabase.child("users").child(userId).get().addOnCompleteListener(new OnCompleteListener<DataSnapshot>() {
    @Override
    public void onComplete(@NonNull Task<DataSnapshot> task) {
        if (!task.isSuccessful()) {
            Log.e("firebase", "Error getting data", task.getException());
        }
        else {
            Log.d("firebase", String.valueOf(task.getResult().getValue()));
        }
    }
});

یک بار با استفاده از شنونده بخوانید

در برخی موارد ممکن است بخواهید به جای بررسی مقدار به روز شده در سرور، مقدار از حافظه نهان محلی بلافاصله برگردانده شود. در این موارد می‌توانید از addListenerForSingleValueEvent برای دریافت فوری داده‌ها از حافظه پنهان دیسک محلی استفاده کنید.

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

به روز رسانی یا حذف داده ها

فیلدهای خاص را به روز کنید

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

هنگام فراخوانی updateChildren() می توانید مقادیر فرزند سطح پایین تر را با تعیین مسیری برای کلید به روز کنید. اگر داده‌ها در مکان‌های مختلف ذخیره می‌شوند تا مقیاس بهتری داشته باشند، می‌توانید تمام نمونه‌های آن داده‌ها را با استفاده از خروجی فن‌آوری داده به‌روزرسانی کنید. به عنوان مثال، یک برنامه وبلاگ نویسی اجتماعی ممکن است یک کلاس Post مانند این داشته باشد:

Kotlin+KTX

@IgnoreExtraProperties
data class Post(
    var uid: String? = "",
    var author: String? = "",
    var title: String? = "",
    var body: String? = "",
    var starCount: Int = 0,
    var stars: MutableMap<String, Boolean> = HashMap(),
) {

    @Exclude
    fun toMap(): Map<String, Any?> {
        return mapOf(
            "uid" to uid,
            "author" to author,
            "title" to title,
            "body" to body,
            "starCount" to starCount,
            "stars" to stars,
        )
    }
}

Java

@IgnoreExtraProperties
public class Post {

    public String uid;
    public String author;
    public String title;
    public String body;
    public int starCount = 0;
    public Map<String, Boolean> stars = new HashMap<>();

    public Post() {
        // Default constructor required for calls to DataSnapshot.getValue(Post.class)
    }

    public Post(String uid, String author, String title, String body) {
        this.uid = uid;
        this.author = author;
        this.title = title;
        this.body = body;
    }

    @Exclude
    public Map<String, Object> toMap() {
        HashMap<String, Object> result = new HashMap<>();
        result.put("uid", uid);
        result.put("author", author);
        result.put("title", title);
        result.put("body", body);
        result.put("starCount", starCount);
        result.put("stars", stars);

        return result;
    }
}

برای ایجاد یک پست و به‌روزرسانی همزمان آن به فید فعالیت اخیر و فید فعالیت کاربر پست کننده، برنامه وبلاگ نویسی از کدی مانند زیر استفاده می‌کند:

Kotlin+KTX

private fun writeNewPost(userId: String, username: String, title: String, body: String) {
    // Create new post at /user-posts/$userid/$postid and at
    // /posts/$postid simultaneously
    val key = database.child("posts").push().key
    if (key == null) {
        Log.w(TAG, "Couldn't get push key for posts")
        return
    }

    val post = Post(userId, username, title, body)
    val postValues = post.toMap()

    val childUpdates = hashMapOf<String, Any>(
        "/posts/$key" to postValues,
        "/user-posts/$userId/$key" to postValues,
    )

    database.updateChildren(childUpdates)
}

Java

private void writeNewPost(String userId, String username, String title, String body) {
    // Create new post at /user-posts/$userid/$postid and at
    // /posts/$postid simultaneously
    String key = mDatabase.child("posts").push().getKey();
    Post post = new Post(userId, username, title, body);
    Map<String, Object> postValues = post.toMap();

    Map<String, Object> childUpdates = new HashMap<>();
    childUpdates.put("/posts/" + key, postValues);
    childUpdates.put("/user-posts/" + userId + "/" + key, postValues);

    mDatabase.updateChildren(childUpdates);
}

این مثال از push() برای ایجاد یک پست در گره حاوی پست برای همه کاربران در /posts/$postid استفاده می کند و همزمان کلید را با getKey() بازیابی می کند. سپس می‌توان از کلید برای ایجاد ورودی دوم در پست‌های کاربر در /user-posts/$userid/$postid استفاده کرد.

با استفاده از این مسیرها، می‌توانید به‌روزرسانی‌های همزمان چندین مکان در درخت JSON را با یک فراخوانی برای updateChildren() انجام دهید، مانند اینکه چگونه این مثال پست جدید را در هر دو مکان ایجاد می‌کند. به‌روزرسانی‌های همزمان ساخته شده به این روش اتمی هستند: یا همه به‌روزرسانی‌ها موفق می‌شوند یا همه به‌روزرسانی‌ها با شکست مواجه می‌شوند.

یک پاسخ به تماس تکمیلی اضافه کنید

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

Kotlin+KTX

database.child("users").child(userId).setValue(user)
    .addOnSuccessListener {
        // Write was successful!
        // ...
    }
    .addOnFailureListener {
        // Write failed
        // ...
    }

Java

mDatabase.child("users").child(userId).setValue(user)
        .addOnSuccessListener(new OnSuccessListener<Void>() {
            @Override
            public void onSuccess(Void aVoid) {
                // Write was successful!
                // ...
            }
        })
        .addOnFailureListener(new OnFailureListener() {
            @Override
            public void onFailure(@NonNull Exception e) {
                // Write failed
                // ...
            }
        });

داده ها را حذف کنید

ساده ترین راه برای حذف داده ها فراخوانی removeValue() بر روی مرجعی به محل آن داده است.

همچنین می توانید با تعیین null به عنوان مقدار برای عملیات نوشتن دیگری مانند setValue() یا updateChildren() حذف کنید. می‌توانید از این تکنیک با updateChildren() برای حذف چند فرزند در یک تماس API استفاده کنید.

شنوندگان را جدا کنید

با فراخوانی متد removeEventListener() در مرجع پایگاه داده Firebase، تماس‌های برگشتی حذف می‌شوند.

اگر شنونده ای چندین بار به یک مکان داده اضافه شده باشد، برای هر رویداد چندین بار فراخوانی می شود و برای حذف کامل باید آن را به همان تعداد دفعه جدا کنید.

فراخوانی removeEventListener() در شنونده والد به طور خودکار شنوندگان ثبت شده در گره های فرزند خود را حذف نمی کند. removeEventListener() همچنین باید در هر شنونده کودک فراخوانی شود تا پاسخ تماس را حذف کند.

ذخیره داده ها به عنوان تراکنش

هنگام کار با داده هایی که ممکن است توسط تغییرات همزمان خراب شوند، مانند شمارنده های افزایشی، می توانید از عملیات تراکنش استفاده کنید. شما به این عملیات دو آرگومان می دهید: یک تابع به روز رسانی و یک فراخوان تکمیل اختیاری. تابع به روز رسانی وضعیت فعلی داده ها را به عنوان آرگومان می گیرد و حالت دلخواه جدیدی را که می خواهید بنویسید برمی گرداند. اگر مشتری دیگری قبل از اینکه مقدار جدید با موفقیت نوشته شود، در مکان بنویسد، تابع به روز رسانی شما دوباره با مقدار فعلی جدید فراخوانی می شود و نوشتن مجدداً امتحان می شود.

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

Kotlin+KTX

private fun onStarClicked(postRef: DatabaseReference) {
    // ...
    postRef.runTransaction(object : Transaction.Handler {
        override fun doTransaction(mutableData: MutableData): Transaction.Result {
            val p = mutableData.getValue(Post::class.java)
                ?: return Transaction.success(mutableData)

            if (p.stars.containsKey(uid)) {
                // Unstar the post and remove self from stars
                p.starCount = p.starCount - 1
                p.stars.remove(uid)
            } else {
                // Star the post and add self to stars
                p.starCount = p.starCount + 1
                p.stars[uid] = true
            }

            // Set value and report transaction success
            mutableData.value = p
            return Transaction.success(mutableData)
        }

        override fun onComplete(
            databaseError: DatabaseError?,
            committed: Boolean,
            currentData: DataSnapshot?,
        ) {
            // Transaction completed
            Log.d(TAG, "postTransaction:onComplete:" + databaseError!!)
        }
    })
}

Java

private void onStarClicked(DatabaseReference postRef) {
    postRef.runTransaction(new Transaction.Handler() {
        @NonNull
        @Override
        public Transaction.Result doTransaction(@NonNull MutableData mutableData) {
            Post p = mutableData.getValue(Post.class);
            if (p == null) {
                return Transaction.success(mutableData);
            }

            if (p.stars.containsKey(getUid())) {
                // Unstar the post and remove self from stars
                p.starCount = p.starCount - 1;
                p.stars.remove(getUid());
            } else {
                // Star the post and add self to stars
                p.starCount = p.starCount + 1;
                p.stars.put(getUid(), true);
            }

            // Set value and report transaction success
            mutableData.setValue(p);
            return Transaction.success(mutableData);
        }

        @Override
        public void onComplete(DatabaseError databaseError, boolean committed,
                               DataSnapshot currentData) {
            // Transaction completed
            Log.d(TAG, "postTransaction:onComplete:" + databaseError);
        }
    });
}

استفاده از تراکنش از نادرست بودن تعداد ستاره‌ها جلوگیری می‌کند، اگر چندین کاربر به طور همزمان یک پست را ستاره‌دار کنند یا مشتری داده‌های قدیمی داشته باشد. اگر تراکنش رد شود، سرور مقدار فعلی را به کلاینت برمی گرداند، که تراکنش را دوباره با مقدار به روز شده اجرا می کند. این کار تا زمانی که تراکنش پذیرفته شود یا تلاش های زیادی انجام شود تکرار می شود.

افزایش سمت سرور اتمی

در مورد استفاده بالا، ما دو مقدار را برای پایگاه داده می نویسیم: شناسه کاربری که پست را ستاره/برداشته ستاره می کند، و تعداد ستاره افزایش یافته. اگر از قبل می‌دانیم که کاربر پست را ستاره‌دار می‌کند، می‌توانیم به جای تراکنش از عملیات افزایش اتمی استفاده کنیم.

Kotlin+KTX

private fun onStarClicked(uid: String, key: String) {
    val updates: MutableMap<String, Any> = hashMapOf(
        "posts/$key/stars/$uid" to true,
        "posts/$key/starCount" to ServerValue.increment(1),
        "user-posts/$uid/$key/stars/$uid" to true,
        "user-posts/$uid/$key/starCount" to ServerValue.increment(1),
    )
    database.updateChildren(updates)
}

Java

private void onStarClicked(String uid, String key) {
    Map<String, Object> updates = new HashMap<>();
    updates.put("posts/"+key+"/stars/"+uid, true);
    updates.put("posts/"+key+"/starCount", ServerValue.increment(1));
    updates.put("user-posts/"+uid+"/"+key+"/stars/"+uid, true);
    updates.put("user-posts/"+uid+"/"+key+"/starCount", ServerValue.increment(1));
    mDatabase.updateChildren(updates);
}

این کد از عملیات تراکنش استفاده نمی‌کند، بنابراین در صورت بروز تضاد به‌روزرسانی، به‌طور خودکار دوباره اجرا نمی‌شود. با این حال، از آنجایی که عملیات افزایش مستقیماً در سرور پایگاه داده انجام می شود، هیچ احتمالی برای تداخل وجود ندارد.

اگر می‌خواهید تضادهای خاص برنامه را شناسایی و رد کنید، مانند ستاره‌دار شدن پستی که قبلاً توسط کاربر ستاره‌دار شده است، باید قوانین امنیتی سفارشی را برای آن مورد استفاده بنویسید.

با داده ها به صورت آفلاین کار کنید

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

هر کلاینت متصل به پایگاه داده Firebase، نسخه داخلی خود را از هر داده ای که شنونده در آن استفاده می شود یا پرچم گذاری شده است تا با سرور همگام شود، حفظ می کند. هنگامی که داده خوانده یا نوشته می شود، ابتدا از این نسخه محلی داده ها استفاده می شود. سپس مشتری Firebase آن داده ها را با سرورهای پایگاه داده راه دور و با سایر مشتریان بر اساس "بهترین تلاش" همگام سازی می کند.

در نتیجه، همه نوشته‌ها در پایگاه داده بلافاصله قبل از هر گونه تعامل با سرور، رویدادهای محلی را راه‌اندازی می‌کنند. این بدان معناست که برنامه شما بدون توجه به تأخیر شبکه یا اتصال، پاسخگو باقی می ماند.

پس از برقراری مجدد اتصال، برنامه شما مجموعه مناسبی از رویدادها را دریافت می کند تا کلاینت بدون نیاز به نوشتن کد سفارشی با وضعیت سرور فعلی همگام شود.

در مورد قابلیت های آنلاین و آفلاین بیشتر بدانید در مورد رفتار آفلاین بیشتر صحبت خواهیم کرد.

مراحل بعدی

،

این سند اصول خواندن و نوشتن داده های Firebase را پوشش می دهد.

داده های Firebase در یک مرجع FirebaseDatabase نوشته می شود و با پیوست کردن یک شنونده ناهمزمان به مرجع بازیابی می شود. شنونده یک بار برای وضعیت اولیه داده ها و بار دیگر هر زمان که داده ها تغییر کند فعال می شود.

(اختیاری) نمونه اولیه و آزمایش با Firebase Local Emulator Suite

قبل از صحبت در مورد نحوه خواندن و نوشتن برنامه شما از Realtime Database ، بیایید مجموعه‌ای از ابزارهایی را که می‌توانید برای نمونه‌سازی و آزمایش عملکرد Realtime Database استفاده کنید، معرفی می‌کنیم: Firebase Local Emulator Suite . اگر در حال آزمایش مدل های مختلف داده، بهینه سازی قوانین امنیتی خود هستید یا برای یافتن مقرون به صرفه ترین راه برای تعامل با back-end تلاش می کنید، اینکه بتوانید به صورت محلی بدون استقرار سرویس های زنده کار کنید می تواند ایده خوبی باشد.

شبیه ساز Realtime Database بخشی از Local Emulator Suite است که به برنامه شما امکان می دهد با محتوای پایگاه داده شبیه سازی شده و پیکربندی شما و همچنین به صورت اختیاری منابع پروژه شبیه سازی شده شما (توابع، سایر پایگاه های داده و قوانین امنیتی) تعامل داشته باشد.

استفاده از شبیه ساز Realtime Database فقط شامل چند مرحله است:

  1. افزودن یک خط کد به پیکربندی آزمایشی برنامه برای اتصال به شبیه ساز.
  2. از ریشه دایرکتوری پروژه محلی خود، firebase emulators:start اجرا کنید.
  3. برقراری تماس از کد نمونه اولیه برنامه خود با استفاده از یک SDK پلتفرم Realtime Database به طور معمول، یا با استفاده از Realtime Database REST API.

یک بررسی دقیق شامل Realtime Database و Cloud Functions در دسترس است. همچنین باید نگاهی به معرفی Local Emulator Suite داشته باشید.

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

برای خواندن یا نوشتن داده ها از پایگاه داده، به یک نمونه از DatabaseReference نیاز دارید:

Kotlin+KTX

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

Java

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

داده ها را بنویسید

عملیات نوشتن اولیه

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

  • انواع پاس که با انواع JSON موجود مطابقت دارند به شرح زیر:
    • String
    • Long
    • Double
    • Boolean
    • Map<String, Object>
    • List<Object>
  • یک شی جاوا سفارشی را ارسال کنید، اگر کلاسی که آن را تعریف می‌کند سازنده پیش‌فرضی داشته باشد که هیچ آرگومان نمی‌گیرد و دارای گیرنده‌های عمومی برای ویژگی‌هایی است که باید تخصیص داده شوند.

اگر از یک شی جاوا استفاده می کنید، محتویات شی شما به طور خودکار به مکان های فرزند به صورت تو در تو نگاشت می شود. استفاده از یک شی جاوا نیز معمولاً کد شما را خواناتر و نگهداری آسان تر می کند. به عنوان مثال، اگر یک برنامه با نمایه کاربری اصلی دارید، شیء User شما ممکن است به شکل زیر باشد:

Kotlin+KTX

@IgnoreExtraProperties
data class User(val username: String? = null, val email: String? = null) {
    // Null default values create a no-argument default constructor, which is needed
    // for deserialization from a DataSnapshot.
}

Java

@IgnoreExtraProperties
public class User {

    public String username;
    public String email;

    public User() {
        // Default constructor required for calls to DataSnapshot.getValue(User.class)
    }

    public User(String username, String email) {
        this.username = username;
        this.email = email;
    }

}

می توانید یک کاربر با setValue() به صورت زیر اضافه کنید:

Kotlin+KTX

fun writeNewUser(userId: String, name: String, email: String) {
    val user = User(name, email)

    database.child("users").child(userId).setValue(user)
}

Java

public void writeNewUser(String userId, String name, String email) {
    User user = new User(name, email);

    mDatabase.child("users").child(userId).setValue(user);
}

استفاده از setValue() در این روش داده ها را در مکان مشخص شده، از جمله گره های فرزند، بازنویسی می کند. با این حال، همچنان می‌توانید یک فرزند را بدون بازنویسی کل شی به‌روزرسانی کنید. اگر می خواهید به کاربران اجازه دهید پروفایل های خود را به روز کنند، می توانید نام کاربری را به صورت زیر به روز کنید:

Kotlin+KTX

database.child("users").child(userId).child("username").setValue(name)

Java

mDatabase.child("users").child(userId).child("username").setValue(name);

داده ها را بخوانید

خواندن داده ها با شنوندگان مداوم

برای خواندن داده ها در یک مسیر و گوش دادن به تغییرات، از متد addValueEventListener() برای افزودن یک ValueEventListener به DatabaseReference استفاده کنید.

شنونده پاسخ تماس رویداد استفاده معمولی
ValueEventListener onDataChange() برای تغییرات در کل محتوای یک مسیر بخوانید و گوش دهید.

می‌توانید از متد onDataChange() برای خواندن یک عکس فوری از محتویات در یک مسیر مشخص استفاده کنید، همانطور که در زمان رویداد وجود داشتند. این روش یک بار در زمانی که شنونده متصل می شود و دوباره هر بار که داده ها، از جمله کودکان، تغییر می کنند، فعال می شود. پاسخ تماس رویداد یک عکس فوری حاوی تمام داده‌ها در آن مکان، از جمله داده‌های فرزند ارسال می‌شود. اگر داده ای وجود نداشته باشد، عکس فوری با فراخوانی exists() false و زمانی که getValue() بر روی آن فراخوانی می کنید، null خواهد شد.

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

Kotlin+KTX

val postListener = object : ValueEventListener {
    override fun onDataChange(dataSnapshot: DataSnapshot) {
        // Get Post object and use the values to update the UI
        val post = dataSnapshot.getValue<Post>()
        // ...
    }

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

Java

ValueEventListener postListener = new ValueEventListener() {
    @Override
    public void onDataChange(DataSnapshot dataSnapshot) {
        // Get Post object and use the values to update the UI
        Post post = dataSnapshot.getValue(Post.class);
        // ..
    }

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

شنونده یک DataSnapshot دریافت می کند که حاوی داده ها در مکان مشخص شده در پایگاه داده در زمان رویداد است. فراخوانی getValue() در یک عکس فوری، نمایش شی جاوا از داده ها را برمی گرداند. اگر هیچ داده ای در مکان وجود نداشته باشد، فراخوانی getValue() null برمی گرداند.

در این مثال، ValueEventListener متد onCancelled() را نیز تعریف می کند که در صورت لغو خواندن فراخوانی می شود. برای مثال، اگر کلاینت مجوز خواندن از محل پایگاه داده Firebase را نداشته باشد، می‌توان یک خواندن را لغو کرد. به این روش یک شی DatabaseError ارسال می شود که نشان می دهد چرا خرابی رخ داده است.

یک بار داده ها را بخوانید

یکبار با استفاده از get() بخوانید

SDK برای مدیریت تعاملات با سرورهای پایگاه داده، چه برنامه شما آنلاین یا آفلاین باشد، طراحی شده است.

به طور کلی، شما باید از تکنیک‌های ValueEventListener که در بالا توضیح داده شد برای خواندن داده‌ها استفاده کنید تا از به‌روزرسانی‌های داده‌ها از باطن مطلع شوید. تکنیک‌های شنونده استفاده و صورت‌حساب شما را کاهش می‌دهند، و بهینه‌سازی شده‌اند تا بهترین تجربه را هنگام آنلاین شدن و آفلاین شدن کاربرانتان ارائه دهند.

اگر فقط یک بار به داده ها نیاز دارید، می توانید از get() برای گرفتن عکس فوری از داده ها از پایگاه داده استفاده کنید. اگر به هر دلیلی get() نتواند مقدار سرور را برگرداند، کلاینت حافظه نهان محلی ذخیره‌سازی را بررسی می‌کند و در صورتی که مقدار هنوز پیدا نشد، خطایی را برمی‌گرداند.

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

Kotlin+KTX

mDatabase.child("users").child(userId).get().addOnSuccessListener {
    Log.i("firebase", "Got value ${it.value}")
}.addOnFailureListener{
    Log.e("firebase", "Error getting data", it)
}

Java

mDatabase.child("users").child(userId).get().addOnCompleteListener(new OnCompleteListener<DataSnapshot>() {
    @Override
    public void onComplete(@NonNull Task<DataSnapshot> task) {
        if (!task.isSuccessful()) {
            Log.e("firebase", "Error getting data", task.getException());
        }
        else {
            Log.d("firebase", String.valueOf(task.getResult().getValue()));
        }
    }
});

یک بار با استفاده از شنونده بخوانید

در برخی موارد ممکن است بخواهید به جای بررسی مقدار به روز شده در سرور، مقدار از حافظه نهان محلی بلافاصله برگردانده شود. در این موارد می‌توانید از addListenerForSingleValueEvent برای دریافت فوری داده‌ها از حافظه پنهان دیسک محلی استفاده کنید.

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

به روز رسانی یا حذف داده ها

فیلدهای خاص را به روز کنید

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

هنگام فراخوانی updateChildren() می توانید مقادیر فرزند سطح پایین تر را با تعیین مسیری برای کلید به روز کنید. اگر داده‌ها در مکان‌های مختلف ذخیره می‌شوند تا مقیاس بهتری داشته باشند، می‌توانید تمام نمونه‌های آن داده‌ها را با استفاده از خروجی فن‌آوری داده به‌روزرسانی کنید. به عنوان مثال، یک برنامه وبلاگ نویسی اجتماعی ممکن است یک کلاس Post مانند این داشته باشد:

Kotlin+KTX

@IgnoreExtraProperties
data class Post(
    var uid: String? = "",
    var author: String? = "",
    var title: String? = "",
    var body: String? = "",
    var starCount: Int = 0,
    var stars: MutableMap<String, Boolean> = HashMap(),
) {

    @Exclude
    fun toMap(): Map<String, Any?> {
        return mapOf(
            "uid" to uid,
            "author" to author,
            "title" to title,
            "body" to body,
            "starCount" to starCount,
            "stars" to stars,
        )
    }
}

Java

@IgnoreExtraProperties
public class Post {

    public String uid;
    public String author;
    public String title;
    public String body;
    public int starCount = 0;
    public Map<String, Boolean> stars = new HashMap<>();

    public Post() {
        // Default constructor required for calls to DataSnapshot.getValue(Post.class)
    }

    public Post(String uid, String author, String title, String body) {
        this.uid = uid;
        this.author = author;
        this.title = title;
        this.body = body;
    }

    @Exclude
    public Map<String, Object> toMap() {
        HashMap<String, Object> result = new HashMap<>();
        result.put("uid", uid);
        result.put("author", author);
        result.put("title", title);
        result.put("body", body);
        result.put("starCount", starCount);
        result.put("stars", stars);

        return result;
    }
}

برای ایجاد یک پست و به‌روزرسانی همزمان آن به فید فعالیت اخیر و فید فعالیت کاربر پست کننده، برنامه وبلاگ نویسی از کدی مانند زیر استفاده می‌کند:

Kotlin+KTX

private fun writeNewPost(userId: String, username: String, title: String, body: String) {
    // Create new post at /user-posts/$userid/$postid and at
    // /posts/$postid simultaneously
    val key = database.child("posts").push().key
    if (key == null) {
        Log.w(TAG, "Couldn't get push key for posts")
        return
    }

    val post = Post(userId, username, title, body)
    val postValues = post.toMap()

    val childUpdates = hashMapOf<String, Any>(
        "/posts/$key" to postValues,
        "/user-posts/$userId/$key" to postValues,
    )

    database.updateChildren(childUpdates)
}

Java

private void writeNewPost(String userId, String username, String title, String body) {
    // Create new post at /user-posts/$userid/$postid and at
    // /posts/$postid simultaneously
    String key = mDatabase.child("posts").push().getKey();
    Post post = new Post(userId, username, title, body);
    Map<String, Object> postValues = post.toMap();

    Map<String, Object> childUpdates = new HashMap<>();
    childUpdates.put("/posts/" + key, postValues);
    childUpdates.put("/user-posts/" + userId + "/" + key, postValues);

    mDatabase.updateChildren(childUpdates);
}

این مثال از push() برای ایجاد یک پست در گره حاوی پست برای همه کاربران در /posts/$postid استفاده می کند و همزمان کلید را با getKey() بازیابی می کند. سپس می‌توان از کلید برای ایجاد ورودی دوم در پست‌های کاربر در /user-posts/$userid/$postid استفاده کرد.

با استفاده از این مسیرها، می‌توانید به‌روزرسانی‌های همزمان چندین مکان در درخت JSON را با یک فراخوانی برای updateChildren() انجام دهید، مانند اینکه چگونه این مثال پست جدید را در هر دو مکان ایجاد می‌کند. به روزرسانی های همزمان ساخته شده از این طریق اتمی است: یا همه به روزرسانی ها موفق می شوند یا همه به روزرسانی ها شکست می خورند.

پاسخ به تماس کامل را اضافه کنید

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

Kotlin+KTX

database.child("users").child(userId).setValue(user)
    .addOnSuccessListener {
        // Write was successful!
        // ...
    }
    .addOnFailureListener {
        // Write failed
        // ...
    }

Java

mDatabase.child("users").child(userId).setValue(user)
        .addOnSuccessListener(new OnSuccessListener<Void>() {
            @Override
            public void onSuccess(Void aVoid) {
                // Write was successful!
                // ...
            }
        })
        .addOnFailureListener(new OnFailureListener() {
            @Override
            public void onFailure(@NonNull Exception e) {
                // Write failed
                // ...
            }
        });

داده ها را حذف کنید

ساده ترین راه برای حذف داده ها ، فراخوانی removeValue() در مورد مراجعه به محل آن داده ها است.

همچنین می توانید با مشخص کردن null به عنوان مقدار عمل نوشتن دیگر مانند setValue() یا updateChildren() حذف کنید. برای حذف چندین کودک در یک تماس API می توانید از این تکنیک با updateChildren() استفاده کنید.

شنوندگان را جدا کنید

تماس تلفنی با فراخوانی روش removeEventListener() در مرجع پایگاه داده Firebase شما حذف می شود.

اگر یک شنونده چندین بار به یک مکان داده اضافه شده است ، برای هر رویداد چندین بار خوانده می شود و شما باید آن را به همان تعداد بار جدا کنید تا آن را به طور کامل حذف کنید.

فراخوانی removeEventListener() روی شنونده والدین ، ​​شنوندگان ثبت شده در گره های فرزند خود را به طور خودکار حذف نمی کند. removeEventListener() نیز باید از هر کودک شنونده خواسته شود تا پاسخ به تماس را حذف کنند.

داده ها را به عنوان معاملات ذخیره کنید

هنگام کار با داده هایی که می توانند با اصلاحات همزمان ، مانند پیشخوان های افزایشی خراب شوند ، می توانید از یک عملیات معامله استفاده کنید. شما این عملیات را دو آرگومان ارائه می دهید: یک عملکرد به روزرسانی و یک پاسخ به اتمام تکمیل اختیاری. عملکرد به روزرسانی وضعیت فعلی داده ها را به عنوان یک آرگومان در نظر می گیرد و حالت مورد نظر جدید را که می خواهید بنویسید برمی گرداند. اگر مشتری دیگری قبل از اینکه ارزش جدید شما با موفقیت نوشته شود ، به مکان می نویسد ، عملکرد به روزرسانی شما دوباره با مقدار فعلی فعلی فراخوانی می شود و نوشتن دوباره انجام می شود.

به عنوان مثال ، به عنوان مثال برنامه وبلاگ نویسی اجتماعی ، می توانید به کاربران اجازه دهید پست ها و پست های UNSTAR را بازی کنند و پیگیری کنند که چند ستاره پست به شرح زیر است:

Kotlin+KTX

private fun onStarClicked(postRef: DatabaseReference) {
    // ...
    postRef.runTransaction(object : Transaction.Handler {
        override fun doTransaction(mutableData: MutableData): Transaction.Result {
            val p = mutableData.getValue(Post::class.java)
                ?: return Transaction.success(mutableData)

            if (p.stars.containsKey(uid)) {
                // Unstar the post and remove self from stars
                p.starCount = p.starCount - 1
                p.stars.remove(uid)
            } else {
                // Star the post and add self to stars
                p.starCount = p.starCount + 1
                p.stars[uid] = true
            }

            // Set value and report transaction success
            mutableData.value = p
            return Transaction.success(mutableData)
        }

        override fun onComplete(
            databaseError: DatabaseError?,
            committed: Boolean,
            currentData: DataSnapshot?,
        ) {
            // Transaction completed
            Log.d(TAG, "postTransaction:onComplete:" + databaseError!!)
        }
    })
}

Java

private void onStarClicked(DatabaseReference postRef) {
    postRef.runTransaction(new Transaction.Handler() {
        @NonNull
        @Override
        public Transaction.Result doTransaction(@NonNull MutableData mutableData) {
            Post p = mutableData.getValue(Post.class);
            if (p == null) {
                return Transaction.success(mutableData);
            }

            if (p.stars.containsKey(getUid())) {
                // Unstar the post and remove self from stars
                p.starCount = p.starCount - 1;
                p.stars.remove(getUid());
            } else {
                // Star the post and add self to stars
                p.starCount = p.starCount + 1;
                p.stars.put(getUid(), true);
            }

            // Set value and report transaction success
            mutableData.setValue(p);
            return Transaction.success(mutableData);
        }

        @Override
        public void onComplete(DatabaseError databaseError, boolean committed,
                               DataSnapshot currentData) {
            // Transaction completed
            Log.d(TAG, "postTransaction:onComplete:" + databaseError);
        }
    });
}

استفاده از معامله مانع از عدم موفقیت در شمارش ستاره ها در صورتی است که چندین کاربر در همان زمان پستی را در همان زمان یا مشتری داده های بی نظیر داشته باشند. اگر معامله رد شود ، سرور مقدار فعلی را به مشتری باز می گرداند ، که معامله را دوباره با مقدار به روز شده اجرا می کند. این تکرار می شود تا اینکه معامله پذیرفته شود یا تلاش های زیادی انجام شود.

افزایش سمت سرور اتمی

در مورد استفاده فوق ما در حال نوشتن دو مقدار به پایگاه داده هستیم: شناسه کاربر که پست را بازی می کند/از آن استفاده می کند و تعداد ستاره های افزایش یافته. اگر از قبل بدانیم که کاربر در حال بازی در پست است ، می توانیم به جای معامله ، از یک عملیات افزایش اتمی استفاده کنیم.

Kotlin+KTX

private fun onStarClicked(uid: String, key: String) {
    val updates: MutableMap<String, Any> = hashMapOf(
        "posts/$key/stars/$uid" to true,
        "posts/$key/starCount" to ServerValue.increment(1),
        "user-posts/$uid/$key/stars/$uid" to true,
        "user-posts/$uid/$key/starCount" to ServerValue.increment(1),
    )
    database.updateChildren(updates)
}

Java

private void onStarClicked(String uid, String key) {
    Map<String, Object> updates = new HashMap<>();
    updates.put("posts/"+key+"/stars/"+uid, true);
    updates.put("posts/"+key+"/starCount", ServerValue.increment(1));
    updates.put("user-posts/"+uid+"/"+key+"/stars/"+uid, true);
    updates.put("user-posts/"+uid+"/"+key+"/starCount", ServerValue.increment(1));
    mDatabase.updateChildren(updates);
}

این کد از یک عملیات معامله استفاده نمی کند ، بنابراین در صورت بروزرسانی متناقض ، به طور خودکار دوباره اجرا نمی شود. با این حال ، از آنجا که عملیات افزایشی مستقیماً در سرور پایگاه داده اتفاق می افتد ، هیچ فرصتی برای درگیری وجود ندارد.

اگر می خواهید تعارضات خاص برنامه را تشخیص داده و رد کنید ، مانند کاربر که پستی را که قبلاً در آن بازی کرده بود ، بازی می کند ، باید قوانین امنیتی سفارشی را برای آن مورد استفاده بنویسید.

با داده های آفلاین کار کنید

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

هر مشتری متصل به یک پایگاه داده Firebase نسخه داخلی خود را از هرگونه داده ای که شنوندگان در آن استفاده می شود یا پرچم گذاری می شود برای همگام سازی با سرور حفظ می کند. هنگامی که داده ها خوانده می شوند یا نوشته می شوند ، ابتدا از این نسخه محلی داده ها استفاده می شود. مشتری Firebase سپس آن داده ها را با سرورهای پایگاه داده از راه دور و با سایر مشتریان بر اساس "بهترین تلاش" هماهنگ می کند.

در نتیجه ، همه قبل از هرگونه تعامل با سرور ، بلافاصله وقایع محلی را می نویسند. این بدان معنی است که برنامه شما بدون در نظر گرفتن تأخیر شبکه یا اتصال ، پاسخگو است.

پس از برقراری مجدد اتصال ، برنامه شما مجموعه مناسبی از رویدادها را دریافت می کند تا مشتری بدون نیاز به نوشتن کد سفارشی ، با وضعیت سرور فعلی همگام شود.

ما در مورد رفتار آفلاین بیشتر صحبت خواهیم کرد تا درباره قابلیت های آنلاین و آفلاین اطلاعات بیشتری کسب کنیم .

مراحل بعدی

،

این سند اصول اولیه خواندن و نوشتن داده های Firebase را در بر می گیرد.

داده های Firebase به یک مرجع FirebaseDatabase نوشته شده و با اتصال یک شنونده ناهمزمان به مرجع بازیابی می شود. شنونده یک بار برای وضعیت اولیه داده ها و در هر زمان دوباره داده ها تغییر می کند.

(اختیاری) نمونه اولیه و آزمایش با Firebase Local Emulator Suite

قبل از صحبت در مورد نحوه خواندن برنامه شما و نوشتن به Realtime Database ، بیایید مجموعه ای از ابزارهایی را که می توانید برای نمونه اولیه و آزمایش عملکرد Realtime Database استفاده کنید معرفی کنیم: Firebase Local Emulator Suite . اگر سعی می کنید مدل های مختلف داده ، بهینه سازی قوانین امنیتی خود یا تلاش برای یافتن مقرون به صرفه ترین روش برای تعامل با پشتی را پیدا کنید ، قادر به کار محلی بدون استقرار خدمات زنده می تواند ایده خوبی باشد.

یک شبیه ساز Realtime Database بخشی از Local Emulator Suite است که برنامه شما را قادر می سازد با محتوای و پیکربندی پایگاه داده تقلید شده شما و همچنین منابع پروژه تقلید شده شما (توابع ، سایر پایگاه داده ها و قوانین امنیتی) تعامل داشته باشد.

استفاده از شبیه ساز Realtime Database فقط چند مرحله را شامل می شود:

  1. اضافه کردن یک خط کد به پیکربندی تست برنامه خود برای اتصال به شبیه ساز.
  2. از ریشه دایرکتوری پروژه محلی خود ، اجرای firebase emulators:start .
  3. برقراری تماس از کد نمونه اولیه برنامه خود با استفاده از یک پلت فرم Realtime Database SDK طبق معمول یا با استفاده از API Realtime Database REST API.

یک پیاده روی دقیق شامل Realtime Database و Cloud Functions در دسترس است. همچنین باید نگاهی به مقدمه Suite Local Emulator Suite داشته باشید.

یک پایگاه داده دریافت کنید

برای خواندن یا نوشتن داده ها از پایگاه داده ، به نمونه ای از DatabaseReference نیاز دارید:

Kotlin+KTX

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

Java

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

داده ها را بنویسید

عملیات نوشتن اساسی

برای عملیات نوشتن اساسی ، می توانید از setValue() برای ذخیره داده ها در یک مرجع مشخص استفاده کنید و هرگونه داده موجود را در آن مسیر جایگزین کنید. می توانید از این روش استفاده کنید:

  • انواع عبور که با انواع JSON موجود به شرح زیر مطابقت دارد:
    • String
    • Long
    • Double
    • Boolean
    • Map<String, Object>
    • List<Object>
  • یک شیء جاوا سفارشی را عبور دهید ، اگر کلاس که آن را تعریف می کند ، یک سازنده پیش فرض دارد که هیچ استدلالی در نظر نمی گیرد و دارای دریافت کننده های عمومی برای اختصاص دادن خواص است.

اگر از یک شیء جاوا استفاده می کنید ، محتوای شیء شما به طور خودکار در مکان های کودک به روشی تو در تو نقشه برداری می شود. استفاده از یک شیء جاوا نیز به طور معمول باعث می شود کد شما قابل خواندن و نگهداری آسان تر باشد. به عنوان مثال ، اگر برنامه ای با نمایه کاربر اصلی دارید ، ممکن است شیء User شما به شرح زیر باشد:

Kotlin+KTX

@IgnoreExtraProperties
data class User(val username: String? = null, val email: String? = null) {
    // Null default values create a no-argument default constructor, which is needed
    // for deserialization from a DataSnapshot.
}

Java

@IgnoreExtraProperties
public class User {

    public String username;
    public String email;

    public User() {
        // Default constructor required for calls to DataSnapshot.getValue(User.class)
    }

    public User(String username, String email) {
        this.username = username;
        this.email = email;
    }

}

می توانید یک کاربر را با setValue() به شرح زیر اضافه کنید:

Kotlin+KTX

fun writeNewUser(userId: String, name: String, email: String) {
    val user = User(name, email)

    database.child("users").child(userId).setValue(user)
}

Java

public void writeNewUser(String userId, String name, String email) {
    User user = new User(name, email);

    mDatabase.child("users").child(userId).setValue(user);
}

با استفاده از setValue() از این طریق داده ها را در محل مشخص شده ، از جمله هر گره کودک بازنویسی می کند. با این حال ، شما هنوز هم می توانید کودک را بدون بازنویسی کل شی به روز کنید. اگر می خواهید به کاربران اجازه دهید پروفایل های خود را به روز کنند ، می توانید نام کاربری را به شرح زیر به روز کنید:

Kotlin+KTX

database.child("users").child(userId).child("username").setValue(name)

Java

mDatabase.child("users").child(userId).child("username").setValue(name);

داده ها را بخوانید

داده ها را با شنوندگان مداوم بخوانید

برای خواندن داده ها در یک مسیر و گوش دادن به تغییرات ، از روش addValueEventListener() استفاده کنید تا یک ValueEventListener به یک DatabaseReference اضافه کنید.

شنونده تماس تلفنی استفاده معمولی
ValueEventListener onDataChange() برای تغییر در کل مطالب یک مسیر بخوانید و گوش دهید.

شما می توانید از روش onDataChange() برای خواندن عکس فوری استاتیک محتویات در یک مسیر معین استفاده کنید ، همانطور که در زمان رویداد وجود داشتند. این روش یک بار ایجاد می شود که شنونده وصل شود و هر بار که داده ها ، از جمله کودکان ، تغییر می کند. پاسخ به تماس تلفنی با یک عکس فوری که شامل کلیه داده های موجود در آن مکان ، از جمله داده های کودک است ، منتقل می شود. اگر داده ای وجود نداشته باشد ، هنگام تماس exists() getValue() روی آن ، عکس فوری به null false می گردد.

مثال زیر یک برنامه وبلاگ نویسی اجتماعی را بازیابی می کند که جزئیات یک پست از پایگاه داده را بازیابی می کند:

Kotlin+KTX

val postListener = object : ValueEventListener {
    override fun onDataChange(dataSnapshot: DataSnapshot) {
        // Get Post object and use the values to update the UI
        val post = dataSnapshot.getValue<Post>()
        // ...
    }

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

Java

ValueEventListener postListener = new ValueEventListener() {
    @Override
    public void onDataChange(DataSnapshot dataSnapshot) {
        // Get Post object and use the values to update the UI
        Post post = dataSnapshot.getValue(Post.class);
        // ..
    }

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

شنونده یک DataSnapshot دریافت می کند که حاوی داده ها در مکان مشخص شده در پایگاه داده در زمان رویداد است. فراخوانی getValue() در عکس فوری بازنمایی شیء جاوا از داده ها را برمی گرداند. اگر هیچ داده ای در محل وجود نداشته باشد ، تماس با getValue() null برمی گرداند.

در این مثال ، ValueEventListener همچنین روش onCancelled() را تعریف می کند که در صورت لغو خواندن خوانده می شود. به عنوان مثال ، اگر مشتری اجازه خواندن از محل پایگاه داده Firebase را نداشته باشد ، می تواند خواندن را لغو کند. این روش به یک شیء DatabaseError منتقل می شود که نشان می دهد چرا این شکست رخ داده است.

داده ها را یک بار بخوانید

یک بار با استفاده از get () بخوانید

SDK برای مدیریت تعامل با سرورهای بانک اطلاعاتی طراحی شده است که آیا برنامه شما بصورت آنلاین یا آفلاین است.

به طور کلی ، شما باید از تکنیک های ValueEventListener که در بالا توضیح داده شده است برای خواندن داده ها استفاده کنید تا از به روزرسانی به داده های مورد پس زمینه مطلع شوید. تکنیک های شنونده میزان استفاده و صورتحساب شما را کاهش می دهد و بهینه شده است تا به کاربران شما بهترین تجربه را در هنگام آنلاین و آفلاین انجام دهد.

اگر فقط یک بار به داده ها نیاز دارید ، می توانید get() برای دریافت عکس فوری از داده ها از پایگاه داده استفاده کنید. اگر به هر دلیلی get() نتواند مقدار سرور را برگرداند ، مشتری در صورت عدم یافتن مقدار ، حافظه نهان ذخیره سازی محلی را بررسی می کند و خطایی را برمی گرداند.

استفاده غیر ضروری از get() می تواند استفاده از پهنای باند را افزایش داده و منجر به از بین رفتن عملکرد شود ، که می توان با استفاده از شنونده Realtime همانطور که در بالا نشان داده شد ، جلوگیری کرد.

Kotlin+KTX

mDatabase.child("users").child(userId).get().addOnSuccessListener {
    Log.i("firebase", "Got value ${it.value}")
}.addOnFailureListener{
    Log.e("firebase", "Error getting data", it)
}

Java

mDatabase.child("users").child(userId).get().addOnCompleteListener(new OnCompleteListener<DataSnapshot>() {
    @Override
    public void onComplete(@NonNull Task<DataSnapshot> task) {
        if (!task.isSuccessful()) {
            Log.e("firebase", "Error getting data", task.getException());
        }
        else {
            Log.d("firebase", String.valueOf(task.getResult().getValue()));
        }
    }
});

یک بار با استفاده از شنونده بخوانید

در بعضی موارد ممکن است بخواهید به جای اینکه یک مقدار به روز شده در سرور را بررسی کنید ، می خواهید مقدار از حافظه نهان محلی بلافاصله برگردانده شود. در این موارد می توانید از addListenerForSingleValueEvent استفاده کنید تا بلافاصله داده ها را از حافظه نهان محلی دریافت کنید.

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

به روزرسانی یا حذف داده ها

زمینه های خاص را به روز کنید

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

هنگام تماس با updateChildren() ، می توانید با مشخص کردن مسیری برای کلید ، مقادیر کودک سطح پایین را به روز کنید. اگر داده ها در چندین مکان برای مقیاس بهتر ذخیره می شوند ، می توانید تمام نمونه های آن داده ها را با استفاده از Data Fan-Out به روز کنید. به عنوان مثال ، یک برنامه وبلاگ نویسی اجتماعی ممکن است یک کلاس Post مانند این داشته باشد:

Kotlin+KTX

@IgnoreExtraProperties
data class Post(
    var uid: String? = "",
    var author: String? = "",
    var title: String? = "",
    var body: String? = "",
    var starCount: Int = 0,
    var stars: MutableMap<String, Boolean> = HashMap(),
) {

    @Exclude
    fun toMap(): Map<String, Any?> {
        return mapOf(
            "uid" to uid,
            "author" to author,
            "title" to title,
            "body" to body,
            "starCount" to starCount,
            "stars" to stars,
        )
    }
}

Java

@IgnoreExtraProperties
public class Post {

    public String uid;
    public String author;
    public String title;
    public String body;
    public int starCount <