كيفية استخدام Realm لنظام Android مثل البطل ، وكيفية معرفة ما إذا كنت تفعل ذلك بشكل خاطئ

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

الإصدار الأحدث في هذا الوقت هو الإصدار v5.9.0 ، يمكنك مشاهدة الإصدار الأحدث على موقعه على الويب. ربما يجب عليك التحديث إذا كنت متخلفًا.

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

ما هو عالم؟

عالم هو نظام قاعدة البيانات. إنه نوع من أنواع SQLite ، باستثناء أنه لا علاقة له بـ SQLite على الإطلاق. تقوم بتعريف الفئات ، وتعرف هذه الفئات المخطط الخاص بك ، ويقوم Realm بتخزين مثيلات هذه الفئة ككائنات. إنها ليست قاعدة بيانات SQL ، إنها قاعدة بيانات NoSQL. إنه سهل الاستخدام إلى حد ما ، ويحتوي على بعض الميزات الرائعة أيضًا - لكنني سوف أتناول بالتفصيل هذه المقالة.

على أي حال ، العودة إلى المسار الصحيح:

أخطاء نموذجية يبدو أن الناس يرتكبونها طوال الوقت

  • باستخدام realm.beginTransaction () و realm.commitTransaction () بدلاً من realm.executeTransaction (Realm.Transaction)

المشكلة في هذا هي أن executeTransaction () يتعامل تلقائيًا مع استدعاء realm.cancelTransaction () في حالة طرح استثناء ، في حين أن البديل الآخر يهمل عادة اختبار التجريب.

نعم ، من المفترض أن تدعو إلى إلغاء المعاملات التي لن تنتهي في نهاية المطاف.

على سبيل المثال ، في مؤشرات ترابط الخلفية:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  • فتح مثيلات Realm باستخدام Realm.getDefaultInstance () لا تغلقه في أي مكان

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

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

إليك قاعدة: إذا رأيت رمزًا مثل Realm.getDefaultInstance (). حيث (...) سيتوقف ذلك عاجلاً أم آجلاً.

(أيضًا ، نصيحة: يجب عليك فتح المثيل الأول الخاص بك على مؤشر ترابط واجهة المستخدم فقط بعد إنشاء النشاط الأول ، فقط لتكون آمنًا من context.getFilesDir () == لاغٍ).

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  • تنفيذ العديد من المعاملات (على سبيل المثال ، تتم إضافة كل عنصر في معاملة جديدة ، في حلقة -) على مؤشرات ترابط الخلفية غير التلقائية

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

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  • إساءة استخدام realmResults.asObservable () وسوء الفهم لدعم Realm's Rx بشكل عام

معظم الأسئلة المتعلقة بـ Rx / Realm على Stack Overflow تميل إلى أن تكون خفيًا للغاية ، لأن هناك سوء فهم أساسي حول ما يفترض أن يدعمه Realm Rx.

الهدف من realmResults.asObservable () هو رؤية النتائج بأكملها على أنها ملحوظة دون الحاجة إلى إضافة مستمعين للتغيير يدويًا وتحديثها (وتلقي الإشعارات في حالة تغيير الجدول) ، في سلسلة رسائل واجهة المستخدم.

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

في حال كنت تتساءل ، من المفترض أن تفتح وتغلق مثيلًا في مجال خيوط الخلفية حتى لو كنت تستخدمه مع Rx.

إذا كنت تحتاج حقًا إلى RealmResults في خيط المناقشة كملاحظة (لم يحدث لي قط من قبل) ، فاستخدم Observable.just (realm.where (…) .find * ()) (وليس async) بدلاً من asObservable ().

(ملاحظة: قبل Realm 2.0.3+ ، يجب أن تفكر في استخدام RealmObservableFactory الخاص بك ، لأنه تم العثور على تسرب للذاكرة.)

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  • محاولة ترقيم صفحات RealmResults أو "تحديد عدد النتائج فيه" دون سبب وجيه على الإطلاق

