ڪهاڻيءَ جو قصو ڪيئن ريلم ۾ ڪاسڪ ڊيليشن هڪ ڊگھي لانچ تي کٽيو

سڀ استعمال ڪندڙ موبائل ايپليڪيشنن ۾ تيز لانچ ۽ جوابي UI وٺن ٿا. جيڪڏهن ايپليڪيشن کي لانچ ڪرڻ ۾ گهڻو وقت لڳندو آهي، صارف اداس ۽ ناراض محسوس ڪرڻ شروع ڪري ٿو. توھان آساني سان ڪسٽمر جي تجربي کي خراب ڪري سگھو ٿا يا صارف کي مڪمل طور تي وڃائي سگھو ٿا ان کان اڳ جو ھو ايپليڪيشن استعمال ڪرڻ شروع ڪري.

اسان هڪ دفعو دريافت ڪيو ته ڊوڊو پيزا ايپ لانچ ٿيڻ ۾ سراسري طور 3 سيڪنڊن جو وقت وٺندو آهي، ۽ ڪجهه ”خوش قسمت ماڻهن“ لاءِ اهو 15-20 سيڪنڊن ۾ وٺندو آهي.

ڪٽ جي هيٺان هڪ ڪهاڻي آهي خوشيءَ جي پڄاڻيءَ سان: ريئلم ڊيٽابيس جي واڌ بابت، هڪ ميموري لڪ، ڪيئن اسان نيسٽ ٿيل شيون گڏ ڪيون، ۽ پوءِ پاڻ کي گڏ ڪيو ۽ هر شيءِ کي درست ڪيو.

ڪهاڻيءَ جو قصو ڪيئن ريلم ۾ ڪاسڪ ڊيليشن هڪ ڊگھي لانچ تي کٽيو

ڪهاڻيءَ جو قصو ڪيئن ريلم ۾ ڪاسڪ ڊيليشن هڪ ڊگھي لانچ تي کٽيو
مضمون نويس: ميڪسم ڪچينڪن - ڊڊو پيزا تي Android ڊولپر.

پهرين سرگرمي جي onResume() تائين ايپليڪيشن آئڪن تي ڪلڪ ڪرڻ کان ٽي سيڪنڊ لامحدود آهي. ۽ ڪجھ استعمال ڪندڙن لاء، شروعاتي وقت 15-20 سيڪنڊن تائين پھچي. اهو به ڪيئن ممڪن آهي؟

جن وٽ پڙهڻ جو وقت نه آهي انهن لاءِ هڪ تمام مختصر تت
اسان جو ريليم ڊيٽابيس بيشمار وڌيو. ڪجھ اندريون شيون ڊهي نه ويون، پر مسلسل گڏ ڪيون ويون. ايپليڪيشن جي شروعات جو وقت آهستي آهستي وڌايو ويو. پوء اسان ان کي مقرر ڪيو، ۽ شروعاتي وقت ٽارگيٽ تي آيو - اهو 1 سيڪنڊ کان گهٽ ٿي ويو ۽ وڌيڪ نه وڌايو. مضمون ۾ صورتحال جو تجزيو ۽ ٻه حل آهن - هڪ تڪڙو ۽ هڪ عام.

مسئلي جي ڳولا ۽ تجزيو

اڄ، ڪنهن به موبائل ايپليڪيشن کي جلدي لانچ ڪرڻ ۽ جواب ڏيڻ گهرجي. پر اهو صرف موبائل ايپ بابت ناهي. هڪ خدمت ۽ ڪمپني سان رابطي جو استعمال ڪندڙ تجربو هڪ پيچيده شيء آهي. مثال طور، اسان جي صورت ۾، ترسيل جي رفتار پيزا خدمت لاءِ اهم اشارن مان هڪ آهي. جيڪڏهن ترسيل تيز آهي، پيزا گرم ٿي ويندو، ۽ گراهڪ جيڪو هاڻي کائڻ چاهي ٿو، گهڻو انتظار نه ڪرڻو پوندو. ايپليڪيشن لاء، موڙ ۾، اهو ضروري آهي ته تيز خدمت جو احساس پيدا ڪرڻ، ڇو ته جيڪڏهن ايپليڪيشن کي لانچ ٿيڻ ۾ صرف 20 سيڪنڊن جو وقت لڳندو آهي، پوء توهان کي پيزا لاء ڪيترو وقت انتظار ڪرڻو پوندو؟

