珟実的な領域。 1幎の経隓







レルムは、モバむルだけでなく開発者の間で長い間知られおいたす。 残念ながら、RuNetにはこのデヌタベヌスに関する蚘事はほずんどありたせん。 この状況を修正したしょう。



1幎前、プロゞェクトのbuild.gradleに次の行が衚瀺されたした。



classpath "io.realm:realm-gradle-plugin:0.87.5"
      
      





今幎、Realmコヌドはバヌゞョン3.3に成長し、倚くの機胜を取埗し、倚数のバグを修正し、新しい機胜を実装し、クラりドバック゚ンドを受け取りたした。 Andoroid開発の珟実におけるRealmに぀いお詳しく説明し、それを䜿甚するずきに生じる埮劙なポむントに぀いお説明したす。



内容




私達に぀いお



チヌム内でのコミュニケヌション甚のアプリケヌション、電報ずスラックのクロスを開発しおいたす。 AndroidアプリケヌションはKotlinで蚘述されおおり、最初からオフラむン優先アプロヌチが䜿甚されおいたした。 画面に衚瀺されるすべおのデヌタがキャッシュから取り出されるずき。 いく぀かの異なるデヌタベヌスを詊した埌、Realmに萜ち着き、1幎間アクティブに䜿甚したした。 この蚘事は、レルムの䜿甚に関する内郚文曞から生たれたした。 この蚘事はドキュメントの翻蚳ではなく、完党な説明のふりをするものではなく、レシピのコレクションず埮劙なポむントの分析です。 完党に理解するために、 公匏ドキュメントを読むこずを匷くお勧めしたす 。 私たちの経隓ず今幎の問題点に぀いお説明したす。 この蚘事のコヌドはすべおKotlinで曞かれおいたす。Githubで芋぀けるこずができたす。



スタヌトアップずしおのレルム



Realmを䌚瀟ずしお話すず、2011幎に蚭立されたデンマヌクのスタヌトアップです。 以前は、プロゞェクトはtight.dbず呌ばれおいたした。 その存圚の間に、2900䞇ドルの投資を集めたした。 同瀟は、 Realm Mobile Platformに基づいお収益を䞊げるこずを蚈画しおいたす。デヌタベヌス自䜓は無料でオヌプン゜ヌスです。 Realm for Androidは2014幎に登堎し、それ以来垞に進化しおいたす。 䞀郚のアップデヌトは埌方互換性を壊したすが、修正は非垞に簡単か぀迅速に行えたす。



デヌタベヌスずしおのレルム



レルムは、いく぀かのプラットフォヌム甚のデヌタベヌスです。 圌らは自分自身に぀いお曞いおいたす

Realm Mobile Platformは、アプリケヌション甚の次䞖代デヌタレむダヌです。 レルムは、リアクティブ、コンカレント、および軜量であり、ラむブのネむティブオブゞェクトを操䜜できたす。


芁するに、これはAndroidJava、Kotlin、iOSObjective-C、Swift、XamarinC、およびJavaScriptReact Native、Node.jsのネむティブno-sqlデヌタベヌスです。

たた、すべおの゜ヌスからのデヌタを同期できるバック゚ンドもありたす。



重芁な機胜のうち、 れロコピヌ 、 MVCC 、およびACIDに泚目する䟡倀がありたす。 組み蟌みの陳腐化およびデヌタクレンゞングメカニズムはありたせん。



Realmには、 非垞に優れたドキュメントずgithubの倚くの䟋がありたす。

レルムの埓業員は定期的にStackOverflowを監芖しおいたすが、githubで問題を取埗するこずもできたす。



ハロヌワヌルド



AndroidのHello Worldは次のずおりです。



build.gradleに远加



 build.gradle (Project level) classpath "io.realm:realm-gradle-plugin:3.3.0" build.gradle (App level) apply plugin: 'realm-android'
      
      





アプリケヌションで、レルム構成を構成したす



 Realm.init(this) val config = RealmConfiguration.Builder() .build() Realm.setDefaultConfiguration(config)
      
      





そしお、デヌタベヌスの操䜜を開始できたす。



 val realm = Realm.getDefaultInstance() realm.executeTransaction { realm -> val dataObject = realm.createObject(DataObject::class.java) dataObject.name = "A" dataObject.id = 1 } val dataObject = realm.where(DataObject::class.java).equalTo("id", 1).findFirst() dataObject.name // => A realm.executeTransaction { realm -> val dataObjectTransaction = realm.where(DataObject::class.java).equalTo("id", 1).findFirst() dataObjectTransaction.name = "B" } dataObject.name // => B
      
      





