تفعيل إمكانيات وضع عدم الاتصال بالإنترنت على نظام التشغيل Android

تعمل تطبيقات Firebase حتى إذا فقد تطبيقك الشبكة مؤقتًا. الاتصال. بالإضافة إلى ذلك، يوفر Firebase أدوات للاحتفاظ بالبيانات محليًا، وإدارة التواجد ووقت الاستجابة.

ثبات القرص

تتعامل تطبيقات Firebase مع الانقطاعات المؤقتة في الشبكة تلقائيًا. تتوفّر البيانات المخزّنة مؤقتًا عندما تكون غير متصل بالإنترنت، ويعيد Firebase إرسال أي عمليات كتابة. عند استعادة اتصال الشبكة.

عند تفعيل خاصية الاحتفاظ بالقرص، يكتب تطبيقك البيانات محليًا على حتى يحافظ تطبيقك على حالته أثناء عدم الاتصال بالإنترنت، حتى إذا كان المستخدم أو نظام التشغيل يعيد تشغيل التطبيق.

يمكنك تفعيل ميزة تثبيت القرص باستخدام سطر واحد فقط من الرمز.

Kotlin+KTX

Firebase.database.setPersistenceEnabled(true)

Java

FirebaseDatabase.getInstance().setPersistenceEnabled(true);

سلوك الاستمرارية

من خلال تفعيل ميزة الثبات، أي بيانات يقوم برنامج قاعدة بيانات Firebase في الوقت الفعلي تتم مزامنته أثناء استمرار الاتصال بالإنترنت على القرص وإتاحتها في وضع عدم الاتصال، حتى عندما يعيد المستخدم أو نظام التشغيل تشغيل التطبيق. هذا يعني أن كما لو كان على الإنترنت باستخدام البيانات المحلية المخزنة في ذاكرة التخزين المؤقت. سيستمر تنشيط استدعاءات المستمعين للحصول على التحديثات المحلية.

يحتفظ برنامج قاعدة بيانات Firebase في الوقت الفعلي تلقائيًا بقائمة انتظار لجميع كتابة العمليات التي يتم إجراؤها أثناء عدم اتصال التطبيق بالإنترنت. عند تمكين الثبات، يتم أيضًا حفظ قائمة الانتظار هذه على القرص حتى يكون جميع عندما تتوفر كتاباتك عندما يستخدم المستخدم أو نظام التشغيل لإعادة تشغيل التطبيق. عندما يستعيد التطبيق الاتصال، فإن جميع يتم إرسال العمليات إلى خادم قاعدة بيانات Firebase في الوقت الفعلي.

إذا كان تطبيقك يستخدم مصادقة Firebase، يحتفظ عميل قاعدة بيانات Firebase في الوقت الفعلي بمصادقة المستخدم الرمز المميز عبر عمليات إعادة تشغيل التطبيق. إذا انتهت صلاحية الرمز المميّز للمصادقة عندما يكون تطبيقك غير متصل بالإنترنت، سيتم إيقاف البرنامج مؤقتًا. كتابة العمليات حتى يعيد التطبيق مصادقة المستخدم، وإلا عمليات الكتابة بسبب قواعد الأمان.

الحفاظ على إعادة تحميل البيانات

وتزامن قاعدة بيانات Firebase في الوقت الفعلي وتخزّن نسخة محلية من المستمعين النشطين. بالإضافة إلى ذلك، يمكنك الاحتفاظ بمواقع جغرافية محددة متزامنًا.

Kotlin+KTX

val scoresRef = Firebase.database.getReference("scores")
scoresRef.keepSynced(true)

Java

DatabaseReference scoresRef = FirebaseDatabase.getInstance().getReference("scores");
scoresRef.keepSynced(true);

يعمل عميل قاعدة بيانات Firebase في الوقت الفعلي تلقائيًا على تنزيل البيانات في هذه المواقع ويحافظ على مزامنتها حتى إذا كان المرجع المستمعين النشطين. يمكنك إيقاف المزامنة مرة أخرى باستخدام السطر التالي من التعليمة البرمجية.

Kotlin+KTX

scoresRef.keepSynced(false)

Java

scoresRef.keepSynced(false);