شروعات ۾، اسان پاڻ کي ان حقيقت سان منهن ڏيڻو پيو ته ڪڏهن ڪڏهن ايپليڪيشن کي لانچ ٿيڻ ۾ ڪجهه سيڪنڊن جو وقت لڳندو هو، ۽ پوء اسان ٻين ساٿين کان شڪايتون ٻڌڻ شروع ڪيو ته اهو ڪيترو وقت وٺندو آهي. پر اسان ان صورتحال کي مسلسل ورجائي نه سگهياسين.

اهو ڪيترو ڊگهو آهي؟ جي مطابق گوگل دستاويز، جيڪڏهن ايپليڪيشن جي ٿڌي شروعات 5 سيڪنڊن کان گهٽ لڳندي آهي، ته پوءِ اهو سمجهيو ويندو ”جيئن عام“. Dodo Pizza Android ايپ شروع ڪئي (فائر بيس ميٽرڪس جي مطابق _app_start) تي ٿڌي شروعات سراسري طور تي 3 سيڪنڊن ۾ - "نه عظيم، نه خوفناڪ،" جيئن اهي چون ٿا.

پر پوءِ شڪايتون ظاهر ٿيڻ لڳيون ته ايپليڪيشن شروع ٿيڻ ۾ تمام گهڻو، تمام گهڻو وقت ورتو! شروع ڪرڻ سان، اسان اندازو ڪرڻ جو فيصلو ڪيو ته "تمام گهڻو، تمام ڊگهو" ڇا آهي. ۽ اسان ان لاءِ فائر بيس ٽريس استعمال ڪيو ايپ جي شروعات جو نشان.

ڪهاڻيءَ جو قصو ڪيئن ريلم ۾ ڪاسڪ ڊيليشن هڪ ڊگھي لانچ تي کٽيو

هي معياري سراغ ان وقت جي وچ ۾ وقت کي ماپ ڪري ٿو جڏهن صارف ايپليڪيشن کي کوليندو آهي ۽ ان لمحي جو پهريون سرگرمي جي onResume() تي عمل ڪيو ويندو آهي. فائر بيس ڪنسول ۾ هي ميٽرڪ سڏيو ويندو آهي _app_start. اهو معلوم ٿيو ته:

  • 95 سيڪڙو کان مٿي استعمال ڪندڙن لاءِ شروعاتي وقت لڳ ڀڳ 20 سيڪنڊ آھن (ڪجھ اڃا وڌيڪ)، وچين ٿڌي شروعاتي وقت 5 سيڪنڊن کان گھٽ ھجڻ جي باوجود.
  • شروعاتي وقت هڪ مستقل قدر نه آهي، پر وقت سان وڌي ٿو. پر ڪڏهن ڪڏهن اتي ڦڙا آهن. اسان اهو نمونو مليو جڏهن اسان تجزيو جي پيماني تي 90 ڏينهن تائين وڌايو.

ڪهاڻيءَ جو قصو ڪيئن ريلم ۾ ڪاسڪ ڊيليشن هڪ ڊگھي لانچ تي کٽيو

ذهن ۾ ٻه خيال آيا:

  1. ڪجهه لڪي رهيو آهي.
  2. هي "ڪجهه" ڇڏڻ کان پوء ري سيٽ ڪيو ويو آهي ۽ پوء ٻيهر ليکي ٿو.

"شايد ڊيٽابيس سان گڏ ڪجهه،" اسان سوچيو، ۽ اسان صحيح هئاسين. پهرين، اسان ڊيٽابيس کي ڪيش طور استعمال ڪندا آهيون؛ لڏپلاڻ دوران اسان ان کي صاف ڪندا آهيون. ٻيو، ڊيٽابيس کي لوڊ ڪيو ويندو آهي جڏهن ايپليڪيشن شروع ٿئي ٿي. هر شيءِ سان ٺهڪي اچي ٿي.

Realm ڊيٽابيس سان ڇا غلط آهي

