Labarin yadda gogewar cascade a cikin Realm yayi nasara akan dogon ƙaddamarwa

Duk masu amfani suna ɗaukar saurin ƙaddamarwa da amsa UI a cikin aikace-aikacen hannu don kyauta. Idan aikace-aikacen ya ɗauki lokaci mai tsawo don ƙaddamarwa, mai amfani ya fara jin bakin ciki da fushi. Kuna iya ɓata ƙwarewar abokin ciniki cikin sauƙi ko rasa gaba ɗaya mai amfani tun kafin ya fara amfani da aikace-aikacen.

Mun taba gano cewa Dodo Pizza app yana ɗaukar daƙiƙa 3 don ƙaddamar da matsakaici, kuma ga wasu “masu sa’a” yana ɗaukar daƙiƙa 15-20.

A ƙasa da yanke wani labari ne tare da kyakkyawan ƙarewa: game da haɓakar bayanan Realm, ƙwaƙwalwar ƙwaƙwalwa, yadda muka tara abubuwa masu gida, sa'an nan kuma haɗa kanmu tare da gyara komai.

Labarin yadda gogewar cascade a cikin Realm yayi nasara akan dogon ƙaddamarwa

Labarin yadda gogewar cascade a cikin Realm yayi nasara akan dogon ƙaddamarwa
Marubucin labarin: Maxim Kachinkin - Mai haɓaka Android a Dodo Pizza.

Daƙiƙa uku daga danna alamar aikace-aikacen zuwa kan Ci gaba() na aikin farko ba shi da iyaka. Kuma ga wasu masu amfani, lokacin farawa ya kai 15-20 seconds. Ta yaya hakan ma zai yiwu?

Takaitaccen taƙaitaccen taƙaitaccen bayani ga waɗanda ba su da lokacin karantawa
Rukunin bayanan mu na Realm ya girma ba iyaka. Wasu abubuwan gida ba a goge su ba, amma ana taruwa akai-akai. Lokacin farawa aikace-aikacen ya ƙaru a hankali. Sa'an nan kuma muka gyara shi, kuma lokacin farawa ya zo ga manufa - ya zama ƙasa da 1 seconds kuma ya daina karuwa. Labarin ya ƙunshi nazarin halin da ake ciki da mafita guda biyu - mai sauri da na al'ada.

Bincike da nazarin matsalar

A yau, kowane aikace-aikacen hannu dole ne ya ƙaddamar da sauri kuma ya kasance mai amsawa. Amma ba kawai game da wayar hannu app ba. Kwarewar mai amfani na hulɗa tare da sabis da kamfani abu ne mai rikitarwa. Misali, a yanayinmu, saurin isarwa ɗaya ne daga cikin maɓalli na sabis na pizza. Idan bayarwa yana da sauri, pizza zai yi zafi, kuma abokin ciniki wanda ke son ci yanzu ba zai jira dogon lokaci ba. Don aikace-aikacen, bi da bi, yana da mahimmanci don ƙirƙirar jin daɗin sabis na sauri, saboda idan aikace-aikacen yana ɗaukar daƙiƙa 20 kawai don ƙaddamarwa, to yaushe zaku jira pizza?

Da farko, mu kanmu mun fuskanci cewa, wani lokacin aikace-aikacen ya ɗauki tsawon daƙiƙa biyu don ƙaddamar da shi, sannan muka fara jin koke-koke daga sauran abokan aikinmu game da tsawon lokacin da aka ɗauka. Amma mun kasa maimaita wannan yanayin akai-akai.