بشكل تلقائي، يتم تخزين 10 ميغابايت من البيانات التي تمت مزامنتها سابقًا مؤقتًا. يجب أن يكون هذا كافيًا لمعظم التطبيقات. إذا تخطت ذاكرة التخزين المؤقت حجمها الذي تم إعداده، فإن قاعدة بيانات Firebase في الوقت الفعلي تزيل نهائيًا البيانات المستخدَمة مؤخرًا. لا تتم الإزالة النهائية للبيانات التي يتم الاحتفاظ بها متزامنة من ذاكرة التخزين المؤقت.

الاستعلام عن البيانات بلا اتصال بالإنترنت

تخزن قاعدة بيانات Firebase في الوقت الفعلي البيانات التي يتم عرضها من استعلام لاستخدامها عندما تكون غير متصل بالإنترنت. بالنسبة للاستعلامات التي تم إنشاؤها أثناء وضع عدم الاتصال، تواصل قاعدة بيانات Firebase في الوقت الفعلي العمل مع البيانات التي تم تحميلها مسبقًا. إذا لم يتم تحميل البيانات المطلوبة، يتم تحميل قاعدة بيانات Firebase في الوقت الفعلي من ذاكرة التخزين المؤقت المحلية. عندما يكون اتصال الشبكة متاحًا مرة أخرى، يتم تحميل البيانات وستعكس الاستعلام.

على سبيل المثال، تطلب هذه التعليمة البرمجية لآخر أربعة عناصر في "قاعدة بيانات Firebase في الوقت الفعلي" للنتائج

Kotlin+KTX

val scoresRef = Firebase.database.getReference("scores")
scoresRef.orderByValue().limitToLast(4).addChildEventListener(object : ChildEventListener {
    override fun onChildAdded(snapshot: DataSnapshot, previousChild: String?) {
        Log.d(TAG, "The ${snapshot.key} dinosaur's score is ${snapshot.value}")
    }

    // ...
})

Java

DatabaseReference scoresRef = FirebaseDatabase.getInstance().getReference("scores");
scoresRef.orderByValue().limitToLast(4).addChildEventListener(new ChildEventListener() {
    @Override
    public void onChildAdded(@NonNull DataSnapshot snapshot, String previousChild) {
        Log.d(TAG, "The " + snapshot.getKey() + " dinosaur's score is " + snapshot.getValue());
    }

    // ...
});

لنفترض أنّ المستخدم يفقد الاتصال بالإنترنت ويتوقّف عن الاتصال بالإنترنت ويعيد تشغيل التطبيق. أثناء عدم اتصال التطبيق بالإنترنت، يطلب التطبيق آخر عنصرين من الموقع نفسه. سيعرض هذا الاستعلام آخر عنصرين بنجاح لأن التطبيق حمّل جميع العناصر الأربعة في الاستعلام أعلاه.

Kotlin+KTX

scoresRef.orderByValue().limitToLast(2).addChildEventListener(object : ChildEventListener {
    override fun onChildAdded(snapshot: DataSnapshot, previousChild: String?) {
        Log.d(TAG, "The ${snapshot.key} dinosaur's score is ${snapshot.value}")
    }

    // ...
})

Java

scoresRef.orderByValue().limitToLast(2).addChildEventListener(new ChildEventListener() {
    @Override
    public void onChildAdded(@NonNull DataSnapshot snapshot, String previousChild) {
        Log.d(TAG, "The " + snapshot.getKey() + " dinosaur's score is " + snapshot.getValue());
    }

    // ...
});

في المثال السابق، يطرح عميل قاعدة بيانات Firebase في الوقت الفعلي "تمت إضافة تابعة" التي تضم أعلى ديناصورين من النقاط، وذلك باستخدام تم الاحتفاظ بذاكرة التخزين المؤقت. لكنها لن تزيد "القيمة" حدث، نظرًا لأن التطبيق يحتوي على لم تنفذ هذا الاستعلام أثناء الاتصال بالإنترنت.

إذا طلب التطبيق العناصر الستة الأخيرة أثناء عدم الاتصال، فسيحصل "تمت إضافة تابعة" الأحداث للعناصر الأربعة المخزنة مؤقتًا على الفور. عندما الإنترنت مرة أخرى، فسيجري برنامج قاعدة بيانات Firebase في الوقت الفعلي مع الخادم ويتم الحصول على آخر عنصرين "تابعين" و 'value' أحداث للتطبيق.