اسان چيڪ ڪرڻ شروع ڪيو ته ڪيئن ڊيٽابيس جو مواد ايپليڪيشن جي زندگي تي، پهرين انسٽاليشن کان وٺي ۽ وڌيڪ فعال استعمال دوران. توھان ڏسي سگھو ٿا مواد ريئلم ڊيٽابيس جي ذريعي اسٽيٿو يا وڌيڪ تفصيل سان ۽ واضح طور تي فائل کولڻ ذريعي ريم اسٽوڊيو. ADB ذريعي ڊيٽابيس جي مواد کي ڏسڻ لاء، نقل ڪريو Realm ڊيٽابيس فائل:

adb exec-out run-as ${PACKAGE_NAME} cat files/${DB_NAME}

مختلف وقتن تي ڊيٽابيس جي مواد کي ڏسڻ سان، اسان کي معلوم ٿيو ته هڪ خاص قسم جي شين جو تعداد مسلسل وڌي رهيو آهي.

ڪهاڻيءَ جو قصو ڪيئن ريلم ۾ ڪاسڪ ڊيليشن هڪ ڊگھي لانچ تي کٽيو
تصوير ٻن فائلن لاءِ ريلم اسٽوڊيو جو هڪ ٽڪرو ڏيکاري ٿي: کاٻي پاسي - ايپليڪيشن جو بنياد ڪجهه وقت انسٽاليشن کان پوءِ، ساڄي پاسي - فعال استعمال کان پوءِ. اهو ڏسي سگهجي ٿو ته شين جو تعداد ImageEntity и MoneyType خاص طور تي وڌي چڪو آهي (اسڪرين شاٽ هر قسم جي شين جو تعداد ڏيکاري ٿو).

ڊيٽابيس جي ترقي ۽ شروعاتي وقت جي وچ ۾ تعلق

بي ترتيب ڊيٽابيس جي ترقي تمام خراب آهي. پر اهو ڪيئن اثر انداز ٿئي ٿو ايپليڪيشن جي شروعاتي وقت تي؟ ActivityManager ذريعي هن کي ماپڻ بلڪل آسان آهي. Android 4.4 کان وٺي، logcat ڏيکاري ٿو لاگ کي ڏيکاريل اسٽرنگ ۽ وقت سان. هي وقت ان وقت جي وقفي جي برابر آهي جنهن لمحي کان وٺي ايپليڪيشن شروع ڪئي وئي آهي سرگرمي رينڊنگ جي آخر تائين. ان دوران هيٺيان واقعا پيش اچن ٿا:

  • عمل شروع ڪريو.
  • شين جي شروعات.
  • تخليق ۽ سرگرمين جي شروعات.
  • ٺاهه ٺاھڻ.
  • ايپليڪيشن رينجرنگ.

اسان کي مناسب. جيڪڏھن توھان ADB کي -S ۽ -W جھنڊن سان هلائيندا آھيو، توھان حاصل ڪري سگھو ٿا وڌايل ٻاھر نڪرڻ واري شروعاتي وقت سان:

adb shell am start -S -W ru.dodopizza.app/.MainActivity -c android.intent.category.LAUNCHER -a android.intent.action.MAIN

جيڪڏهن توهان ان کي اتان کان پڪڙيو grep -i WaitTime وقت، توهان هن ميٽرڪ جي مجموعي کي خودڪار ڪري سگهو ٿا ۽ نتيجن کي بصري طور تي ڏسي سگهو ٿا. هيٺ ڏنل گراف ڏيکاري ٿو ايپليڪيشن جي شروعاتي وقت جي انحصار کي ايپليڪيشن جي سرد ​​شروعات جي تعداد تي.

ڪهاڻيءَ جو قصو ڪيئن ريلم ۾ ڪاسڪ ڊيليشن هڪ ڊگھي لانچ تي کٽيو

ساڳئي وقت، ڊيٽابيس جي سائيز ۽ واڌ جي وچ ۾ لاڳاپو جي ساڳي نوعيت هئي، جيڪا 4 MB کان 15 MB تائين وڌي وئي. مجموعي طور تي، اهو ظاهر ٿئي ٿو ته وقت سان گڏ (ٿڌ جي شروعات جي واڌ سان)، ٻئي ايپليڪيشن لانچ جو وقت ۽ ڊيٽابيس جي سائيز ۾ اضافو ٿيو. اسان جي هٿن تي هڪ مفروضو آهي. هاڻي باقي رهيل انحصار جي تصديق ڪرڻ لاءِ. تنهن ڪري، اسان "ليڪس" کي هٽائڻ جو فيصلو ڪيو ۽ ڏسو ته ڇا اهو لانچ کي تيز ڪندو.

