Как да използвате Realm за Android като шампион и как да разберете дали го правите погрешно

Използвам Realm от дълго време (от v0.81.1) и трябва да отбележа, че не съм свързан с Realm. Но оттогава следя много променливи промени и видях доста публикации за това как да използвам Realm ... и повечето от тях грешаха по някакъв или друг начин. Свръхкомплекс, склонен към грешки или просто очевидно грешен.

Най-новата версия към този момент е v5.9.0, можете да видите последната версия на уебсайта им. Вероятно трябва да актуализирате, ако изоставате.

Във всеки случай, тук ще ви кажа как да разберете дали пример, който четете, е лош или ако сте длъжен да объркате нещо. Но първо, много кратко въведение в Царството.

Какво е Realm?

Realm е система от бази данни. Това е нещо като SQLite, само че изобщо няма нищо общо със SQLite. Вие дефинирате класове, тези класове дефинират вашата схема и Realm съхранява екземпляри от този клас като обекти. Това не е SQL база данни, а е NoSQL база данни. Той е сравнително лесен за използване и има и доста готини функции - но ще разгледам подробно по-нататък в статията.

Както и да е, обратно на пътя:

Обикновено грешките, които хората правят през цялото време

  • използвайки realm.beginTransaction () и realm.commitTransaction () вместо realm.executeTransaction (Realm.Transaction)

Проблемът с това е, че ExecuteTransaction () автоматично се справя с извикване realm.cancelTransaction () в случай, че е изхвърлено изключение, докато другата алтернатива обикновено пренебрегва опитът за улов.

Да, трябва да се обадите за отмяна на транзакции, които няма да свършат.

Например на фонови нишки:

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

  • отваряне на Realm екземпляри с Realm.getDefaultInstance (), които те никога не затварят никъде

Проблемът с това е, че на фоновите нишки наличието на отворен екземпляр Realm, който не затваряте, дори когато изпълнението на нишката е приключило, е много скъпо и може да причини странни грешки. По този начин се препоръчва да затворите Realm във вашата фонова нишка, когато изпълнението се извърши накрая. Това включва IntentServices.

Също така трябва да помислите за затваряне на екземпляра Realm в своя потребителски интерфейс, когато вече нямате дейности, така че да можете да компактирате нешифрираното си царство при изход. Не можете да направите това, ако около вас има отворени екземпляри. (Лично аз имам един глобален екземпляр Realm за нишката на потребителския интерфейс и го затварям, когато няма отворени дейности).

Ето едно правило: ако видите код като Realm.getDefaultInstance (). Където (…) ще се счупи рано или късно.

(Също така, съвет: трябва да отворите първия си екземпляр на Realm в потребителския интерфейс само след създаването на първата активност, само за да сте сигурни от контекста.getFilesDir () == null).

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

  • извършване на много транзакции (например всеки елемент се добавя в нова транзакция, в for-loop) на неоткрити фонови нишки, които не се автоматизират

Това ще ви създаде проблеми в даден момент. Минимизирайте броя на транзакциите си за дадена фонова нишка.

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

  • злоупотреба с realmResults.asObservable () и неразбиране на поддръжката на Realm Rx като цяло

Повечето въпроси, свързани с Rx / Realm в Stack Overflow, са склонни да бъдат прекалено изкривени, тъй като има фундаментално недоразумение за това, което трябва да прави поддръжката на Realm за Rx.

Смисълът на realmResults.asObservable () е да видите целите резултати като наблюдателни, без да е необходимо да добавяте слушатели за промяна ръчно и да бъдете актуализирани (и да получавате известия в случай, че таблицата е променена) в нишката на потребителския интерфейс.

Ако изравнявате RealmResults, в крайна сметка ще се объркате. Исках да намеря случай за употреба, в който е необходимо, и още не съм го намерил. Използването на switchMap обаче има смисъл.

В случай че се чудите, трябва да отворите и затворите екземпляр Realm за вашите нишки на заден план, дори ако го използвате с Rx.

Ако наистина имате нужда от вашите RealmResults на фонова нишка като Наблюдаем (никога досега не ми се е случвало), тогава използвайте Observable.just (realm.where (…) .find * ()) (not async) вместо asObservable ().

(Забележка: Преди Realm 2.0.3+, трябва да помислите да използвате свой собствен RealmObservableFactory, тъй като е открит теч на памет.)

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

  • опити да се пагинира RealmResults или „ограничи броя на резултатите в него“ без никаква валидна причина

Виждал съм хора в SO да се опитват да пагинират RealmResulta или „как да направя заявка за ограничение“. Първият зададен въпрос е защо дори се опитвате да го направите.

Най-вече защото, за да ограничите RealmResults, просто трябва да не го индексирате над произволния си праг.

esseЗа да стане ясно: RealmResults НЕ съдържа елементи. Той съдържа средствата за оценка на резултатите от заявката. Елементът се получава от Realm само когато извиквате realmResults.get (i) и само този единствен елемент се връща наведнъж. Той е като курсор, с изключение на списък. Следователно „ограничаването“ и „пагинирането“ няма смисъл. Ако наистина имате нужда от него, тогава ограничете индекса си.

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

Ако видите findAll (). Sort (), той обикновено трябва да бъде sort (). FindAll (). Преди Realm-Java 5.0.0, тя беше преди findAllSorted (), така че потърсете това.

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

  • Не използвайте @Index пояснение за полетата, които използвате в заявката си, въпреки че трябва