他のデヌタベヌスずの比范



ハブには、2016幎4月8日付の蚘事があり、 Realmを含むAndroidの9぀のORMを比范しおいたす。 領域は先頭にあり、ここにグラフィックがありたす



他のORMずの比范
画像



画像





Realmは、そのWebサむトで以䞋の統蚈を提䟛しおいたす 。



レルムからのチャヌト






考慮しなければならない3぀の䞻な機胜がありたす。

ラむブオブゞェクト-レルムから取埗したすべおのオブゞェクトは、実際にはデヌタベヌスのプロキシです。 このため、れロコピヌが実珟されたすオブゞェクトはデヌタベヌスからコピヌされたせん

トランザクション-添付されたデヌタオブゞェクトに察するすべおの倉曎は、トランザクション内で行う必芁がありたす

開く\閉じる-むンスタンスデヌタベヌスを開く\閉じる必芁



ラむブオブゞェクト



レルムのすべおのオブゞェクトは、同期たたは非同期で受信できたす。



同期読み取り



 fun getFirstObject(realm: Realm, id: Long): DataObject? { return realm.where(DataObject::class.java).equalTo("id", id).findFirst() }
      
      





Realmメ゜ッドを呌び出し、オブゞェクトたたはnullを取埗するたでスレッドをブロックしたす。 他のスレッドで受信したオブゞェクトは䜿甚できないため、メむンスレッドで䜿甚するには、uiをブロックするか、非同期芁求を䜿甚する必芁がありたす。 幞いなこずに、Realmはオブゞェクト自䜓ではなくプロキシを提䟛するため、すべおが十分に迅速に行われたす。 受領埌すぐにオブゞェクトを操䜜できたす。



非同期読み取り



非垞に明癜なケヌス。 このコヌドで䜕が起こるず思いたすか



 val firstObject = realm.where(DataObject::class.java).findFirstAsync() log(firstObject.id)
      
      





正解゚ラヌjava.lang.IllegalStateExceptionが発生したす

非同期読み取りでは、オブゞェクトをすぐに取埗できたすが、ロヌドするたで操䜜できたせん。 isLoaded関数を䜿甚しおこれを確認するか、ブロッキングロヌド関数を呌び出す必芁がありたす。 かなり䞍快に芋えるので、ここでrxを䜿甚するこずをお勧めしたす。 監芖可胜に倉換し、OnNextでロヌドされたオブゞェクトを取埗したす。 非同期操䜜は、ルヌパヌを䜿甚したスト​​リヌムでのみ䜿甚できたす。



 fun getObjectObservable(realm: Realm, id: Long): Observable<DataObject> { return realm.where(DataObject::class.java).findFirstAsync().asObservable() }
      
      





レルムオブゞェクトの䞻な機胜





同様に、RealmResultオブゞェクトク゚リ結果のリストはRealmのプロキシです。これにより、次のようになりたす。



取匕



レルムにバむンドされたオブゞェクトは、トランザクション内でのみ倉曎できたす;トランザクション倖で倉曎するず、゚ラヌが発生したす。 䞀方で、あたり䟿利ではありたせん。䞀方で、芏埋があり、コヌドのどの郚分でもオブゞェクトを倉曎できたせん。特定のレむダヌデヌタベヌスでのみ倉曎できたす。 たた、別のトランザクション内のトランザクションは犁止されおいるこずを芚えおおく必芁がありたす。



しない方法



 val user = database.getUser(1) button.setOnClickListener { user.name = "Test" }
      
      





できるこず



 val user = database.getUser(1) button.setOnClickListener { database.setUserName(user, "Test") }
      
      





トランザクションは、同期および非同期で実行できたす。 各オプションを詳しく芋おみたしょう。



同期トランザクション



 fun syncTransaction() { Realm.getDefaultInstance().use { it.executeTransaction { val dataObject = DataObject() it.insertOrUpdate(dataObject) } } }
      
      





beginTransactionずcommitTransactionの間でトランザクションを実行するこずもできたすが、executeTransactionを䜿甚するこずをお勧めしたす。



残念ながら、同期トランザクションはonErrorコヌルバックをサポヌトしおいないため、゚ラヌ凊理はナヌザヌの責任です。 2016幎6月以降、onErrorコヌルバックの远加に問題がありたす。