لاتعداد ڊيٽابيس جي ترقي جا سبب

"ليڪس" کي هٽائڻ کان اڳ، اهو سمجهڻ جي قابل آهي ته اهي پهرين جڳهه ۾ ڇو ظاهر ٿيا. هن کي ڪرڻ لاء، اچو ته ياد رکون ته ريمم ڇا آهي.

Realm هڪ غير لاڳاپو ڊيٽابيس آهي. اهو توهان کي شين جي وچ ۾ لاڳاپن کي بيان ڪرڻ جي اجازت ڏئي ٿو ساڳئي طريقي سان Android تي ڪيترا ORM تعلقي ڊيٽابيس بيان ڪيا ويا آهن. ساڳي ئي وقت، ريئلم شيون سڌو سنئون ياداشت ۾ ذخيرو ڪري ٿو گھٽ ۾ گھٽ تبديلين ۽ نقشن سان. هي توهان کي ڊسڪ مان ڊيٽا کي تمام جلدي پڙهڻ جي اجازت ڏئي ٿو، جيڪا حقيقي طاقت آهي ۽ ڇو ان کي پيار ڪيو ويندو آهي.

(هن مضمون جي مقصدن لاءِ، هي بيان اسان لاءِ ڪافي هوندو. توهان وڌيڪ پڙهي سگهو ٿا ريئلم بابت ٿڌي ۾ دستاويز يا ان ۾ اڪيڊمي).

ڪيترائي ڊولپرز رشتيدار ڊيٽابيس سان وڌيڪ ڪم ڪرڻ جا عادي آهن (مثال طور، هود جي هيٺان SQL سان ORM ڊيٽابيس). ۽ شيون cascading ڊيٽا ختم ڪرڻ وانگر اڪثر هڪ ڏنو وانگر لڳي. پر ريموٽ ۾ نه.

رستي جي ذريعي، cascade ختم ڪرڻ جي خاصيت هڪ ڊگهي وقت لاء پڇيو ويو آهي. هي نظرثاني и .يوان سان لاڳاپيل، فعال طور تي بحث ڪيو ويو. اتي هڪ احساس هو ته اهو جلدي ٿي ويندو. پر پوءِ اهو سڀ ڪجهه مضبوط ۽ ڪمزور ڳنڍين جي تعارف ۾ ترجمو ڪيو ويو، جيڪو پڻ خود بخود اهو مسئلو حل ڪندو. هن ڪم تي ڪافي متحرڪ ۽ سرگرم هو ڇڪڻ جي درخواستجنهن کي اندروني مشڪلاتن سبب في الحال روڪيو ويو آهي.

ڊيٽا ليڪ بغير ڪاسڪيڊنگ ختم ڪرڻ

جيڪڏهن توهان غير موجود ڪاسڪيڊنگ ڊيليٽ تي ڀروسو ڪندا آهيو ته ڊيٽا ڪيئن لڪي ٿي؟ جيڪڏهن توهان وٽ آهي ريئلم شيون، پوءِ انهن کي ختم ڪيو وڃي.
اچو ته هڪ (تقريبا) حقيقي مثال ڏسو. اسان وٽ هڪ اعتراض آهي CartItemEntity:

@RealmClass
class CartItemEntity(
 @PrimaryKey
 override var id: String? = null,
 ...
 var name: String = "",
 var description: String = "",
 var image: ImageEntity? = null,
 var category: String = MENU_CATEGORY_UNKNOWN_ID,
 var customizationEntity: CustomizationEntity? = null,
 var cartComboProducts: RealmList<CartProductEntity> = RealmList(),
 ...
) : RealmObject()

ڪارٽ ۾ پراڊڪٽ مختلف شعبن تي مشتمل آهي، بشمول هڪ تصوير ImageEntity، حسب ضرورت اجزاء CustomizationEntity. انهي سان گڏ، ڪارٽ ۾ پيداوار هڪ ڪمبو ٿي سگهي ٿو پنهنجي مصنوعات جي پنهنجي سيٽ سان RealmList (CartProductEntity). سڀ درج ٿيل فيلڊز آهن Realm شيون. جيڪڏهن اسان هڪ نئون اعتراض داخل ڪيو (copyToRealm() / copyToRealmOrUpdate()) ساڳئي id سان، پوء اهو اعتراض مڪمل طور تي ختم ٿي ويندو. پر سڀ اندروني شيون (تصوير، ڪسٽمائيزيشن اينٽيٽي ۽ cartComboProducts) والدين سان لاڳاپا وڃائي ويهندا ۽ ڊيٽابيس ۾ رهندا.