لقد رأيت أشخاصًا في SO يحاولون ترقيم صفحات RealmResults ، أو "كيف أقوم بإجراء استعلام حد". السؤال الأول المطروح هو لماذا تحاول القيام بذلك.

في الغالب لأنه للحد من RealmResults ، عليك ببساطة عدم فهرسته أعلى من الحد التعسفي الخاص بك.

أوضح esseTo: RealmResults لا يحتوي على أي عناصر. أنه يحتوي على وسائل لتقييم نتائج الاستعلام. يتم الحصول على العنصر من المجال فقط عند استدعاء realmResults.get (i) ، ويتم إرجاع هذا العنصر المفرد فقط في كل مرة. يشبه المؤشر ، إلا أنه عبارة عن قائمة. لذلك ، "الحد منها" و "ترقيم الصفحات" هذا غير منطقي. إذا كنت حقًا بحاجة إليها ، فقم بتحديد الفهرس الخاص بك.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

إذا رأيت findAll (). sort () ، فيجب أن يكون sort (). findAll () عادةً. قبل Realm-Java 5.0.0 ، اعتاد أن يكون findAllSorted () ، لذلك ابحث عن ذلك.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  • عدم استخدام التعليقات التوضيحيةIndex في الحقول التي تستخدمها في الاستعلام الخاص بك ، على الرغم من ذلك

يجعل استفساراتك مثل 4x أسرع إن لم يكن أكثر. يجب عليك دائمًا استخدامIndex إذا كنت تستخدم حقلًا في استعلام.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  • فتح مثيل Realm على خيط خلفية ، والحصول على RealmResults ، ثم فتح المعاملة ، ثم التعامل مع النتائج على السطح الخارجي للمعاملة

عند فتح معاملة ، تكتب مباشرةً إلى أحدث إصدار من Realm. مما يعني أنه يجب الحصول على RealmResults داخل الصفقة وليس خارج المعاملة ؛ بحيث ترى أحدث إصدار في جميع الأوقات.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  • عدم استخدام RealmRecyclerViewAdapter و RecyclerView على الرغم من أنه قد توقف لفترة من الوقت الآن

تجدر الإشارة أيضًا إلى أن المهايئ (الإصدار 3.0.0) يدير إضافة وإزالة مستمع تغيير يستدعي المحول الصحيح للمحول. notifyItem * الأساليب التي تسمح بالحركة عند التغيير ، لذلك حرفيًا ، كل ما عليك فعله هو توفير الاستعلام الذي تريد رؤيته عناصر على الشاشة وأنه يعمل فقط والتحديثات التلقائية عندما تحدث الأشياء.

ListViews و AsyncTasks تجعلني أشعر بالألم. : /

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

كان هذا في الغالب يتعلق بأخطاء شائعة ، لكن ما الذي يدور حوله على أي حال؟

عالم يحل بعض المشاكل التي قد تتطلب الكثير من السباكة للتعامل مع نفسك ، وهذا ما يجعلها جميلة في المقام الأول.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  • تقييم الاستعلام نسخة الصفر

وهذا ما قلته سابقًا عن RealmResults لا يحتوي على أي عناصر. يمكنك فقط الحصول على فئات الوكيل التي تم إنشاؤها عند استدعاء .get (i). القائمة التي تحصل عليها هي مجرد مؤشر يتوهم إلى قاعدة البيانات الأساسية. يعد تقييم RealmResults بحد ذاته رخيصًا إلى حد ما ، ويمنحك وصولاً سهلاً إلى جميع البيانات المؤهلة لظروفك ، دون نسخ مجموعة البيانات بالكامل إلى الذاكرة.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  • السماح بالكتابة من مؤشرات ترابط متعددة (معاملة واحدة فقط في كل مرة) والتحديثات التلقائية على مؤشر ترابط واجهة المستخدم لإظهار أحدث إصدار من البيانات

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

يمكنك إرسال العناصر التي تم إدراجها حديثًا من خلال ناقل الأحداث ، وإلحاقها بقائمتك ، ثم الاتصال بـ adapter.notifyItemRangeInserted (...) ، لكن لن يكون ذلك رائعًا إذا كتبت البيانات في مكان واحد فقط ، كانت البيانات محدّثة في أي مكان آخر دون استخدام السباكة اليدوية على الإطلاق؟