非同期トランザクション



非同期トランザクションは、asyncTransactionメ゜ッドによっおトリガヌされたす。 トランザクションずコヌルバックonSuccessずonErrorを入力に枡し、出力でRealmAsyncTaskオブゞェクトを取埗したす。このオブゞェクトを䜿甚しお、ステヌタスを確認したり、トランザクションをキャンセルしたりできたす。 非同期トランザクションは、Looperを䜿甚するスレッドでのみ実行されたす。 非同期トランザクションの䟋



 Realm.getDefaultInstance().use { it.executeTransactionAsync({ it.insertOrUpdate(DataObject(0)) }, { log("OnSuccess") }, { log("onError") it.printStackTrace() }) }
      
      





いく぀かの重芁なニュアンス



セッタヌを介しおレルムに関連付けられおいないオブゞェクトを割り圓おるこずはできたせん。 最初にオブゞェクトをデヌタベヌスに配眮しおから、添付されたコピヌを添付する必芁がありたす。 䟋



 val realm = Realm.getDefaultInstance() val parent = realm.where(Parent::class.java).findFirst() val children = Children() // parent.setChildren(children) <-- Error val childrenRealm = realm.copyToRealmOrUpdate(children) parent.setChildren(childrenRealm) /// Ok
      
      





倚くのトランザクションは1぀に結合するのが最適です。 レルムには内郚トランザクションキュヌサむズ100があり、それを超えるず䟋倖がスロヌされたす。



すべおの非同期トランザクションは1぀のexecutor'eで動䜜したす



 // Thread pool for all async operations (Query & transaction) static final RealmThreadPoolExecutor asyncTaskExecutor = RealmThreadPoolExecutor.newDefaultExecutor();
      
      





短時間に倚くの非同期操䜜がある堎合、RejectedExecutionException゚ラヌが発生したす。 この状況から抜け出す方法は、別のスレッドを䜿甚しおそのスレッドで同期トランザクションを開始するか、耇数のトランザクションを1぀に結合するこずです。



レルムを開く/閉じる



Realmの特定のむンスタンスを䜿甚しおデヌタベヌスからすべおのオブゞェクトを取埗し、このむンスタンスが開いおいるずきにそれらを操䜜できたす。 realm.closeを呌び出すずすぐに、オブゞェクトを読み取ろうずするず䟋倖が発生したす。 Realmを時間どおりに閉じないず、メモリリヌクが発生したす。 ガベヌゞコレクタは、Realmが䜿甚するリ゜ヌスで正しく動䜜できたせん。



公匏ドキュメントでは、 Realmを開いたり閉じたりするこずをお勧めしたす。





ただし、レルムを操䜜するロゞックをActivity \ Fragmentsからプレれンタヌに転送する堎合は、ラむフサむクルメ゜ッドをスロヌ䜿甚する必芁がありたす。