جيئن ته انهن سان لاڳاپو گم ٿي ويو آهي، اسان انهن کي وڌيڪ نه پڙهندا آهيون يا انهن کي حذف ڪندا آهيون (جيستائين اسان انهن کي واضح طور تي رسائي يا پوري "ٽيبل" کي صاف نه ڪيو). اسان ان کي سڏيو "ميموري ليک".

جڏهن اسان ريليم سان ڪم ڪريون ٿا، اسان کي واضح طور تي سڀني عناصر جي ذريعي وڃڻ گهرجي ۽ واضح طور تي هر شيء کي ختم ڪرڻ کان اڳ اهڙي عملن کان اڳ. اهو ٿي سگهي ٿو، مثال طور، هن طرح:

val entity = realm.where(CartItemEntity::class.java).equalTo("id", id).findFirst()
if (first != null) {
 deleteFromRealm(first.image)
 deleteFromRealm(first.customizationEntity)
 for(cartProductEntity in first.cartComboProducts) {
   deleteFromRealm(cartProductEntity)
 }
 first.deleteFromRealm()
}
// и потом уже сохраняем

جيڪڏھن توھان ھي ڪريو، پوء سڀ ڪجھ ڪم ڪندو جيئن اھو ھجڻ گھرجي. هن مثال ۾، اسان فرض ڪريون ٿا ته تصوير، ڪسٽمائيزيشن اينٽيٽي، ۽ ڪارٽ ڪمبو پروڊڪٽس اندر ٻيون nested Realm شيون نه آهن، تنهنڪري اتي ٻيون nested loops ۽ حذف نه آهن.

"جلدي" حل

پهرين شيء جيڪا اسان ڪرڻ جو فيصلو ڪيو ته تيز ترين وڌندڙ شين کي صاف ڪيو ۽ نتيجن کي ڏسو ته ڏسو ته اهو اسان جو اصل مسئلو حل ڪندو. پهريون، آسان ۽ سڀ کان وڌيڪ وجداني حل ڪيو ويو، يعني: هر شئي پنهنجي ٻارن کي هٽائڻ لاء ذميوار هجڻ گهرجي. هن کي ڪرڻ لاء، اسان هڪ انٽرفيس متعارف ڪرايو جنهن کي واپس ڪيو ويو ان جي nested Realm شين جي هڪ فهرست:

interface NestedEntityAware {
 fun getNestedEntities(): Collection<RealmObject?>
}

۽ اسان ان کي اسان جي دائري جي شين ۾ لاڳو ڪيو:

@RealmClass
class DataPizzeriaEntity(
 @PrimaryKey
 var id: String? = null,
 var name: String? = null,
 var coordinates: CoordinatesEntity? = null,
 var deliverySchedule: ScheduleEntity? = null,
 var restaurantSchedule: ScheduleEntity? = null,
 ...
) : RealmObject(), NestedEntityAware {

 override fun getNestedEntities(): Collection<RealmObject?> {
   return listOf(
       coordinates,
       deliverySchedule,
       restaurantSchedule
   )
 }
}

В getNestedEntities اسان سڀني ٻارن کي فليٽ لسٽ طور واپس ڪريون ٿا. ۽ هر چائلڊ آبجیکٹ NestedEntityAware انٽرفيس کي به لاڳو ڪري سگھي ٿو، جنهن مان ظاهر ٿئي ٿو ته ان وٽ اندروني ريئلم شيون آهن حذف ڪرڻ لاءِ، مثال طور ScheduleEntity:

@RealmClass
class ScheduleEntity(
 var monday: DayOfWeekEntity? = null,
 var tuesday: DayOfWeekEntity? = null,
 var wednesday: DayOfWeekEntity? = null,
 var thursday: DayOfWeekEntity? = null,
 var friday: DayOfWeekEntity? = null,
 var saturday: DayOfWeekEntity? = null,
 var sunday: DayOfWeekEntity? = null
) : RealmObject(), NestedEntityAware {

 override fun getNestedEntities(): Collection<RealmObject?> {
   return listOf(
       monday, tuesday, wednesday, thursday, friday, saturday, sunday
   )
 }
}