Това прави вашите заявки като 4 пъти по-бързи, ако не и повече. Винаги трябва да използвате @Index, ако използвате поле в заявка.

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

  • Отваряне на екземпляр на Realm във фонова нишка, получаване на RealmResults, след това отваряне на транзакцията и след това манипулиране на резултатите от външната страна на транзакцията

Когато отворите транзакция, пишете директно в най-новата версия на Realm. Което означава, че вашите RealmResulta трябва да бъдат получени Вътре в транзакцията, а НЕ извън транзакцията; така че да виждате най-новата версия по всяко време.

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

  • Не използвайте RealmRecyclerViewAdapter и RecyclerView, въпреки че вече не е известно

Също така си струва да се отбележи, че техният адаптер (версия 3.0.0) управлява добавянето и премахването на слушател за промяна, който извиква правилния адаптер методи.notifyItem *, които позволяват анимация при промяна, така че буквално всичко, което трябва да направите, е да предоставите заявката, която искате да видите елементите на екрана и той просто работи и се актуализира автоматично, когато нещата се случват.

ListViews и AsyncTasks ме карат да изпитвам болка. : /

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

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

Това беше най-вече за често срещани грешки, но за какво е Realm във всеки случай?

Realm решава няколко проблема, които иначе биха отнели много водопровод, за да се справите сами, което е, което на първо място го прави доста готино.

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

  • Оценка на заявки с нулева копия

Което казах по-рано за RealmResults , който не съдържа никакви елементи. Получавате прокси класове, създадени само когато се обадите .get (i). Списъкът, който получавате, е просто фантастичен курсор към основната база данни. Самата оценка на RealmResults е доста евтина и ви дава лесен достъп до всички данни, които отговарят на условията ви, без да копирате целия набор от данни в паметта.

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

  • Разрешаване на запис от множество нишки (само една транзакция наведнъж) и автоматични актуализации на потребителския интерфейс за показване на най-новата версия на данните

Вашата фонова операция изтегли нова партида котки на фонова нишка. Сега трябва по някакъв начин да покажете тези нови котки върху старите котки във вашия RecyclerView. Да допуснем, че мразите AsyncTasks, защото обичате да въртите екрана.

Можете да изпратите новите вмъкнати елементи чрез шина за събития, да ги добавите към списъка си и след това да се обадите на adapter.notifyItemRangeInserted (...), но не би ли било чудесно, ако просто записахте данни на едно място, и данните бяха актуални навсякъде другаде, без ръчно водопровод изобщо?

Да, ако използвате RealmResults, върнати от вашето запитване, и го хвърлите в RealmRecyclerViewAdapter (което между другото не прави много магия във фонов режим), всяко писане, което правите на която и да е тема, автоматично ще актуализира набора от резултати, като винаги показва най-новото данни. Готино, а?

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

  • Слушане за асинхронно получени резултати от заявки без много усилия

Ако не използвате RealmRecyclerViewAdapter, все още можете да слушате всички промени, направени от всякакви нишки, като използвате RealmChangeListener, добавен към вашите RealmResults.

частно царство;
частни RealmResults  котки;
частен RealmChangeListener <...> realmChangeListener = котки -> {
    adapter.setData (котки);
};
@Override
защитена void onCreate (пакет запазенInstanceState) {
    super.onCreate (savedInstanceState);
    realm = Realm.getDefaultInstance ();
    котки = realm.where (Cat.class) .findAllAsync ();
    cats.addChangeListener (realmChangeListener);
}
@Override
защитена празнота onDestroy () {
    super.onDestroy ();
    cats.removeAllChangeListeners ();
    realm.close ();
}

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

Имайки това предвид, как трябва да се използва Realm?

  • Не използвайте startTransaction () и commitTransaction () ръчно. Използвайте ExecuteTransaction () (или ExecuteTransactionAsync () в нишката на потребителския интерфейс, ако е необходимо).
  • Използвайте RealmRecyclerViewAdapter с метода findAllSortedAsync (), за да разтоварите напълно заявката от потребителската нишка на потребителския интерфейс и автоматично да покажете всички нови елементи, ангажирани от фонови нишки (адаптерът на Realm се справя с вас с RealmChangeListener)
  • Не се изисква ръчно копиране и никаква синхронизация, просто дефинирайте заявката и как да свържете вашия притежател на изглед с обекта Realm и ще получите решение за използване на ниска памет с автоматично синхронизиране.

Ако все още не се отказвате от копирането на данните в паметта, можете да опитате с монархията.

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

Дано това ви е помогнало:

  • получете малко разбиране за това какво е Realm, за какво е Realm и как да работите с него
  • как да идентифицирате лоши публикации като тази (свръхкомплексно управление на потребителски инстанции на Realm, синхронни транзакции в нишката на потребителския интерфейс, използване на start / ангажиране вместо изпълнение, Низходящи затворени случаи на Realm, които ви казват да добавяте първоначални данни с помощта на миграция вместо InitiData (Realm .Transaction), използвайки методи, които вече не съществуват от 0.89.0 ... такива неща).

Преработих примерната книга, показана в тази публикация по-горе в това хранилище на Github.

За прост пример, който изтегля външни данни, вижте това хранилище на Github.

(между другото, с новата функция за плескане на Medium, ако сте харесали статията, всъщност можете да натиснете бутона „хлопка“ няколко пъти, за да изразите колко ви хареса!)