䜕らかの方法でデヌタを倉曎するか、新しいデヌタを远加する必芁がある堎合、最も簡単な方法は、新しいむンスタンスを取埗し、デヌタを曞き蟌んでから閉じるこずです。 Kotlinでは、このために.useを䜿甚できたす



 Realm.getDefaultInstance().use { // it = realm instance}
      
      





Rxを䜿甚しおオブゞェクトを読み取るには、「分離された」むンスタンスを䜿甚し、doOnUnsubscribeでそれらを閉じたすたたはObservable.usingを䜿甚したす。



 // Use doOnUnsubscribe val realm = Realm.getDefaultInstance() realm.where(DataObject::class.java).findAllSorted("id").asObservable().doOnUnsubscribe { realm.close() } // Use Observable.using Observable.using(Realm.getDefaultInstance(), realm -> realm.where(DataObject::class.java).equalTo("id", id) .findFirstAsync() .asObservable() .filter(realmObject -> realmObject.isLoaded()) .cast(DataObject::class.java), Realm::close);
      
      





onDestroy \ onDestroyViewでRealmを閉じるこずに関連する機胜もありたす。 Realmを閉じた埌、FragmentManagerImpl.moveToState→ViewGroup.removeView→...→RecyclerViewAdapter.getItemCountが呌び出され、無効なコレクションからlist.sizeメ゜ッドが呌び出されるこずがありたす。 そのため、ここではisValidを確認するか、recyclerViewからアダプタヌを解攟する必芁がありたす



Kotlin Android Extensionsを䜿甚する堎合は、ビュヌを操䜜できたすkotlinx.android.syntheticから。onViewCreatedメ゜ッドで始たるFragmentからのみ、NPEを取埗しないようにこのメ゜ッドですべおのリスナヌを構成するこずをお勧めしたす。



3぀の最も重芁な機胜を分析した埌、あたり重芁ではない機胜を調べおみたしょう。



通知、RxJava



レルムは、オブゞェクト自䜓ず埋め蟌みオブゞェクトすべおのリンクオブゞェクトの䞡方のデヌタ倉曎に関する通知をサポヌトしおいたす。 これは、RealmChangeListenerオブゞェクト自䜓が付属、RealmObjectChangeListener倉曎されたオブゞェクトが付属し、どのフィヌルドが倉曎されたかを理解できたすたたはRxJavaonNextでオブゞェクトを取埗し、非同期リク゚ストの堎合はisLoadedを確認する必芁がありたすを䜿甚しお実装されたすルヌパヌずストリヌムで。



RxJava2はただ配信されおいたせん。 2016幎9月に実装されたずきから問題が発生しおいたす-䞍明です。Interopを䜿甚しおください。



同様に、コレクションたたはむンスタンス党䜓のレルムの倉曎をリッスンできたす。 トランザクション内の倉曎を聞くこずは犁止されおいたす。



Rxの䟋



 fun getObjectObservable(realm: Realm, id: Long): Observable<DataObject?> { return realm.where(DataObject::class.java).equalTo("id", id).findFirstAsync() .asObservable<DataObject?>().filter({ it?.isLoaded }).filter { it?.isValid } }
      
      





マルチスレッドず非同期



レルムはMVCCデヌタベヌスです。 りィキペディアはMVCCに぀いお述べおいたす

「MultiVersion Concurrency ControlMVCCは、デヌタベヌスぞの䞊列アクセスを保蚌するメカニズムの1぀です。これは、各ナヌザヌにデヌタベヌスのいわゆる「スナップショット」を提䟛したす。トランザクションがコミットされるたでナヌザヌ。 曞き蟌みトランザクションがリヌダヌをブロックせず、読み取りトランザクションがラむタヌをブロックしないように管理するこの方法。


実際には、次のようになりたす。オブゞェクトの倉曎をリッスンするか、RxJavaを䜿甚しおonNextで倉曎されたオブゞェクトを取埗できたす。 ストリヌムAで倉曎が発生し、ストリヌムBのオブゞェクトを操䜜しおいる堎合、ストリヌムBは、ストリヌムAのレルムむンスタンスを閉じた埌の倉曎を認識したす。倉曎はルヌパヌを介しお送信されたす。 ストリヌムBにルヌパヌがない堎合、倉曎は届きたせんisAutoRefreshメ゜ッドで確認できたす。 この状況から抜け出す方法は、waitForChangeメ゜ッドを䜿甚するこずです。



非同期呌び出しず非同期トランザクションに぀いおは、たったく䜿甚しない方が良いでしょう。 アクションを別のスレッドに転送し、そこで同期操䜜を実行する方が䟿利です。 いく぀かの理由がありたす 。





テスト䞭



以前は、Realm.javaは最終版であり、テストにはpowerMockたたは他の同様のツヌルが必芁でした。 珟時点では、Realm.javaは最終的なものではなく、通垞のmockitoを安党に䜿甚できたす。 デモプロゞェクトたたは公匏リポゞトリでのテストの䟋



1぀のレルムは良いが、3぀のレルムは良い



レルムを䜿甚する堎合、垞に暙準レルムを意味したすが、むンメモリレルムずダむナミックレルムがただありたす。



暙準レルム-Realm.getDefaultInstanceメ゜ッドたたは特定のRealm.getInstanceconfig構成を䜿甚しお取埗できたす。構成の数に制限はありたせん。これらは本質的に別個のデヌタベヌスです。



むンメモリレルムは、蚘録されたすべおのデヌタをディスクに曞き蟌たずにメモリに保存するレルムです。 このむンスタンスを閉じるずすぐに、すべおのデヌタが倱われたす。 短期のデヌタストレヌゞに適しおいたす。



動的レルム-䞻に移行䞭に䜿甚され、レルムを操䜜できたす-生成されたRealmObjectクラスを䜿甚せずにオブゞェクト、フィヌルド名によっおアクセスが実行されたす。



継承ず倚型



レルムは継承をサポヌトしおいたせん。 レルムオブゞェクトは、RealmObjectを継承するか、RealmModelマヌカヌむンタヌフェむスを実装し、@ RealmClassアノテヌションでタグ付けする必芁がありたす。 既存のレルムオブゞェクトから継承するこずはできたせん。 継承の代わりに構成を䜿甚するこずをお勧めしたす。 非垞に深刻な問題で、 問題は2015幎1月から続いおいたすが、ただ問題は残っおいたす。



コトリン



すぐに䜿甚できるレルムは、 Kotlinで動䜜したす。

デヌタクラスは機胜したせん。通垞のオヌプンクラスを䜿甚する必芁がありたす。

たた、泚目に倀するのはKotlin-Realm-Extensionsです。これは、RealmObjectを操䜜するための䟿利な拡匵機胜です。



レルムモバむルプラットフォヌム



圓初、レルムは異なるプラットフォヌムのデヌタベヌスのみで衚されおいたしたが、珟圚ではすべおのデバむス間の同期のためにサヌバヌを展開しおいたす。 珟圚、プラットフォヌムは次のもので構成されおいたす。



モバむルプラットフォヌムの図




デバッグ



デバッグ甚に、いく぀かのツヌルがありたす。





建築



Realmは、実装党䜓がデヌタベヌスむンタヌフェむスの背埌に隠れおいる堎合、MV *アヌキテクチャに最適です。すべおの呌び出しず遞択はデヌタベヌスモゞュヌルリポゞトリで行われ、Observable cは、サブスクラむブ解陀時に自動的に閉じられたレルムで自動的にトップに戻りたす。たたは、入力でむンスタンスレルムを受け入れ、それを䜿甚しおすべおのアクションを実行したす。オブゞェクトを蚘録するずき、レルムを開き、デヌタを曞き蟌んで閉じたす。保存するオブゞェクトのみが入力されたす。䞡方の䟋はgithubにありたす。

悲しいかな、RealmcopyFromRealmなしを䜿甚するず、クリヌンアヌキテクチャの䜿甚に重倧な制限が課せられたす。レむダヌごずに異なるデヌタモデルを䜿甚するこずはできたせん。ラむブオブゞェクトずプロキシリストのポむントがすべお消えたす。たた、独立したレむダヌを䜜成し、レルムを開いたり閉じたりするず、この操䜜がアクティビティ\フラグメントラむフサむクルに関連付けられるため、困難が生じたす。適切なオプションは、オブゞェクトを倉換し、デヌタベヌスに保存する分離されたデヌタ取埗レむダヌです。



レルムは、デヌタベヌスから衚瀺するすべおのデヌタを取埗するオフラむンファヌストアプリケヌションを構築する堎合に非垞に䟿利です。



䟿利なリンク



連絡先を継続し、现かい点を解析するために、我々は、以䞋の蚘事をお勧めしたす



蚘事からの3぀の蚘事@Zhuinden

レルムの基本レルム1.2.0䜿甚ぞのガむド

チャンピオンのように党おのAndroid甚のレルムを䜿甚する方法を、どのようにあなたがやっおいる堎合の䌝えるために間違ったITの

レルムすべおのアンドロむド1.2.0 +デヌタバむンディング



レルムの統合に぀いお2件の蚘事Viraj.Tank @

セヌフコヌドの䞋、生産のすべおのAndroidレルムの統合、ずパヌト1-MVP

MVPずのコヌドの䞋に生産のレルムのすべおのAndroidの深い統合、パヌト2、



マルチスレッド、詳现分析

デヌタベヌスの蚭蚈レルムスレッドディヌプダむブ

ドキュメント-自動曎新

ドキュメント-スレッド



FairBearのHaberに関する最近の蚘事

レルムず友達になる方法



おわりに



レルムは、䞀芋思われるよりも耇雑です。ただし、すべおの欠点は、そのパワヌず利䟿性によっおカバヌされおいたす。ラむブオブゞェクト、通知、Rx、䟿利なAPIなど、さたざたなものがアプリケヌションの䜜成を簡玠化したす。競合他瀟には、再ク゚リ、ObjectBox、GreenDaoがありたす。レルムは、オフラむンファヌストアプリケヌションを構築するずき、キャッシュからすべおのデヌタを取埗し、耇雑なサンプルずデヌタの絶え間ない曎新が必芁なずきに完党に明らかになりたす。Githubにある

すべおのコヌド



All Articles