التعامل مع المعاملات بلا إنترنت

يتم وضع أي معاملات يتم إجراؤها عندما يكون التطبيق بلا اتصال بالإنترنت في قائمة الانتظار. بعد أن يستعيد التطبيق الاتصال بالشبكة، يتم إرسال المعاملات إلى خادم قاعدة بيانات الوقت الفعلي.

إدارة التواجد

غالبًا ما يكون من المفيد في تطبيقات الوقت الفعلي اكتشاف الوقت الذي يستغرقه العملاء الاتصال وقطع الاتصال. على سبيل المثال، يمكنك أن تريد وضع علامة "غير متصل" على المستخدم عندما ينقطع اتصال عميله.

يوفر عملاء قاعدة بيانات Firebase أساسيات بسيطة يمكنك استخدامها الكتابة في قاعدة البيانات عندما ينقطع اتصال العميل بقاعدة بيانات Firebase الخوادم. وتحدث هذه التحديثات سواء انفصل العميل عن الخدمة أم لا لذا يمكنك الاعتماد عليها لتنظيف البيانات حتى إذا انقطع الاتصال أو تعطل عميل ما. جميع عمليات الكتابة، بما في ذلك الإعداد وتحديثه وإزالته، عند قطع الاتصال.

فيما يلي مثال بسيط لكتابة البيانات عند الفصل باستخدام المجموعة الأساسية onDisconnect:

Kotlin+KTX

val presenceRef = Firebase.database.getReference("disconnectmessage")
// Write a string when this client loses connection
presenceRef.onDisconnect().setValue("I disconnected!")

Java

DatabaseReference presenceRef = FirebaseDatabase.getInstance().getReference("disconnectmessage");
// Write a string when this client loses connection
presenceRef.onDisconnect().setValue("I disconnected!");

طريقة عمل ميزة on Connect

عند إنشاء عملية onDisconnect()، يتم على خادم قاعدة بيانات Firebase في الوقت الفعلي. يفحص الخادم الأمان وتتأكد من أن المستخدم يمكنه تنفيذ حدث الكتابة المطلوب، ويبلغ تطبيقك إذا كان غير صالح. ثم الخادم ويراقب الاتصال. إذا انتهت مهلة الاتصال في أي وقت أو بواسطة عميل قاعدة البيانات في الوقت الفعلي، فإن الخادم يفحص أمان مرة ثانية (للتأكد من أن العملية لا تزال صالحة) ثم يتم استدعاء الحدث.

يمكن لتطبيقك استخدام الاستدعاء في عملية الكتابة. للتأكّد من توصيل onDisconnect بشكل صحيح:

Kotlin+KTX

presenceRef.onDisconnect().removeValue { error, reference ->
    error?.let {
        Log.d(TAG, "could not establish onDisconnect event: ${error.message}")
    }
}

Java

presenceRef.onDisconnect().removeValue(new DatabaseReference.CompletionListener() {
    @Override
    public void onComplete(DatabaseError error, @NonNull DatabaseReference reference) {
        if (error != null) {
            Log.d(TAG, "could not establish onDisconnect event:" + error.getMessage());
        }
    }
});

يمكن أيضًا إلغاء حدث "onDisconnect" من خلال الاتصال بالرقم .cancel():

Kotlin+KTX

val onDisconnectRef = presenceRef.onDisconnect()
onDisconnectRef.setValue("I disconnected")
// ...
// some time later when we change our minds
// ...
onDisconnectRef.cancel()

Java

OnDisconnect onDisconnectRef = presenceRef.onDisconnect();
onDisconnectRef.setValue("I disconnected");
// ...
// some time later when we change our minds
// ...
onDisconnectRef.cancel();

اكتشاف حالة الاتصال

بالنسبة إلى العديد من الميزات المتعلّقة بالحضور، يكون ذلك مفيدًا لتطبيقك. لمعرفة ما إذا كان متصلاً بالإنترنت أو غير متصل بالإنترنت. قاعدة بيانات Firebase في الوقت الفعلي يوفر موقعًا خاصًا في /.info/connected يتم تعديل كل مرة يتم فيها تعديل حالة اتصال عميل قاعدة بيانات Firebase في الوقت الفعلي التغييرات. وفي ما يلي مثال لذلك:

Kotlin+KTX

val connectedRef = Firebase.database.getReference(".info/connected")
connectedRef.addValueEventListener(object : ValueEventListener {
    override fun onDataChange(snapshot: DataSnapshot) {
        val connected = snapshot.getValue(Boolean::class.java) ?: false
        if (connected) {
            Log.d(TAG, "connected")
        } else {
            Log.d(TAG, "not connected")
        }
    }

    override fun onCancelled(error: DatabaseError) {
        Log.w(TAG, "Listener was cancelled")
    }
})

Java

DatabaseReference connectedRef = FirebaseDatabase.getInstance().getReference(".info/connected");
connectedRef.addValueEventListener(new ValueEventListener() {
    @Override
    public void onDataChange(@NonNull DataSnapshot snapshot) {
        boolean connected = snapshot.getValue(Boolean.class);
        if (connected) {
            Log.d(TAG, "connected");
        } else {
            Log.d(TAG, "not connected");
        }
    }

    @Override
    public void onCancelled(@NonNull DatabaseError error) {
        Log.w(TAG, "Listener was cancelled");
    }
});

/.info/connected هي قيمة منطقية ليست متزامنة بين عملاء قاعدة البيانات في الوقت الفعلي لأن القيمة يعتمد على حالة العميل. بعبارة أخرى، إذا كان أحد العملاء تقرأ /.info/connected على أنها false، هذا لا ضمان قراءة عميل منفصل للخطأ أيضًا.

على نظام التشغيل Android، يدير Firebase تلقائيًا حالة الاتصال من أجل لتقليل معدل نقل البيانات واستخدام البطارية. عندما لا يكون لدى العميل مستمعون نشطون، ما مِن كتابة في انتظار المراجعة أو onDisconnect عملياتها، ولا يتم فصلها بشكل صريح من خلال goOffline، يغلق Firebase الاتصال بعد 60 ثانية من عدم النشاط.

وقت الاستجابة

الطوابع الزمنية للخادم

وتوفر خوادم قاعدة بيانات Firebase في الوقت الفعلي آلية لإدراج الطوابع الزمنية التي تم إنشاؤها على الخادم كبيانات. وهذه الميزة بالإضافة إلى onDisconnect، طريقة سهلة لتدوين الملاحظات بشكل موثوق فيه الوقت الذي تم فيه قطع اتصال عميل قاعدة البيانات في الوقت الفعلي:

Kotlin+KTX

val userLastOnlineRef = Firebase.database.getReference("users/joe/lastOnline")
userLastOnlineRef.onDisconnect().setValue(ServerValue.TIMESTAMP)

Java

DatabaseReference userLastOnlineRef = FirebaseDatabase.getInstance().getReference("users/joe/lastOnline");
userLastOnlineRef.onDisconnect().setValue(ServerValue.TIMESTAMP);

انحراف الساعة

في حين أن firebase.database.ServerValue.TIMESTAMP أكثر بكثير ودقيقة ومفضلة لمعظم عمليات القراءة/الكتابة، قد يكون من المفيد أحيانًا تقدير ساعة العميل فيما يتعلق بخوادم قاعدة بيانات Firebase في الوقت الفعلي. إِنْتَ يمكن إرفاق معاودة الاتصال بالموقع الجغرافي /.info/serverTimeOffset. وذلك من أجل الحصول على القيمة بالمللي ثانية التي يستخدمها عملاء قاعدة بيانات Firebase في الوقت الفعلي تضيفه إلى الوقت المحلي المبلّغ عنه (وقت الحقبة بالمللي ثانية) لتقدير وقت الخادم. لاحظ أن دقة هذه الإزاحة يمكن أن تتأثر وقت استجابة الشبكة، لذا فهي مفيدة في المقام الأول لاكتشاف اختلافات كبيرة (أكبر من ثانية واحدة) في وقت الساعة.

Kotlin+KTX

val offsetRef = Firebase.database.getReference(".info/serverTimeOffset")
offsetRef.addValueEventListener(object : ValueEventListener {
    override fun onDataChange(snapshot: DataSnapshot) {
        val offset = snapshot.getValue(Double::class.java) ?: 0.0
        val estimatedServerTimeMs = System.currentTimeMillis() + offset
    }

    override fun onCancelled(error: DatabaseError) {
        Log.w(TAG, "Listener was cancelled")
    }
})