Har yaushe ne? Bisa lafazin Takardun Google, idan farkon sanyi na aikace-aikacen yana ɗaukar ƙasa da daƙiƙa 5, to ana ɗaukar wannan "kamar al'ada". An ƙaddamar da Dodo Pizza Android app (bisa ga ma'aunin Firebase _app_farawa) a ba fara sanyi a matsakaita a cikin 3 seconds - "Ba mai girma ba, ba muni ba," kamar yadda suke faɗa.

Amma sai korafe-korafe suka fara bayyana cewa aikace-aikacen ya ɗauki lokaci mai tsawo da gaske don ƙaddamarwa! Da farko, mun yanke shawarar auna abin da yake "sosai, da yawa, da tsayi sosai". Kuma mun yi amfani da alamar Firebase don wannan Alamar farawa app.

Labarin yadda gogewar cascade a cikin Realm yayi nasara akan dogon ƙaddamarwa

Wannan madaidaicin alamar yana auna lokacin tsakanin lokacin da mai amfani ya buɗe aikace-aikacen da lokacin da aka ci gaba() na aikin farko. A cikin Firebase Console ana kiran wannan ma'aunin _app_start. Sai ya zama cewa:

  • Lokutan farawa don masu amfani da ke sama da kashi 95 sun kusan daƙiƙa 20 (wasu ma sun fi tsayi), duk da lokacin farawa na sanyi ƙasa da daƙiƙa 5.
  • Lokacin farawa ba ƙima ba ne, amma yana girma akan lokaci. Amma wani lokacin akwai digo. Mun sami wannan tsari lokacin da muka ƙara ma'auni na bincike zuwa kwanaki 90.

Labarin yadda gogewar cascade a cikin Realm yayi nasara akan dogon ƙaddamarwa

Tunani guda biyu suka zo a rai:

  1. Wani abu yana zubowa.
  2. Ana sake saita wannan "wani abu" bayan an sake shi sannan kuma ya sake fitowa.

"Wataƙila wani abu tare da bayanan bayanai," mun yi tunani, kuma mun kasance daidai. Da farko, muna amfani da ma'ajin bayanai azaman ma'auni; yayin hijira muna share shi. Abu na biyu, ana loda ma'aunin bayanai lokacin da aikace-aikacen ya fara. Komai yayi daidai.

Me ke damun Rukunin Bayanai na Realm

Mun fara duba yadda abubuwan da ke cikin bayanan ke canzawa akan rayuwar aikace-aikacen, daga shigarwa na farko da ƙari yayin amfani mai aiki. Kuna iya duba abubuwan da ke cikin bayanan Realm ta hanyar stetho ko dalla-dalla kuma a sarari ta hanyar buɗe fayil ɗin ta hanyar Daular Studio. Don duba abubuwan da ke cikin bayanan ta hanyar ADB, kwafi fayil ɗin bayanan Realm:

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

Bayan duba abubuwan da ke cikin ma’adanar bayanai a lokuta daban-daban, mun gano cewa adadin abubuwan wani nau’i na karuwa kullum.

Labarin yadda gogewar cascade a cikin Realm yayi nasara akan dogon ƙaddamarwa
Hoton yana nuna guntun Realm Studio don fayiloli guda biyu: a hagu - tushen aikace-aikacen ɗan lokaci bayan shigarwa, a dama - bayan amfani mai aiki. Ana iya ganin cewa adadin abubuwa ImageEntity и MoneyType ya girma sosai (hoton hoton yana nuna adadin abubuwan kowane nau'in).

Dangantaka tsakanin haɓakar bayanai da lokacin farawa

Ci gaban bayanan da ba a sarrafa shi yana da muni sosai. Amma ta yaya wannan ke shafar lokacin farawa aikace-aikacen? Yana da sauƙi a auna wannan ta hanyar ActivityManager. Tun da Android 4.4, logcat yana nuna log ɗin tare da kirtani da aka Nuna da lokacin. Wannan lokacin yana daidai da tazara daga lokacin da aka ƙaddamar da aikace-aikacen har zuwa ƙarshen aikin. A wannan lokacin abubuwa masu zuwa suna faruwa:

  • Fara tsari.
  • Farkon abubuwa.
  • Ƙirƙira da ƙaddamar da ayyukan.
  • Ƙirƙirar shimfidawa.
  • Mayar da aikace-aikace.

Ya dace da mu. Idan kuna gudanar da ADB tare da -S da -W tutoci, zaku iya samun ƙarin fitarwa tare da lokacin farawa:

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

Idan kun kama shi daga can grep -i WaitTime lokaci, zaku iya sarrafa tarin wannan ma'aunin kuma ku kalli sakamakon da gani. Hoton da ke ƙasa yana nuna dogaro da lokacin farawa aikace-aikacen akan adadin fara sanyi na aikace-aikacen.

Labarin yadda gogewar cascade a cikin Realm yayi nasara akan dogon ƙaddamarwa

A lokaci guda kuma, akwai nau'in alakar da ke tsakanin girma da girma na rumbun adana bayanai, wanda ya karu daga 4 MB zuwa 15 MB. Gabaɗaya, ya bayyana cewa bayan lokaci (tare da haɓakawar sanyi), duka lokacin ƙaddamar da aikace-aikacen da girman bayanan bayanai sun ƙaru. Muna da hasashe a hannunmu. Yanzu abin da ya rage shi ne tabbatar da dogaro. Saboda haka, mun yanke shawarar cire "leaks" kuma mu ga ko wannan zai hanzarta ƙaddamarwa.

Dalilan haɓakar bayanai marasa iyaka

Kafin cire "leaks", yana da daraja fahimtar dalilin da yasa suka bayyana a farkon wuri. Don yin wannan, bari mu tuna abin da Realm yake.

Mulkin bayanai ne marasa alaƙa. Yana ba ku damar bayyana alaƙa tsakanin abubuwa ta hanya mai kama da nawa aka kwatanta bayanan alaƙar ORM akan Android. A lokaci guda, Realm tana adana abubuwa kai tsaye cikin ƙwaƙwalwar ajiya tare da ƙaramin adadin canje-canje da taswira. Wannan yana ba ku damar karanta bayanai daga faifai cikin sauri, wanda shine ƙarfin Realm kuma dalilin da yasa ake son shi.

(Don dalilan wannan labarin, wannan bayanin zai ishe mu. Kuna iya karanta ƙarin game da Mulki a cikin sanyi takardun ko a cikin su makarantar kimiyya).

Yawancin masu haɓakawa sun saba da yin aiki tare da bayanan alaƙa (misali, bayanan ORM tare da SQL a ƙarƙashin hular). Kuma abubuwa kamar cascading shafewar bayanai sau da yawa kamar an bayar. Amma ba a Masarautar ba.

Af, an nemi fasalin gogewar cascade na dogon lokaci. Wannan bita и wani, hade da shi, an tattauna sosai. Akwai jin cewa ba da daɗewa ba za a yi. Amma sai duk abin da aka fassara zuwa gabatarwar hanyoyin haɗin gwiwa masu ƙarfi da rauni, wanda kuma zai magance wannan matsala ta atomatik. Ya kasance mai raye-raye kuma yana aiki akan wannan aikin ja roƙon, wanda aka dakatar a yanzu saboda matsalolin cikin gida.

Yayyo bayanai ba tare da gogewa ba

Ta yaya daidai bayanai ke zubewa idan kun dogara da gogewar da ba ta wanzu ba? Idan kun shigar da abubuwan Realm, to dole ne a goge su.
Bari mu kalli misali na gaske (kusan). Muna da wani abu 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()

Samfurin a cikin keken yana da filaye daban-daban, gami da hoto ImageEntity, musamman sinadaran CustomizationEntity. Har ila yau, samfurin da ke cikin keken zai iya zama haɗuwa tare da nasa samfurin RealmList (CartProductEntity). Duk filayen da aka jera abubuwa ne na Mulki. Idan muka saka sabon abu (copyToRealm() / copyToRealmOrUpdate()) tare da id iri ɗaya, to wannan abun za a sake rubuta shi gaba ɗaya. Amma duk abubuwan ciki (hoto, gyare-gyareEntity da cartComboProducts) za su rasa haɗin gwiwa tare da iyaye kuma su kasance a cikin bayanan.

Tun da haɗin kai da su ya ɓace, ba za mu ƙara karanta su ko share su ba (sai dai idan mun isa gare su a fili ko share duk "tebur"). Mun kira wannan "leaks memory".

Lokacin da muke aiki tare da Realm, dole ne mu fito fili mu bi duk abubuwan kuma mu share komai a sarari kafin irin waɗannan ayyukan. Ana iya yin wannan, misali, kamar haka:

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()
}
// и потом уже сохраняем

Idan kun yi haka, to komai zai yi aiki yadda ya kamata. A cikin wannan misalin, muna ɗauka cewa babu wasu abubuwan da aka gina a cikin hoto, gyare-gyareEntity, da cartComboProducts, don haka babu sauran madaukai na gida da sharewa.

Maganin "sauri".

Abu na farko da muka yanke shawarar yi shine tsaftace abubuwa masu girma da sauri kuma mu duba sakamakon don ganin ko wannan zai magance matsalar mu ta asali. Na farko, an yi bayani mafi sauƙi kuma mafi fahimta, wato: kowane abu ya kamata ya ɗauki alhakin cire 'ya'yansa. Don yin wannan, mun gabatar da keɓancewa wanda ya dawo da jerin abubuwan da aka gina ta Realm:

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

Kuma mun aiwatar da shi a cikin abubuwan Mulkinmu:

@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 muna mayar da dukan yara a matsayin lebur list. Kuma kowane abu na yaro yana iya aiwatar da ƙirar NestedEntityAware, yana nuna cewa yana da abubuwan cikin Realm don sharewa, misali. 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
   )
 }
}

