Что такое Realm?
Изучая хранение данных в БД по урокам на нашем канале, вы могли познакомиться с БД SQLite. SQLite традиционно используется в Android для хранения самых разных типов данных. SQLite демонстрирует надежность, гибкость, она имеет хорошую документацию и много примеров. Но относительно недавно стала популярной новая Бд Realm, разработчики которой позиционируют ее как полноценную замену SQLite. Realm имеет собственное ядро, написанное на C++. Не волнуйтесь, для работы с базой данных Realm учить С++ совсем не обязательно. Таким образом, что очень важно, Realm не использует SQLite. Если вы используете ORMLite или Sugar ORM, которую мы рассматривали в этом видео, ваши данные все равно хранятся в базе данных SQLite. Эти библиотеки дают нам только надстройку над SQLite для более удобной работы с БД. При использовании Realm, SQLite не используется вообще. При этом Realm работает очень быстро, так что зачастую можно даже сохранять данные в UI потоке.
Основные преимущества Realm:
- до 10 раз быстрее, чем SQLite
- проста в использовании
- удобно для создания и хранения данных на лету
- хорошая документация и поддержка
Простой пример
Рассмотрим простой пример, который демонстрирует, как работает база данных Realm и как ее использовать. Скачайте пример кода из хранилища GitHub и импортируйте проект в Android Studio.
Gradle
За добавление Realm в проект отвечает такая строка в разделе зависимостей файла сборки build.gradle.
dependencies { compile 'io.realm:realm-android:0.87.4' //other dependencies }
Рассмотрим код
Приложение состоит из одного активити (контейнера) и двух фрагментов.
Первый фрагмент позволяет нам добавить книгу, которая хранится в базе данных (MyEditionFragment), а второй показывает список всех книг из базы данных (MyListFragment).
Начнем с файлов разметки.
Разметка главного экрана – здесь контейнеры для фрагментов.
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:orientation="vertical" tools:context=".ui.MyMainActivity"> <FrameLayout android:id="@+id/edition_container" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_weight="1"/> <FrameLayout android:id="@+id/list_container" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_weight="1"/> </LinearLayout>
Файл разметки фрагмента MyEditionFragment содержит поле ввода текста и две кнопки – добавить и удалить.
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/light_green"> <EditText android:id="@+id/edit_title" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="Write a title"/> <Button android:id="@+id/button_add" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/add"/> <Button android:id="@+id/button_remove" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/remove"/> </LinearLayout>
В разметке MyListFragment компонент RecyclerView для отображения списка.
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/light_yellow"> <android.support.v7.widget.RecyclerView android:id="@+id/recycler_view" android:layout_width="match_parent" android:layout_height="match_parent" android:scrollbars="vertical"/> </LinearLayout>
item_book.xml содержит описание пункта списка.
<?xml version="1.0" encoding="utf-8"?> <TextView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/text_title" android:layout_width="match_parent" android:layout_height="wrap_content"/>
В активити мы создаем фрагменты и размещаем их на главном экране.
RealmObject
Для использования объекта в качестве объекта Realm вы должны просто расширить класс RealmObject.
Аннотация @Required означает , что заголовок не может быть пустым.
public class MyBook extends RealmObject { @Required private String title; public String getTitle() { return title; } public void setTitle(final String title) { this.title = title; } }
Как видите, класс MyBook для простоты содержит только одно поле title с геттером и сеттером, в реальности их может быть гораздо больше.
Операции CRUD
Все стандартные операции с БД, такие как создание, чтение, обновление и удаление записей выполняются с экземпляром класса Realm. Это можно сделать несколькими способами, которые подробно описаны в документации realm, найти ее можно по ссылке в описании видео.
В классе MyEditionFragment
private Realm mRealm; @Override public void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); mRealm = Realm.getInstance(getContext()); }
- Выполняем операции с базой данных. Например добавить или удалить книгу по нажатию соответствующей кнопки.
@OnClick(R.id.button_add) public void onAddClick() { mRealm.beginTransaction(); MyBook book = mRealm.createObject(MyBook.class); book.setTitle(getTrimmedTitle()); mRealm.commitTransaction(); } @OnClick(R.id.button_remove) public void onRemoveClick() { mRealm.beginTransaction(); RealmResults<MyBook> books = mRealm.where(MyBook.class).equalTo("title", getTrimmedTitle()).findAll(); if(!books.isEmpty()) { for(int i = books.size() - 1; i >= 0; i--) { books.get(i).removeFromRealm(); } } mRealm.commitTransaction(); } private String getTrimmedTitle() { return mEditTitle.getText().toString().trim(); }
Каждая операция с базой данных , кроме операции чтения, должна начинаться с выполнения метода Realm.beginTransaction () и заканчиваться выполнением метода Realm.commitTransaction (). Это гарантирует, что наши данные всегда будут находиться в согласованном состоянии, и обеспечивает безопасность потока. Если вы забыли зафиксировать ваши изменения методом Realm.commitTransaction (), они не будут сохранены. Вы также можете отменить транзакцию вызовом метода Realm.cancelTransaction ().
- В соответствии с документацией Realm, необходимо закрыть все экземпляры Realm методом Realm.close(), когда мы закончили работу с ними.
@Override public void onDestroy() { super.onDestroy(); mRealm.close(); }
Обратите внимание, для привязки кнопок к методам обработки нажатий мы используем аннотации @OnClick.
Такую возможность нам обеспечивает библиотека Butter Knife, которая используется для быстрой привязки вьюшек. Я ее как-нибудь рассмотрю в одном из выпусков “инструментов разработчика”, а сейчас вернемся к Realm.
Автообновление
Приведенный пример показывает, как добавить или удалить какой-либо объект в Realm, но реальная магия происходит в классе MyListFragment, который работает со списком книг.
Нужно обновлять наш список каждый раз, когда книга добавляется в базу данных или удаляется из нее. Для этого нужно:
- Получить экземпляр Realm в методе onCreate () фрагмента и закрыть его в методе onDestroy () .
- Передать все книги адаптеру.
@Override public void onViewCreated(final View view, @Nullable final Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext())); mRecyclerView.setAdapter(new MyListAdapter(mRealm.allObjects(MyBook.class))); }
Запрос Realm.allObjects (ClassName.class) возвращает объекты RealmResults. Важно понимать , что данные в Realm никогда не копируются. Мы получаем список ссылок и работаем над оригинальными объектами.
Теперь посмотрим код адаптера списка MyListAdapter
Здесь мы добавляем слушатель RealmChangeListener к RealmResults .
public MyListAdapter(RealmResults<MyBook> books) { mBooks = books; mBooks.addChangeListener(this); } @Override public void onChange() { notifyDataSetChanged(); }
И теперь, если сли мы добавим новую книгу в MyEditionFragment, список в MyListFragment будет обновляться автоматически. Все , что мы сделали, это добавили слушатель , который сообщит нам, если набор данных изменился. Это все, что нам нужно. Метод OnChange () не имеет объекта (или списка объектов) , которые изменены, потому что нам не нужна эта информация. Она у нас уже есть. Как уже было сказано ранее, данные Realm никогда не копируются. Поэтому , когда новая книга добавляется в MyEditionFragment , адаптер списка будет проинформирован, что набор книг был изменен , поскольку объект mBooks автомагически обновляется Realm. Все , что нам остается сделать, это обновить представление списка.
Возможные проблемы
Венгерская нотация
Если вы используете префикс ‘m’ для полей ваших классов, следует помнить ,что геттеры и сеттеры для них должны также иметь символ ‘m’ в именах. Подробнее.
Схема миграции
Во время разработки баз данных схемы все время меняются. Если у вас есть какие – то данные , хранящиеся в базе данных Realm, любые будущие изменения схемы приведут к проблемам миграции. Вы можете либо обрабатывать изменения миграции, либо удалить файл .realm со всеми данными, если Realm обнаруживает проблемы миграции.
RealmConfiguration config = new RealmConfiguration.Builder(context) .deleteRealmIfMigrationNeeded() .build()
Миграция из SQLite
Если у вас есть в настоящее время есть приложение, которое использует SQLite и вы хотите мигрировать в Realm, это потребует достаточно труда. SQLite хранит данные в упорядоченной форме, которая не обязательно будет работать лучше всего с Realm. Лучше пересмотреть свою схему данных и смоделировать их в качестве объектов.
После того, как вы измените схему, для адаптации к Realm, будет гораздо легче переносить любые данные из SQLite. Просто настройте миграцию (начиная с версии от 0 до 1 из базы данных Realm), и в этой миграции, загружайте данные SQLite в объекты Realm, а затем просто сохранить их.
Если ваши данные также находится на удаленном сервере, вы можете просто создать базу данных Realm с нуля.
Перед тем, как остановить ваш выбор на Realm, имейте в виду, что это передовое программное обеспечение, с API, которые может иметь критические изменения в будущих версиях.
Проект можно найти на DroidsOnRoids Github в хранилище ExampleRealm-Android .
А более тесную работу с Realm мы продемонстрируем в продвинутом курсе по разработке приложения для группы вконтакте, который выйдет уже совсем скоро.
А уже есть приложение для группы ВКонтакте? Или это всё по-прежнему в планах?..
Приложение в финальной стадии разработки. Этой весной закончим и выпустим курс по нему.