نعم ، إذا كنت تستخدم RealmResults التي تم إرجاعها من خلال الاستعلام الخاص بك ورميه في RealmRecyclerViewAdapter (والتي لا تفعل الكثير من السحر في الخلفية بالمناسبة) ، فإن أي كتابة تقوم بها على أي موضوع ستقوم تلقائيًا بتحديث مجموعة النتائج الخاصة بك ، وستظهر دائمًا آخر البيانات. رائع. صحيح؟

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  • الاستماع لنتائج الاستعلام التي تم الحصول عليها بشكل غير متزامن دون بذل جهد كبير

إذا كنت لا تستخدم RealmRecyclerViewAdapter ، فلا يزال بإمكانك الاستماع إلى أي تغييرات تم إجراؤها من أي سلاسل رسائل باستخدام RealmChangeListener المضافة إلى RealmResults الخاص بك.

عالم خاص
RealmResults  القطط الخاصة ؛
RealmChangeListener <...> realmChangeListener = cats -> {
    adapter.setData (القطط)؛
}؛
@تجاوز
محمي void onCreate (تم حفظ الحزمة في InstanceState) {
    super.onCreate (savedInstanceState)؛
    realm = Realm.getDefaultInstance ()؛
    القطط = realm.where (Cat.class) .findAllAsync ()؛
    cats.addChangeListener (realmChangeListener)؛
}
@تجاوز
باطل محمي onDestroy () {
    super.onDestroy ()؛
    cats.removeAllChangeListeners ()؛
    realm.close ()؛
}

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

مع وضع ذلك في الاعتبار ، كيف يجب استخدام مملكة؟

  • لا تستخدم startTransaction () و الالتزامTransaction () يدويًا. استخدم executeTransaction () (أو executeTransactionAsync () على مؤشر ترابط واجهة المستخدم ، إذا لزم الأمر).
  • استخدم RealmRecyclerViewAdapter مع طريقة findAllSortedAsync () لإلغاء تحميل الاستعلام تمامًا من مؤشر ترابط واجهة المستخدم ، وعرض أي عناصر جديدة ملتزمة من مؤشرات ترابط الخلفية تلقائيًا (يعالج محول Realm هذا لك من خلال RealmChangeListener)
  • لا يوجد نسخ يدوي ولا يتطلب التزامن على الإطلاق ، يمكنك فقط تحديد الاستعلام وكيفية ربط حامل العرض الخاص بك بكائن Realm ، وستحصل على حل استخدام ذاكرة منخفضة مع المزامنة التلقائية.

إذا كنت لا تزال لا تتخلى عن نسخ البيانات الموجودة في الذاكرة ، فيمكنك محاولة Monarchy.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

نأمل أن يكون ساعدك:

  • احصل على القليل من التفهم حول ماهية عالم ما ، وما هو مجاله ، وكيفية التعامل معه
  • كيفية التعرف على المنشورات السيئة مثل هذه المشاركة (إدارة مثيل Realm معقدة ، معاملات متزامنة على مؤشر ترابط واجهة المستخدم ، استخدام start / الالتزام بدلاً من التنفيذ ، مثيلات Realm التي لم يتم إغلاقها ، لإخبارك بإضافة بيانات أولية باستخدام الترحيل بدلاً من initialData (Realm .التعامل) ، باستخدام الأساليب التي لم تعد موجودة منذ 0.89.0 ... أشياء من هذا القبيل).

قمت بإعادة صياغة مثال الكتاب الموضح في هذا المنشور أعلاه في مستودع جيثب هذا.

للحصول على مثال بسيط يقوم بتنزيل البيانات الخارجية ، يرجى الرجوع إلى مستودع جيثب.

(بالمناسبة ، مع ميزة التصفيق الجديدة لـ Medium ، إذا أعجبك المقال ، فيمكنك بالفعل الضغط على زر "التصفيق" عدة مرات للتعبير عن مدى إعجابك به!)