Da sauransu, ana iya maimaita gida na abubuwa.

Sa'an nan kuma mu rubuta hanyar da za ta sake share duk abubuwan gida. Hanyar (wanda aka yi azaman tsawo) deleteAllNestedEntities yana samun duk manyan abubuwa da hanya deleteNestedRecursively A kai a kai yana cire duk abubuwan gida da aka yi ta amfani da keɓancewar NestedEntityAware:

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()
   }
 }
}

Mun yi wannan tare da abubuwa masu girma da sauri kuma mun duba abin da ya faru.

Labarin yadda gogewar cascade a cikin Realm yayi nasara akan dogon ƙaddamarwa

A sakamakon haka, waɗannan abubuwan da muka rufe da wannan maganin sun daina girma. Kuma gaba ɗaya girma na tushe ya ragu, amma bai tsaya ba.

Maganin "al'ada"

Kodayake tushe ya fara girma a hankali, har yanzu yana girma. Haka muka fara dubawa. Aikinmu yana yin amfani sosai na caching na bayanai a cikin Masarautar. Don haka, rubuta duk abubuwan gida na kowane abu yana da aiki mai ƙarfi, tare da haɗarin kurakurai yana ƙaruwa, saboda zaku iya mantawa da saka abubuwa yayin canza lambar.