Java

DatabaseReference offsetRef = FirebaseDatabase.getInstance().getReference(".info/serverTimeOffset");
offsetRef.addValueEventListener(new ValueEventListener() {
    @Override
    public void onDataChange(@NonNull DataSnapshot snapshot) {
        double offset = snapshot.getValue(Double.class);
        double estimatedServerTimeMs = System.currentTimeMillis() + offset;
    }

    @Override
    public void onCancelled(@NonNull DatabaseError error) {
        Log.w(TAG, "Listener was cancelled");
    }
});

نموذج تطبيق التواجد

من خلال الجمع بين عمليات قطع الاتصال ومراقبة حالة الاتصال الطوابع الزمنية للخادم، فيمكنك إنشاء نظام تواجد المستخدم. في هذا النظام، يقوم كل مستخدم بتخزين البيانات في موقع قاعدة البيانات للإشارة إلى ما إذا كان برنامج قاعدة البيانات في الوقت الفعلي متصل بالإنترنت. يضبط العملاء هذا الموقع على "صحيح" عندما عند اتصالهم بالإنترنت وبطابع زمني عند قطع الاتصال. هذا الطابع الزمني تشير إلى آخر مرة كان فيها المستخدم المحدد متصلاً بالإنترنت.

تجدر الإشارة إلى أنّ تطبيقك يجب أن يضع عمليات إلغاء الربط في قائمة انتظار قبل أن يبدأ المستخدم وتمييزها عبر الإنترنت، لتجنب أي شروط سباق في حال انعقاد يتم فقدان اتصال الشبكة قبل إرسال كلا الأمرين إلى الخادم.

إليك نظام بسيط لتواجد المستخدم:

Kotlin+KTX

// Since I can connect from multiple devices, we store each connection instance separately
// any time that connectionsRef's value is null (i.e. has no children) I am offline
val database = Firebase.database
val myConnectionsRef = database.getReference("users/joe/connections")

// Stores the timestamp of my last disconnect (the last time I was seen online)
val lastOnlineRef = database.getReference("/users/joe/lastOnline")

val connectedRef = database.getReference(".info/connected")
connectedRef.addValueEventListener(object : ValueEventListener {
    override fun onDataChange(snapshot: DataSnapshot) {
        val connected = snapshot.getValue<Boolean>() ?: false
        if (connected) {
            val con = myConnectionsRef.push()

            // When this device disconnects, remove it
            con.onDisconnect().removeValue()

            // When I disconnect, update the last time I was seen online
            lastOnlineRef.onDisconnect().setValue(ServerValue.TIMESTAMP)

            // Add this device to my connections list
            // this value could contain info about the device or a timestamp too
            con.setValue(java.lang.Boolean.TRUE)
        }
    }

    override fun onCancelled(error: DatabaseError) {
        Log.w(TAG, "Listener was cancelled at .info/connected")
    }
})

Java

// Since I can connect from multiple devices, we store each connection instance separately
// any time that connectionsRef's value is null (i.e. has no children) I am offline
final FirebaseDatabase database = FirebaseDatabase.getInstance();
final DatabaseReference myConnectionsRef = database.getReference("users/joe/connections");

// Stores the timestamp of my last disconnect (the last time I was seen online)
final DatabaseReference lastOnlineRef = database.getReference("/users/joe/lastOnline");

final DatabaseReference connectedRef = database.getReference(".info/connected");
connectedRef.addValueEventListener(new ValueEventListener() {
    @Override
    public void onDataChange(@NonNull DataSnapshot snapshot) {
        boolean connected = snapshot.getValue(Boolean.class);
        if (connected) {
            DatabaseReference con = myConnectionsRef.push();

            // When this device disconnects, remove it
            con.onDisconnect().removeValue();

            // When I disconnect, update the last time I was seen online
            lastOnlineRef.onDisconnect().setValue(ServerValue.TIMESTAMP);

            // Add this device to my connections list
            // this value could contain info about the device or a timestamp too
            con.setValue(Boolean.TRUE);
        }
    }

    @Override
    public void onCancelled(@NonNull DatabaseError error) {
        Log.w(TAG, "Listener was cancelled at .info/connected");
    }
});