Behind the mVoter 2017 : Scrapping Realm
ကျွန်တော်တို့ mVoter 2017 ကို စစရေးတော့ Offline Database ကိုဘာသုံးကြမလဲဆိုပြီး တိုင်ပင်ကြတယ်။ အဲဒီအချိန်တုန်းက Realm က အရမ်းခေတ်စားနေတာက တစ်ကကြောင်း၊ အသုံးပြုရတာလွယ်ကူတယ်ဆိုတဲ့ အချက်က တစ်ကြောင်း အဲဒါနဲ့ပဲ Realm ကို စသုံးဖြစ်ခဲ့ကြတယ်။ စစချင်းကတော့ ရေးရတာ အဆင်ပြေနေတာပဲ။ ဘာ mapper မှမလိုဘူး၊ Object လိုက်ကြီး save လိုက်ရုံပဲ။ ဘာအခက်အခဲမှလည်း မရှိဘူးပေါ့။ ဒါပေမဲ့နောက်ပိုင်းမှာ ကျွန်တော်တို့ Realm Framework ကြီးတစ်ခုလုံးကို ဖြုတ်ချပြီး ဘာ library မှမသုံးပဲ SQLite နဲ့ပဲရအောင် Mapper တွေကအစ ပြန်ရေးခဲ့ကြတယ်။ ဒီလို Realm ကိုအကုန်ပြန်ဖျက်ပြီး SQLIte ရွေးလိုက်ရတဲ့ အကြောင်းအရင်းက စိတ်ဝင်စားဖို့ကောင်းမလားဆိုတဲ့ ရည်ရွယ်ချက်နဲ့ ဒီ blog post ကိုရေးလိုက်တာပါ။
Boilerplates
Realm ကိုစသုံးမယ်ဆို အရင်ဆုံးသတိပြုမိမှာက Realm က RealmList<String> တွေကို တိုက်ရိုက် save လို့မရဘူး။ ဘာဖြစ်လို့လဲဆိုတော့ String class က RealmObject ကို extends ထားတာမဟုတ်လို့ပါ။ ဒီတော့ ဘာလုပ်ရလဲဆိုတော့ RealmString ဆိုပြီး class ထပ်ရေးရတယ်။
Retrofit ရဲ့ Default GsonConverter က သူ de-serialize လုပ်တဲ့အခါမှာ RealmList<RealmString> ကိုသူမသိဘူး။ ဒီတော့ ဘာဆက်လုပ်ရလဲဆိုတော့ RealmStringListTypeAdapter ဆိုပြီး ထပ်ရေးပေး၊ ပြီးရင် Gson create လုပ်တဲ့အခါမှာ ရေးခဲ့တဲ့ Adapter ကိုထည့်ပေးရတယ်။
Scroll ရတာ တော်တော်တောင်များသွားလား မသိဘူး။ ဒါဆိုကျွန်တော်ပြောချင်တဲ့ Boilerplate တွေအများကြီး အနောက်ကလိုက်ရတဲ့ အချက်ကိုသွားသတိထားမိမှာပါ။ သင်က Parceler ကိုသုံးမယ်ဆိုရင် ReamListParcelConverter ကိုထပ်ရေးပေးရပါတယ်။ ကျွန်တော့်အနေနဲ့ကတော့ Library တစ်ခုတည်းအတွက်နဲ့ နောက်က လိုက်ညှိရေးနေရတာကိုသဘောမကျဘူး။ တကယ်လို့ ထားပါတော့ Realm မသုံးပဲနောက်တစ်ခုပြောင်းတော့မယ်ဆိုရင် List<String> ဆိုရင် ကိစ္စမရှိပေမဲ့. RealmString တွေဖြစ်နေလို့ သင် realmString.value ဆိုပြီး ခေါ်ထားသမျှနေရာတိုင်းမှာ ပွထနေအောင် လိုက်ပြင်ရတော့မယ်။
Forget to Close = Chaos
အမှားကင်းတဲ့ code ဆိုတာမရှိပါဘူး၊ တစ်ချိန်ချိန်မှာသင်က db ကို close ဖို့မေ့သွားနိုင်တယ်။ Realm ကို ပြောင်းလိုက်ရတဲ့ ရည်ရွယ်ချက်နောက်တစ်ချက်ကို ပြရမယ်ဆိုရင် သူ အလုပ်လုပ်ပုံကို အရင်ဆုံး နားလည်မှရမယ်။ Realm က Design အရသူက data write နေတုန်းမှာ read လုပ်လိုက်တယ်ဆိုရင် write တာပြီးမှထုတ်ပေးတာမျိုးမဟုတ်ပဲ အရင်ရှိပြီးသား နောက်ဆုံး version ကိုထုတ်ပေးလိုက်ပါတယ်။ IO Lock လုပ်ထားတာမျိုးမရှိပါဘူး။ getInstance လို့ခေါ်လိုက်တိုင်းမှာ သူက instance count ကို ၁ တိုးလိုက်ပါတယ်။ close လုပ်တဲ့အခါကျမှ count ကို ၁ လျှော့တယ်။ instance count သုညဖြစ်သွားပြီဆိုရင် Data ငြိမ်သွားပြီလို့ ယူဆပြီး changes တွေအကုန်လုံးကို merge ပြန်လုပ်ပါတယ်။ Data Query လုပ်နေတုန်းမှာ NullPointer ပဲဖြစ်ဖြစ် တစ်ခုခုကြောင့် crash သွားရင် instance ကို ၁ ပြန်မလျှော့ပါဘူး။ ဒီတော့ Data က ဘယ်တော့မှ update မဖြစ်တော့ဘူး၊ ဒါ့အပြင် close မလုပ်တဲ့ Data Snapshot တွေ တဖြည်းဖြည်းများလားပြီး App Data Size က သိသိသာသာတိုးလာတယ်။ ဒါကြောင့်သူက Doc ထဲမှာ Activity ရဲ့ onDestroy ကိုရောက်တော့မှ close လုပ်ခိုင်းတာဖြစ်တယ်။ ဒါကိုကာကွယ်တဲ့နည်းရှိပါတယ်။ ဘာလုပ်လုပ် beginTranscation ကိုအရင်ခေါ်ပါ။ beginTranscation ကိုခေါ်လိုက်တိုင်းမှာ သူက ရှိသမျှ snapshot တွေအကုန်လုံးကို merge လုပ်ပြီး version အသစ်ထုတ်ပေးတယ်။ ပြီးမှ ကျန်တာကို ဆက်လုပ်တယ်။
Larger Apk Size
Realm Library က 3 MB ရှိပါတယ်။ ဘာမှမလုပ်ပဲ Realm ထည့်ပြီး Release Build ထုတ်ကြည့်လိုက်ပါ။ Apk Size က သိသိသာသာတိုးလာတာကိုတွေ့ရမှာဖြစ်တယ်။ ဒါကို ဘယ်လိုလျှော့လို့ရလဲဆိုတော့ abi split လုပ်လို့ရပါတယ်။ CPU architecture ပေါ်မူတည်ပြီး apk တွေခွဲထုတ်လိုက်တာပါ။ ဒါဆိုရင် တော့ size ကတော်တော်နည်းသွားမယ်။
ဒါပေမဲ့လည်း တစ်ချို့ CPU Architecture တွေမှာ Apk Size က Realm မထည့်ခင်ကထက် 1 MB လောက်တိုးနေတာတွေ့ရလိမ့်မယ်။ mVoter က နယ်ဘက်တွေအထိမှာ ဖြန့်မှာဖြစ်လို့ အတတ်နိုင်ဆုံး apk size ကို နည်းသထက်နည်းအောင် လုပ်မှဖြစ်မယ်။ ဒီအချက်ကတော့ Realm ကိုပြန်ဖြုတ်လိုက်ရတဲ့ အဓိကအချက်လို့ပြောလို့ရတယ်။ User တွအနေနဲ့ Download Size နည်းလေ ကောင်းလေဆိုပြီး SQLite ကိုပြန်သွားဖြစ်သွားတယ်။
Thread Confined
Realm က Thread Confined ဖြစ်တယ်။ Realm ကရလာတဲ့ RealmResults တွေက IO Thread မှာ query လုပ်ထားတာဆိုရင် IO Thread ကနေပဲသုံးလို့ရပါတယ်။ တစ်ခြား Thread ပေါ်ပြောင်းချင်ရင် copyFromRealm ဆိုတဲ့ method ကိုသုံးရတယ်။ မြန်ချင်လို့ Realm သုံးပါတယ်ဆို အမြဲတမ်း copyFromRealm ခေါ်နေရတာက Anti-Pattern တစ်ခုဖြစ်တယ်။ mVoter ကိုစရေးကတည်းက Rx Approach ကိုသုံးခဲ့တယ်။ Rx က thread ခွဲပေးတယ်၊ Realm က Thread Confined ဖြစ်နေတယ် ဆိုတော့ နှစ်ခုက တောင်နဲ့ မြောက်လို ဖြစ်နေတယ်။ ဒါကို မရမက အတင်းလိုက်ညှိထားသလိုဖြစ်နေတယ်။ Realm ရဲ့ approach ကိုက deep integrate လုပ်ရတဲ့နည်း ကိုလိုက်နာထားတယ်။ Realm Query တွေကို ဆိုင်ရာဆိုင်ရာ class ခွဲရေးလို့မရပဲ Activity/Fragment ထဲမှာ တိုက်ရိုက် query လုပ်ရတဲ့ approach ကိုသုံးထားတယ်။ ကျွန်တော့်အမြင်ကတော့ Database က UI ပိုင်းပေါ်ကို တက်လာစရာမလိုဘူး လို့ထင်တယ်။ UI ဆိုတာ Data ပြဖို့ပဲဖြစ်နေရမယ်။ Data Query တွေပါလာပြီဆိုရင်း “tightly coupled” ဖြစ်ပြီး maintain လုပ်ရတာ ခက်လာနိုင်ပါတယ်။
So.. Realm is Bad?
ဒီလိုလည်း မဟုတ်သေးဘူး။ သူ့နေရာနဲ့သူ အသုံးဝင်ပါတယ်။ တကယ်လို့ သင်ဟာ အောက်ပါအချက်တွေ ကိုက်ညီတယ်ဆိုရင်တော့ Realm က တကယ်ကိုသုံးလို့ အဆင်ပြေတဲ့ OODB တစ်ခု ဖြစ်တယ်လို့ ကျွန်တော်ကမြင်တယ်။ ဒီလိုမှမဟုတ်ဘူးဆိုရင်တော့ တစ်ခြား DAO တစ်ခုရှာတာပိုကောင်းမယ်လို့ပဲထင်တယ်။
- Data Accuracy ထက် Speed ကို ဉီးစားပေးချင်တယ်။
- အချိန်မရပဲ အမြန်ရေးရမယ်။
- Apk size ကြီးလည်း ပြဿနာမရှိဘူး။
- Deep Integration လုပ်မယ်။
ကျွန်တော့ အမြင်ရယ် အတွေ့အကြုံရယ်ကို သုံးသပ်ထားတာဖြစ်လို့ အမှားပါသွားရင် ထောက်ပြပေးစေလိုပါတယ်။