Ina so in tabbatar da cewa ban yi amfani da musaya ba, amma duk abin da ke aiki da kansa.

Lokacin da muke son wani abu ya yi aiki da kansa, dole ne mu yi amfani da tunani. Don yin wannan, za mu iya shiga cikin kowane filin aji mu duba ko abu ne na Mulki ko jerin abubuwa:

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

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

Idan filin RealmModel ne ko RealmList, to ƙara abin wannan filin zuwa jerin abubuwan gida. Komai daidai yake kamar yadda muka yi a sama, a nan ne kawai za a yi shi da kansa. Hanyar share cascade kanta abu ne mai sauqi kuma yayi kama da haka:

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()
         }
       }
 }
}

Tsawaitawa filterRealmObject tacewa da wuce abubuwan Realm kawai. Hanya getNestedRealmObjects ta hanyar tunani, yana gano duk abubuwan da aka gina a cikin Realm kuma yana sanya su cikin jerin layi. Sa'an nan kuma mu yi abin da aka maimaita akai-akai. Lokacin sharewa, kuna buƙatar bincika abu don inganci isValid, domin yana iya zama abubuwa daban-daban na iyaye na iya samun gida ɗaya iri ɗaya. Zai fi kyau a guje wa wannan kuma kawai amfani da ƙirƙira ta atomatik lokacin ƙirƙirar sabbin abubuwa.