۽ ائين ئي، شين جي nesting کي بار بار ڪري سگهجي ٿو.

ان کان پوء اسان هڪ طريقو لکندا آهيون جيڪو بار بار ختم ڪري ٿو سڀني اندر ٿيل شيون. طريقه (هڪ واڌ جي طور تي ٺهيل) deleteAllNestedEntities تمام اعلي سطحي شيون ۽ طريقو حاصل ڪري ٿو deleteNestedRecursively بار بار NestedEntityAware انٽرفيس استعمال ڪندي سڀني nested شين کي هٽائي ٿو:

fun <T> Realm.deleteAllNestedEntities(entities: Collection<T>,
 entityClass: Class<out RealmObject>,
 idMapper: (T) -> String,
 idFieldName : String = "id"
 ) {

 val existedObjects = where(entityClass)
     .`in`(idFieldName, entities.map(idMapper).toTypedArray())
     .findAll()

 deleteNestedRecursively(existedObjects)
}

private fun Realm.deleteNestedRecursively(entities: Collection<RealmObject?>) {
 for(entity in entities) {
   entity?.let { realmObject ->
     if (realmObject is NestedEntityAware) {
       deleteNestedRecursively((realmObject as NestedEntityAware).getNestedEntities())
     }
     realmObject.deleteFromRealm()
   }
 }
}

اسان هن کي تيز ترين وڌندڙ شين سان ڪيو ۽ چيڪ ڪيو ته ڇا ٿيو.

ڪهاڻيءَ جو قصو ڪيئن ريلم ۾ ڪاسڪ ڊيليشن هڪ ڊگھي لانچ تي کٽيو

نتيجي طور، اهي شيون جيڪي اسان هن حل سان ڍڪيندا هئا، وڌڻ بند ٿي ويا. ۽ بنيادي جي مجموعي ترقي سست، پر بند نه ڪيو.

"عام" حل

جيتوڻيڪ بنيادي طور تي وڌيڪ سست وڌڻ شروع ڪيو، اهو اڃا تائين وڌايو ويو. تنهنڪري اسان اڳتي وڌڻ شروع ڪيو. اسان جو پروجيڪٽ Realm ۾ ڊيٽا ڪيشنگ جو تمام گهڻو استعمال ڪري ٿو. تنهن ڪري، هر شئي لاءِ سڀني nested شيون لکڻ ۾ محنت آهي، ان سان گڏ غلطين جو خطرو به وڌي ٿو، ڇاڪاڻ ته ڪوڊ تبديل ڪرڻ وقت توهان شيون بيان ڪرڻ وساري سگهو ٿا.

مان پڪ ڪرڻ چاهيان ٿو ته مون انٽرفيس استعمال نه ڪيو، پر اهو سڀ ڪجهه پنهنجو پاڻ تي ڪم ڪيو.

جڏهن اسان چاهيون ٿا ته ڪا شيءِ پنهنجو پاڻ تي ڪم ڪري، اسان کي عڪس استعمال ڪرڻو پوندو. هن کي ڪرڻ لاء، اسان هر طبقي جي فيلڊ ذريعي وڃون ٿا ۽ چيڪ ڪري سگهون ٿا ته ڇا اهو هڪ حقيقي اعتراض آهي يا شين جي فهرست:

RealmModel::class.java.isAssignableFrom(field.type)

RealmList::class.java.isAssignableFrom(field.type)

جيڪڏهن فيلڊ هڪ RealmModel يا RealmList آهي، ته پوء هن فيلڊ جي اعتراض کي nested شين جي فهرست ۾ شامل ڪريو. هر شيء بلڪل ساڳي آهي جيئن اسان مٿي ڪيو آهي، صرف هتي اهو ئي ٿيندو. cascade ختم ڪرڻ جو طريقو پاڻ کي بلڪل سادو آهي ۽ هن وانگر ڏسڻ ۾ اچي ٿو:

fun <T : Any> Realm.cascadeDelete(entities: Collection<T?>) {
 if(entities.isEmpty()) {
   return
 }

 entities.filterNotNull().let { notNullEntities ->
   notNullEntities
       .filterRealmObject()
       .flatMap { realmObject -> getNestedRealmObjects(realmObject) }
       .also { realmObjects -> cascadeDelete(realmObjects) }

   notNullEntities
       .forEach { entity ->
         if((entity is RealmObject) && entity.isValid) {
           entity.deleteFromRealm()
         }
       }
 }
}

واڌارو filterRealmObject فلٽر ڪري ٿو ۽ پاس ڪري ٿو صرف Realm شيون. طريقو getNestedRealmObjects عڪس جي ذريعي، اهو سڀ nested Realm شيون ڳولي ٿو ۽ انهن کي هڪ لڪير فهرست ۾ وجهي ٿو. پوءِ اسان ساڳيو ڪم بار بار ڪندا آهيون. جڏهن حذف ڪرڻ، توهان کي اعتراض جي صحيحيت جي جانچ ڪرڻ جي ضرورت آهي isValid, ڇاڪاڻ ته اهو ٿي سگهي ٿو ته مختلف والدين شين کي هڪجهڙائي رکي سگهي ٿي. اھو بھتر آھي ته ھن کان پاسو ڪيو وڃي ۽ بس استعمال ڪريو id جي آٽو جنريشن جڏھن نيون شيون ٺاھيو.

ڪهاڻيءَ جو قصو ڪيئن ريلم ۾ ڪاسڪ ڊيليشن هڪ ڊگھي لانچ تي کٽيو

getNestedRealmObjects طريقي جي مڪمل عمل درآمد

private fun getNestedRealmObjects(realmObject: RealmObject) : List<RealmObject> {
 val nestedObjects = mutableListOf<RealmObject>()
 val fields = realmObject.javaClass.superclass.declaredFields

// Проверяем каждое поле, не является ли оно RealmModel или списком RealmList
 fields.forEach { field ->
   when {
     RealmModel::class.java.isAssignableFrom(field.type) -> {
       try {
         val child = getChildObjectByField(realmObject, field)
         child?.let {
           if (isInstanceOfRealmObject(it)) {
             nestedObjects.add(child as RealmObject)
           }
         }
       } catch (e: Exception) { ... }
     }

     RealmList::class.java.isAssignableFrom(field.type) -> {
       try {
         val childList = getChildObjectByField(realmObject, field)
         childList?.let { list ->
           (list as RealmList<*>).forEach {
             if (isInstanceOfRealmObject(it)) {
               nestedObjects.add(it as RealmObject)
             }
           }
         }
       } catch (e: Exception) { ... }
     }
   }
 }

 return nestedObjects
}

private fun getChildObjectByField(realmObject: RealmObject, field: Field): Any? {
 val methodName = "get${field.name.capitalize()}"
 val method = realmObject.javaClass.getMethod(methodName)
 return method.invoke(realmObject)
}

نتيجي طور، اسان جي ڪلائنٽ ڪوڊ ۾ اسان استعمال ڪندا آهيون "cascading حذف" هر ڊيٽا جي ترميمي آپريشن لاءِ. مثال طور، هڪ داخل آپريشن لاء اهو هن طرح نظر اچي ٿو:

override fun <T : Entity> insert(
 entityInformation: EntityInformation,
 entities: Collection<T>): Collection<T> = entities.apply {
 realmInstance.cascadeDelete(getManagedEntities(entityInformation, this))
 realmInstance.copyFromRealm(
     realmInstance
         .copyToRealmOrUpdate(this.map { entity -> entity as RealmModel }
 ))
}

طريقو پهريون getManagedEntities سڀ شامل ڪيل شيون حاصل ڪري ٿو، ۽ پوء طريقو cascadeDelete نون لکڻ کان اڳ ٻيهر گڏ ڪيل سڀئي شيون حذف ڪري ٿو. اسان سڄي ايپليڪيشن ۾ هن طريقي کي استعمال ڪندي ختم ڪريون ٿا. ريلي ۾ ميموري ليڪس مڪمل طور تي هليا ويا آهن. ايپليڪيشن جي ٿڌي شروعاتن جي تعداد تي شروعاتي وقت جي انحصار جي ساڳئي ماپ کي انجام ڏيڻ، اسان نتيجو ڏسون ٿا.

ڪهاڻيءَ جو قصو ڪيئن ريلم ۾ ڪاسڪ ڊيليشن هڪ ڊگھي لانچ تي کٽيو