Labarin yadda gogewar cascade a cikin Realm yayi nasara akan dogon ƙaddamarwa

Cikakken aiwatar da hanyar 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)
}

A sakamakon haka, a cikin lambar abokin ciniki muna amfani da "cascading share" don kowane aiki na gyara bayanai. Misali, don aikin sakawa yana kama da haka:

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 }
 ))
}

Hanyar farko getManagedEntities yana karɓar duk abubuwan da aka ƙara, sannan hanyar cascadeDelete A kai a kai yana share duk abubuwan da aka tattara kafin rubuta sababbi. Mun ƙare yin amfani da wannan hanyar a duk aikace-aikacen. Ƙwaƙwalwar ƙwaƙwalwa a cikin Masarautar ta ƙare gaba ɗaya. Bayan aiwatar da ma'auni iri ɗaya na dogaro da lokacin farawa akan adadin farawar sanyi na aikace-aikacen, muna ganin sakamakon.

Labarin yadda gogewar cascade a cikin Realm yayi nasara akan dogon ƙaddamarwa

Koren layin yana nuna dogaron lokacin farawa aikace-aikacen akan adadin fara sanyi yayin gogewar kaske ta atomatik na abubuwan gida.

Sakamako da ƙarshe

Rukunin bayanai na Realm da ke haɓaka koyaushe yana haifar da ƙaddamar da aikace-aikacen a hankali. Mun fitar da sabuntawa tare da namu "shafewar cascading" na abubuwan gida. Kuma yanzu muna saka idanu da kimanta yadda shawararmu ta shafi lokacin fara aikace-aikacen ta hanyar ma'aunin _app_start.

Labarin yadda gogewar cascade a cikin Realm yayi nasara akan dogon ƙaddamarwa

Don bincike, muna ɗaukar lokaci na kwanaki 90 kuma mu ga: lokacin ƙaddamar da aikace-aikacen, duka na tsakiya da abin da ya faɗi akan kashi 95 na masu amfani, ya fara raguwa kuma ba ya tashi.

Labarin yadda gogewar cascade a cikin Realm yayi nasara akan dogon ƙaddamarwa

Idan ka kalli ginshiƙi na kwanaki bakwai, ma'aunin _app_start ya yi kama da cikakke kuma bai wuce daƙiƙa 1 ba.

Hakanan yana da daraja ƙarawa cewa ta tsohuwa, Firebase yana aika sanarwa idan matsakaiciyar ƙimar _app_start ta wuce daƙiƙa 5. Koyaya, kamar yadda muke iya gani, bai kamata ku dogara da wannan ba, amma ku shiga ku duba shi a sarari.

Abu na musamman game da rumbun adana bayanai na Realm shi ne cewa bayanan da ba su da alaka da su. Duk da sauƙin amfani, kamanceceniya da mafita na ORM da haɗin abu, ba shi da gogewar cascade.

Idan ba a yi la'akari da wannan ba, to, abubuwan gida za su taru kuma su "zuba". Rukunin bayanai za su yi girma akai-akai, wanda hakan zai haifar da raguwa ko farawa na aikace-aikacen.

Na raba gwanintar mu kan yadda ake saurin goge abubuwa a cikin Realm, wanda har yanzu bai fita daga cikin akwatin ba, amma an daɗe ana magana akai. ka ce и ka ce. A cikin yanayinmu, wannan ya ƙara saurin lokacin farawa aikace-aikacen.

Duk da tattaunawa game da kusantowar wannan fasalin, rashin gogewar cascade a cikin Masarautar ana yin ta ta ƙira. Idan kuna zana sabon aikace-aikacen, to, kuyi la'akari da wannan. Kuma idan kuna amfani da Realm, bincika idan kuna da irin waɗannan matsalolin.

source: www.habr.com

Add a comment