سائي لڪير ڏيکاري ٿي ايپليڪيشن جي شروعاتي وقت جي انحصار کي سرد ​​شروع جي تعداد تي خودڪار cascade ختم ڪرڻ دوران اينسٽ ٿيل شيون.

نتيجا ۽ نتيجا

هميشه وڌندڙ ريئلم ڊيٽابيس ايپليڪيشن کي تمام سست شروع ڪرڻ جو سبب بڻائيندو هو. اسان هڪ تازه ڪاري جاري ڪئي اسان جي پنهنجي "cascading delete" nested objects جي. ۽ ھاڻي اسان مانيٽر ڪريون ٿا ۽ جائزو وٺون ٿا ته اسان جي فيصلي _app_start ميٽرڪ ذريعي ايپليڪيشن جي شروعاتي وقت کي ڪيئن متاثر ڪيو.

ڪهاڻيءَ جو قصو ڪيئن ريلم ۾ ڪاسڪ ڊيليشن هڪ ڊگھي لانچ تي کٽيو

تجزيي لاءِ، اسان 90 ڏينهن جو عرصو وٺون ٿا ۽ ڏسو: ايپليڪيشن لانچ جو وقت، ٻنهي وچين ۽ جيڪي استعمال ڪندڙن جي 95 هين فيصد تي اچي ٿو، گهٽجڻ شروع ٿي ويا ۽ وڌيڪ نه وڌندا.

ڪهاڻيءَ جو قصو ڪيئن ريلم ۾ ڪاسڪ ڊيليشن هڪ ڊگھي لانچ تي کٽيو

جيڪڏهن توهان ستن ڏينهن جي چارٽ تي نظر رکون ٿا، _app_start ميٽرڪ مڪمل طور تي مناسب نظر اچي ٿو ۽ 1 سيڪنڊ کان گهٽ آهي.

اهو پڻ شامل ڪرڻ جي قابل آهي ته ڊفالٽ طور، فائر بيس نوٽيفڪيشن موڪلي ٿو جيڪڏهن _app_start جو وچين قدر 5 سيڪنڊن کان وڌيڪ آهي. بهرحال، جيئن اسان ڏسي سگهون ٿا، توهان کي هن تي ڀروسو نه ڪرڻ گهرجي، بلڪه اندر وڃو ۽ ان کي واضح طور تي چيڪ ڪريو.

Realm ڊيٽابيس بابت خاص ڳالهه اها آهي ته اهو هڪ غير لاڳاپو ڊيٽابيس آهي. ان جي استعمال جي آسانيءَ جي باوجود، ORM حلن ۽ اعتراض سان ڳنڍڻ جي هڪجهڙائي، ان ۾ ڪاسڪيڊ ڊيليٽ نه آهي.

جيڪڏهن اهو حساب ۾ نه ورتو وڃي، ته پوءِ اندر ٿيل شيون گڏ ٿينديون ۽ ”ليڪ پري“ ٿينديون. ڊيٽابيس مسلسل وڌندو، جنهن جي نتيجي ۾ ايپليڪيشن جي سست يا شروع ٿيڻ تي اثر انداز ٿيندو.

مون اسان جو تجربو شيئر ڪيو ته ڪيئن جلدي ڪيئن ڪجي cascade of objects of Realm ۾ حذف ڪرڻ، جيڪو اڃا تائين دٻي کان ٻاهر نه آهي، پر هڪ ڊگهي وقت تائين ڳالهايو ويو آهي. اهي چون ٿا и اهي چون ٿا. اسان جي حالت ۾، هن ايپليڪيشن جي شروعاتي وقت کي تمام گهڻو تيز ڪيو.

هن خصوصيت جي امڪاني ظهور جي باري ۾ بحث جي باوجود، ريئلم ۾ cascade ختم ڪرڻ جي غير موجودگي ڊزائن طرفان ڪيو ويو آهي. جيڪڏهن توهان هڪ نئين ايپليڪيشن ٺاهي رهيا آهيو، پوء هن کي اڪائونٽ ۾ وٺو. ۽ جيڪڏھن توھان اڳ ۾ ئي استعمال ڪري رھيا آھيو Realm، چيڪ ڪريو ته توھان وٽ اھڙيون مسئلا آھن.

جو ذريعو: www.habr.com

تبصرو شامل ڪريو