Whether you’re an experienced Android developer, or you’re just getting started, you will need to think about storing data on the device. Android offers several options including relational data storage on a SQLite Database, but the APIs and examples provided by Google look like the state-of-the-art JDBC programming you did back in 1995! In this talk, you’ll learn how to use RealmDB, an elegant Object Relational database alternative to SQLite, to make database development on Android fun, easy, and testable.
9. Simple Data Object
public class Lyric {
private long id;
private String lyricText;
// Getters / Setters...
// Will be used by the ArrayAdapter in the ListView
@Override
public String toString() {
return lyricText;
}
}
10. Activity - Lifecycle Events
public class NaeNaeActivity extends ListActivity {
private LyricDataSource datasource;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_naenae);
datasource = new LyricDataSource(this);
datasource.open();
List<Lyric> values = datasource.getAllLyrics();
// Use the SimpleCursorAdapter to show the
// elements in a ListView
ArrayAdapter<Lyric> adapter = new ArrayAdapter<Lyric>(this,
android.R.layout.simple_list_item_1, values);
setListAdapter(adapter);
}
@Override
protected void onResume() {
super.onResume();
datasource.open();
}
@Override
protected void onPause() {
super.onPause();
datasource.close();
}
...
}
The datasource gets
initialized and opened on
create and is then used to
get all of the lyrics and
placed into an array
adapter.
The datasource is opened
and closed on resume and
stop.
12. Custom SQLiteOpenHelper
public class MySQLiteHelper extends SQLiteOpenHelper {
public static final String TABLE_LYRICS = "lyrics";
public static final String COLUMN_ID = "_id";
public static final String COLUMN_LYRIC = "lyric";
private static final String DATABASE_NAME = "lyric.db";
private static final int DATABASE_VERSION = 1;
// Database creation sql statement
private static final String DATABASE_CREATE = "create table "
+ TABLE_LYRICS + "( " + COLUMN_ID
+ " integer primary key autoincrement, " + COLUMN_LYRIC
+ " text not null);";
public MySQLiteHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase database) {
database.execSQL(DATABASE_CREATE);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Log.w(MySQLiteHelper.class.getName(),
"Upgrading database from version " + oldVersion + " to "
+ newVersion + ", which will destroy all old data");
db.execSQL("DROP TABLE IF EXISTS " + TABLE_LYRICS);
onCreate(db);
}
}
13. Lyric Datasource
public class LyricDataSource {
// Database fields
private SQLiteDatabase database;
private MySQLiteHelper dbHelper;
private String[] allColumns = { MySQLiteHelper.COLUMN_ID,
MySQLiteHelper.COLUMN_LYRIC};
public LyricDataSource(Context context) {
dbHelper = new MySQLiteHelper(context);
}
public void open() throws SQLException {
database = dbHelper.getWritableDatabase();
}
public void close() {
dbHelper.close();
}
public List<Lyric> getAllLyrics() {
List<Lyric> lyrics = new ArrayList<Lyric>();
Cursor cursor = database.query(MySQLiteHelper.TABLE_LYRICS,
allColumns, null, null, null, null,
MySQLiteHelper.COLUMN_ID);
cursor.moveToFirst();
while (!cursor.isAfterLast()) {
Lyric lyric = cursorToLyric(cursor);
lyrics.add(lyric);
cursor.moveToNext();
}
// Make sure to close the cursor
cursor.close();
return lyrics;
}
public Lyric createLyric(String lyric) {
ContentValues values = new ContentValues();
values.put(MySQLiteHelper.COLUMN_LYRIC, lyric);
long insertId = database.insert(MySQLiteHelper.TABLE_LYRICS, null,
values);
Cursor cursor = database.query(MySQLiteHelper.TABLE_LYRICS,
allColumns, MySQLiteHelper.COLUMN_ID + " = " +
insertId, null,
null, null, null);
cursor.moveToFirst();
Lyric newLyric = cursorToLyric(cursor);
cursor.close();
return newLyric;
}
public void deleteLyric(Lyric lyric) {
long id = lyric.getId();
database.delete(MySQLiteHelper.TABLE_LYRICS,
MySQLiteHelper.COLUMN_ID
+ " = " + id, null);
}
public Lyric getLyricByValue(String lyricText) {
Lyric lyric = null;
Cursor cursor = database.query(MySQLiteHelper.TABLE_LYRICS,
allColumns, MySQLiteHelper.COLUMN_LYRIC + " = '" +
lyricText + "'" , null, null, null, null);
cursor.moveToFirst();
if (!cursor.isAfterLast()) {
lyric = cursorToLyric(cursor);
}
// Make sure to close the cursor
cursor.close();
return lyric;
}
private Lyric cursorToLyric(Cursor cursor) {
Lyric lyric = new Lyric();
lyric.setId(cursor.getLong(0));
lyric.setLyricText(cursor.getString(1));
return lyric;
}
14. Using SQLite Pros
• No additional dependencies
• Built in programatic transaction management
• Built in creation and upgrade event handling
15. What happens when you add a couple fields?
public class MySQLiteHelper extends SQLiteOpenHelper {
public static final String TABLE_LYRICS = "lyrics";
public static final String COLUMN_ID = "_id";
public static final String COLUMN_LYRIC = "lyric";
private static final String DATABASE_NAME = "lyric.db";
private static final int DATABASE_VERSION = 1;
// Database creation sql statement
private static final String DATABASE_CREATE = "create table "
+ TABLE_LYRICS + "( " + COLUMN_ID
+ " integer primary key autoincrement, " + COLUMN_LYRIC
+ " text not null);";
public MySQLiteHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase database) {
database.execSQL(DATABASE_CREATE);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Log.w(MySQLiteHelper.class.getName(),
"Upgrading database from version " + oldVersion + " to "
+ newVersion + ", which will destroy all old data");
db.execSQL("DROP TABLE IF EXISTS " + TABLE_LYRICS);
onCreate(db);
}
}
16. Update the data model
public class Lyric {
private long id;
private String lyricText;
private String source;
private boolean isChorus;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getLyricText() {
return lyricText;
}
public void setLyricText(String lyricText) {
this.lyricText = lyricText;
}
// Additional Getter/Setters
// Will be used by the ArrayAdapter in the ListView
@Override
public String toString() {
return lyricText;
}
}
17. Update the Datasource
public class LyricDataSource {
// Database fields
private SQLiteDatabase database;
private MySQLiteHelper dbHelper;
private String[] allColumns = { MySQLiteHelper.COLUMN_ID,
MySQLiteHelper.COLUMN_LYRIC,
MySQLiteHelper.COLUMN_SOURCE,
MySQLiteHelper.COLUMN_CHORUS};
public LyricDataSource(Context context) {
dbHelper = new MySQLiteHelper(context);
}
public void open() throws SQLException {
database = dbHelper.getWritableDatabase();
}
public void close() {
dbHelper.close();
}
public List<Lyric> getAllLyrics() {
List<Lyric> lyrics = new ArrayList<Lyric>();
Cursor cursor = database.query(MySQLiteHelper.TABLE_LYRICS,
allColumns, null, null, null, null,
MySQLiteHelper.COLUMN_ID);
cursor.moveToFirst();
while (!cursor.isAfterLast()) {
Lyric lyric = cursorToLyric(cursor);
lyrics.add(lyric);
cursor.moveToNext();
}
// Make sure to close the cursor
cursor.close();
return lyrics;
}
public Lyric createLyric(String lyric) {
ContentValues values = new ContentValues();
values.put(MySQLiteHelper.COLUMN_LYRIC, lyric);
values.put(MySQLiteHelper.COLUMN_CHORUS, isChorus ? 1 : 0);
values.put(MySQLiteHelper.COLUMN_SOURCE, source);
long insertId = database.insert(MySQLiteHelper.TABLE_LYRICS, null,
values);
Cursor cursor = database.query(MySQLiteHelper.TABLE_LYRICS,
allColumns, MySQLiteHelper.COLUMN_ID + " = " + insertId,
null,
null, null, null);
cursor.moveToFirst();
Lyric newLyric = cursorToLyric(cursor);
cursor.close();
return newLyric;
}
public void deleteLyric(Lyric lyric) {
long id = lyric.getId();
database.delete(MySQLiteHelper.TABLE_LYRICS, MySQLiteHelper.COLUMN_ID
+ " = " + id, null);
}
public Lyric getLyricByValue(String lyricText) {
Lyric lyric = null;
Cursor cursor = database.query(MySQLiteHelper.TABLE_LYRICS,
allColumns, MySQLiteHelper.COLUMN_LYRIC + " = '" +
lyricText + "'" , null, null, null, null);
cursor.moveToFirst();
if (!cursor.isAfterLast()) {
lyric = cursorToLyric(cursor);
}
// Make sure to close the cursor
cursor.close();
return lyric;
}
private Lyric cursorToLyric(Cursor cursor) {
Lyric lyric = new Lyric();
lyric.setId(cursor.getLong(0));
lyric.setLyricText(cursor.getString(1));
lyric.setChorus(cursor.getInt(2) == 1);
lyric.setSource(cursor.getString(3));
return lyric;
}
18. Add the columns to our SQLiteOpenHelper
public class MySQLiteHelper extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "lyric.db";
private static final int DATABASE_VERSION = 1;
public static final String COLUMN_ID = "_id";
public static final String TABLE_LYRICS = "lyrics";
public static final String COLUMN_LYRIC = "lyric";
public static final String COLUMN_SOURCE = "source";
public static final String COLUMN_CHORUS = "isChorus";
// Database creation sql statement
private static final String DATABASE_CREATE =
"create table "
+ TABLE_LYRICS + "( " + COLUMN_ID
+ " integer primary key autoincrement, “ + COLUMN_LYRIC + " text
not null, "
+ COLUMN_CHORUS + " text, "
+ COLUMN_SOURCE + " integer, "
+ " ); "
;
...
}
19. How about a second table
public class MySQLiteHelper extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "lyric.db";
private static final int DATABASE_VERSION = 1;
public static final String COLUMN_ID = "_id";
public static final String TABLE_LYRICS = "lyrics";
public static final String COLUMN_LYRIC = "lyric";
public static final String COLUMN_SOURCE = "source";
public static final String COLUMN_CHORUS = "isChorus";
public static final String TABLE_ARTISTS = "artists";
public static final String COLUMN_ARTIST_NAME = "name";
public static final String COLUMN_ARTIST_LABEL = "label";
// Database creation sql statement
private static final String DATABASE_CREATE =
"create table "
+ TABLE_LYRICS + "( " + COLUMN_ID + " integer primary key autoincrement, "
+ COLUMN_LYRIC + " text not null, "
+ COLUMN_CHORUS + " text, "
+ COLUMN_SOURCE + " integer "
+ " ); "
+
"create table "
+ TABLE_ARTISTS + "( " + COLUMN_ID + " integer primary key autoincrement, "
+ COLUMN_ARTIST_NAME + " text not null, "
+ COLUMN_ARTIST_LABEL + " text "
+ " ); "
;
...
}
20. Or a many to many relationship
public class MySQLiteHelper extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "lyric.db";
private static final int DATABASE_VERSION = 1;
public static final String COLUMN_ID = "_id";
public static final String TABLE_LYRICS = "lyrics";
public static final String COLUMN_LYRIC = "lyric";
public static final String COLUMN_SOURCE = "source";
public static final String COLUMN_CHORUS = "isChorus";
public static final String TABLE_ARTISTS = "artists";
public static final String COLUMN_ARTIST_NAME = "name";
public static final String COLUMN_ARTIST_LABEL = “label";
public static final String TABLE_LYRIC_ARTIST = "lyric_artist";
public static final String COLUMN_LYRIC_ID = "lyric_id";
public static final String COLUMN_ARTIST_ID = "artist_id";
// Database creation sql statement
private static final String DATABASE_CREATE =
"create table "
+ TABLE_LYRICS + "( " + COLUMN_ID + " integer primary key autoincrement, "
+ COLUMN_LYRIC + " text not null, "
+ COLUMN_CHORUS + " text, "
+ COLUMN_SOURCE + " integer "
+ " ); "
+
"create table "
+ TABLE_ARTISTS + "( " + COLUMN_ID + " integer primary key autoincrement, "
+ COLUMN_ARTIST_NAME + " text not null, "
+ COLUMN_ARTIST_LABEL + " text "
+ " ); “
+
"create table "
+ TABLE_LYRIC_ARTIST + "( "
+ COLUMN_LYRIC_ID + " integer, "
+ COLUMN_ARTIST_ID + " integer, "
+ " FOREIGN KEY(" + COLUMN_ARTIST_ID + ") REFERENCES " + TABLE_ARTISTS + "(" + COLUMN_ID + "), "
+ " FOREIGN KEY(" + COLUMN_LYRIC_ID + ") REFERENCES " + TABLE_LYRICS + "(" + COLUMN_ID + ") "
+ " ); "
;
...
}
21. Using SQLite Cons
• Queries / Updates are very tedious
• Changes become cumbersome
• Relationships between an object graph must be manually
managed
23. About Realm
• Realm is a Mobile Database:
• A replacement for Core Data or SQLite
• Feels like an Object Relational Mapping tool
• Free & Open Source Apache 2.0
• Comparable Performance to SQLite
28. Realm Objects Look like Regular Objects…
public class Dog extends RealmObject {
private String name;
private int age;
@ignored private int dontPersistMe;
// + Standard setters and getters here
}
@RealmClass
public class Person implements RealmModel {
private String name;
private RealmList<Dog> dogs;
}
29. Easy to add / relate objects
try(Realm realm = Realm.getInstance(this.getContext())) {
realm.beginTransaction();
Dog dog = realm.createObject(Dog.class);
dog.setName("Rex");
dog.setAge(3);
Person person = realm.createObject(Person.class);
person.setName("Tim");
person.getDogs().add(dog);
realm.commitTransaction();
}
30. Updates must be wrapped in transactions
try(Realm realm = Realm.getInstance(this.getContext())) {
realm.beginTransaction();
Dog dog = realm.createObject(Dog.class);
dog.setName("Rex");
dog.setAge(3);
Person person = realm.createObject(Person.class);
person.setName("Tim");
person.getDogs().add(dog);
realm.commitTransaction();
}
31. Updates must be wrapped in transactions
try(Realm realm = Realm.getInstance(this.getContext())) {
realm.beginTransaction();
Dog dog = realm.createObject(Dog.class);
dog.setName("Rex");
dog.setAge(3);
Person person = realm.createObject(Person.class);
person.setName("Tim");
person.getDogs().add(dog);
realm.commitTransaction();
}
If an exception occurs, you must catch it
and rollback with realm.cancelTransaction( );
32. 1 to Many
try(Realm realm = Realm.getInstance(this.getContext())) {
realm.beginTransaction();
Dog dog = realm.createObject(Dog.class);
dog.setName("Rex");
dog.setAge(3);
Person person = realm.createObject(Person.class);
person.setName("Tim");
person.getDogs().add(dog);
realm.commitTransaction();
}
1 to Many
33. Many to Many
try(Realm realm = Realm.getInstance(this.getContext())) {
realm.beginTransaction();
Dog rex = realm.createObject(Dog.class);
rex.setName("Rex");
rex.setAge(3);
Dog spot = realm.createObject(Dog.class);
spot.setName("Spot");
spot.setAge(4);
Person tim = realm.createObject(Person.class);
tim.setName(“Tim");
tim.getDogs().addAll(Arrays.asList(spot, rex));
Person mary = realm.createObject(Person.class);
mary.setName("Mary");
mary.getDogs().addAll(Arrays.asList(spot, rex));
rex.getOwners().addAll(Arrays.asList(tim, mary));
spot.getOwners().addAll(Arrays.asList(tim, mary));
realm.commitTransaction();
}
Many to Many
34. Querying
// Queries uses Builder pattern to build up the query conditions
// For this example, assume Dog has a property “Person owner”
RealmResults<Dog> results = realm.where(Dog.class)
.greaterThan("age", 8)
.equalTo("owner.name", "Tim")
.findAll();
// Queries are chainable
RealmResults<Dog> allRex = results.where()
.contains("name", "rex")
.findAll();
// Or they can be combined
RealmResults<Dog> result2 = realm.where(Dog.class)
.greaterThan("age", 8)
.equalTo(“owner.name", "Tim")
.contains("name", "rex")
.findAll();
35. More Queries
// Logical Query Grouping
boolean checkCase = false;
RealmResults<Dog> r = realm.where(Dog.class)
.greaterThan("age", 8) //implicit AND
.beginGroup()
.equalTo("name", “CuJo”, checkCase)
.or()
.contains("name", "rex")
.endGroup()
.findAll();
// Sorting
RealmResults<Dog> result = r.where(Dog.class).findAll();
result.sort("age"); // Sort ascending
result.sort("age", RealmResults.SORT_ORDER_DESCENDING);
// Result Aggregation
long sum = result.sum("age").longValue();
long min = result.min("age").longValue();
long max = result.max("age").longValue();
double average = result.average("age");
long matches = result.size();
38. Realm Annotations
• @Required - Not Null
• @Ignore - Transient
• @PrimaryKey - string or integer (short, int or long)
• @Index - Implicitly set by Primary Key
40. Realm Installation
1. Add a classpath dependency to the project level build.grade file
buildscript {
repositories {
jcenter()
}
dependencies {
classpath "io.realm:realm-gradle-plugin:2.1.1"
}
}
2. Apply the realm-android plugin at the app module level build.grade file
apply plugin: 'realm-android'
41. Custom SQLiteOpenHelper Goes Away!
public class MySQLiteHelper extends SQLiteOpenHelper {
public static final String TABLE_LYRICS = "lyrics";
public static final String COLUMN_ID = "_id";
public static final String COLUMN_LYRIC = "lyric";
private static final String DATABASE_NAME = "lyric.db";
private static final int DATABASE_VERSION = 1;
// Database creation sql statement
private static final String DATABASE_CREATE = "create table "
+ TABLE_LYRICS + "( " + COLUMN_ID
+ " integer primary key autoincrement, " + COLUMN_LYRIC
+ " text not null);";
public MySQLiteHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase database) {
database.execSQL(DATABASE_CREATE);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Log.w(MySQLiteHelper.class.getName(),
"Upgrading database from version " + oldVersion + " to "
+ newVersion + ", which will destroy all old data");
db.execSQL("DROP TABLE IF EXISTS " + TABLE_LYRICS);
onCreate(db);
}
}
42. public class Lyric extends RealmObject {
private int sortKey;
@PrimaryKey private String id;
@Required private String lyricText;
// Getters / Setters...
// Will be used by the ArrayAdapter in the ListView
@Override
public String toString() {
return lyricText;
}
}
Simple Data Object - Still Simple
RealmObject
and
Annotations
43. Lyric Datasource
public class LyricDataSource {
// Database fields
private SQLiteDatabase database;
private MySQLiteHelper dbHelper;
private String[] allColumns = { MySQLiteHelper.COLUMN_ID,
MySQLiteHelper.COLUMN_LYRIC};
public LyricDataSource(Context context) {
dbHelper = new MySQLiteHelper(context);
}
public void open() throws SQLException {
database = dbHelper.getWritableDatabase();
}
public void close() {
dbHelper.close();
}
public List<Lyric> getAllLyrics() {
List<Lyric> lyrics = new ArrayList<Lyric>();
Cursor cursor = database.query(MySQLiteHelper.TABLE_LYRICS,
allColumns, null, null, null, null,
MySQLiteHelper.COLUMN_ID);
cursor.moveToFirst();
while (!cursor.isAfterLast()) {
Lyric lyric = cursorToLyric(cursor);
lyrics.add(lyric);
cursor.moveToNext();
}
// Make sure to close the cursor
cursor.close();
return lyrics;
}
public Lyric createLyric(String lyric) {
ContentValues values = new ContentValues();
values.put(MySQLiteHelper.COLUMN_LYRIC, lyric);
long insertId = database.insert(MySQLiteHelper.TABLE_LYRICS, null,
values);
Cursor cursor = database.query(MySQLiteHelper.TABLE_LYRICS,
allColumns, MySQLiteHelper.COLUMN_ID + " = " + insertId,
null,
null, null, null);
cursor.moveToFirst();
Lyric newLyric = cursorToLyric(cursor);
cursor.close();
return newLyric;
}
public void deleteLyric(Lyric lyric) {
long id = lyric.getId();
System.out.println("Lyric deleted with id: " + id);
database.delete(MySQLiteHelper.TABLE_LYRICS, MySQLiteHelper.COLUMN_ID
+ " = " + id, null);
}
public Lyric getLyricByValue(String lyricText) {
Lyric lyric = null;
Cursor cursor = database.query(MySQLiteHelper.TABLE_LYRICS,
allColumns, MySQLiteHelper.COLUMN_LYRIC + " = '" +
lyricText + "'" , null, null, null, null);
cursor.moveToFirst();
if (!cursor.isAfterLast()) {
lyric = cursorToLyric(cursor);
}
// Make sure to close the cursor
cursor.close();
return lyric;
}
private Lyric cursorToLyric(Cursor cursor) {
Lyric lyric = new Lyric();
lyric.setId(cursor.getLong(0));
lyric.setLyricText(cursor.getString(1));
return lyric;
}
44. Lyric Datasource
private Realm r;
public LyricDataSource(Realm realm) {
r = realm;
}
public void deleteAllLyrics() {
r.executeTransaction(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
realm.delete(Lyric.class);
}
});
}
public Lyric getLyricByValue(String lyricText) {
return r.where(Lyric.class)
.equalTo("lyricText", lyricText)
.findFirst();
}
public List<Lyric> getAllLyrics() {
return r.where(Lyric.class)
.findAllSorted("sortKey");
}
public Lyric createLyric(String lyricString) {
final Lyric lyric = new Lyric();
lyric.setId(UUID.randomUUID().toString());
lyric.setLyricText(lyricString);
lyric.setSortKey(System.currentTimeMillis());
r.executeTransaction(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
lyric = realm.copyToRealm(lyric);
}
});
return lyric;
}
public void deleteLyric(Lyric lyric) {
r.executeTransaction(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
realm.where(Lyric.class)
.equalTo("id", lyric.getId())
.findAll()
.deleteAllFromRealm();
}
});
}
45. Application / Activity - Lifecycle Events
public class TestDatabaseActivity extends ListActivity {
private LyricDataSource datasource;
private Realm realm;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
realm = Realm.getDefaultInstance();
datasource = new LyricDataSource(realm);
List<Lyric> values = datasource.getAllLyrics();
// Use the SimpleCursorAdapter to show the
// elements in a ListView
ArrayAdapter<Lyric> adapter =
new ArrayAdapter<Lyric>(this,
android.R.layout.simple_list_item_1,
values);
setListAdapter(adapter);
}
@Override
protected void onDestroy() {
super.onDestory();
realm.close();
}
...
}
public class NaeNaeApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
Realm.init(this);
RealmConfiguration realmConfiguration =
new RealmConfiguration.Builder()
.deleteRealmIfMigrationNeeded()
.build();
Realm.setDefaultConfiguration(realmConfiguration);
}
}
46. Application / Activity - Lifecycle Events
public class TestDatabaseActivity extends ListActivity {
private LyricDataSource datasource;
private Realm realm;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
realm = Realm.getDefaultInstance();
datasource = new LyricDataSource(realm);
List<Lyric> values = datasource.getAllLyrics();
// Use the SimpleCursorAdapter to show the
// elements in a ListView
ArrayAdapter<Lyric> adapter =
new ArrayAdapter<Lyric>(this,
android.R.layout.simple_list_item_1,
values);
setListAdapter(adapter);
}
@Override
protected void onDestroy() {
super.onDestory();
realm.close();
}
...
}
Keep reference
Get new instance of
realm datasource
on create and pass
to the datasource to
use.
1
public class NaeNaeApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
Realm.init(this);
RealmConfiguration realmConfiguration =
new RealmConfiguration.Builder()
.deleteRealmIfMigrationNeeded()
.build();
Realm.setDefaultConfiguration(realmConfiguration);
}
}
47. Application / Activity - Lifecycle Events
public class TestDatabaseActivity extends ListActivity {
private LyricDataSource datasource;
private Realm realm;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
realm = Realm.getDefaultInstance();
datasource = new LyricDataSource(realm);
List<Lyric> values = datasource.getAllLyrics();
// Use the SimpleCursorAdapter to show the
// elements in a ListView
ArrayAdapter<Lyric> adapter =
new ArrayAdapter<Lyric>(this,
android.R.layout.simple_list_item_1,
values);
setListAdapter(adapter);
}
@Override
protected void onDestroy() {
super.onDestory();
realm.close();
}
...
}
Keep reference
Get new instance of
realm datasource
on create and pass
to the datasource to
use.
For
getDefaultInstance
to work, we must
set the default
configuration once
1
2
public class NaeNaeApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
Realm.init(this);
RealmConfiguration realmConfiguration =
new RealmConfiguration.Builder()
.deleteRealmIfMigrationNeeded()
.build();
Realm.setDefaultConfiguration(realmConfiguration);
}
}
48. Application / Activity - Lifecycle Events
public class TestDatabaseActivity extends ListActivity {
private LyricDataSource datasource;
private Realm realm;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
realm = Realm.getDefaultInstance();
datasource = new LyricDataSource(realm);
List<Lyric> values = datasource.getAllLyrics();
// Use the SimpleCursorAdapter to show the
// elements in a ListView
ArrayAdapter<Lyric> adapter =
new ArrayAdapter<Lyric>(this,
android.R.layout.simple_list_item_1,
values);
setListAdapter(adapter);
}
@Override
protected void onDestroy() {
super.onDestory();
realm.close();
}
...
} Close on destroy
Keep reference
Get new instance of
realm datasource
on create and pass
to the datasource to
use.
For
getDefaultInstance
to work, we must
set the default
configuration once
1
2
3
public class NaeNaeApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
Realm.init(this);
RealmConfiguration realmConfiguration =
new RealmConfiguration.Builder()
.deleteRealmIfMigrationNeeded()
.build();
Realm.setDefaultConfiguration(realmConfiguration);
}
}
51. Change Listeners
public class MyActivity extends Activity {
private Realm realm;
private RealmChangeListener<RealmResults<Lyric>> listener;
private RealmResults<Lyric> lyrics;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
realm = Realm.getDefaultInstance();
lyrics = realm.where(Lyric.class).contains("lyricText", "Bop").findAll();
listener = new RealmChangeListener<RealmResults<Lyric>>() {
@Override
public void onChange(RealmResults<Lyric> updatedResults) {
// Do something with the updated results, actually the results here
// match the ones stored as a reference in this activity.
}
};
lyrics.addChangeListener(listener);
}
@Override
protected void onDestroy() {
super.onDestroy();
realm.removeAllChangeListeners();
realm.close();
}
}
52. Async Operations
// Async with Callback
private RealmChangeListener callback = new RealmChangeListener() {
@Override
public void onChange() { // called once the query complete and on every update
// use the result
}
};
public void onStart() {
try(Realm r = Realm.getInstance(context)) {
RealmResults<Lyric> result = r.where(Lyric.class).findAllAsync();
result.addChangeListener(callback);
}
}
// Or you can block (be careful)
while (!result.isLoaded()) {
// Results are now available
}
// Or to block until async results are loaded
result.load()
53. Transaction Blocks & Async Transactions
// Transaction will be committed after block unless exception occurs.
realm.executeTransaction(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
Lyric user = realm.createObject(Lyric.class);
user.setId(UUID.randomUUID().toString());
user.setLyricText("Let's get it started!");
}
});
// Asynchronous Transaction
realm.executeTransactionAsync(new Realm.Transaction() {
@Override
public void execute(Realm bgRealm) {
Lyric user = bgRealm.createObject(Lyric.class);
user.setId(UUID.randomUUID().toString());
user.setLyricText("Let's get it started in here");
}
}, new Realm.Transaction.OnSuccess() {
@Override
public void onSuccess() {
// Do something on the thread you were on.
}
}, new Realm.Transaction.OnError() {
@Override
public void onError(Throwable error) {
// Transaction failed and was automatically canceled.
// Do something on the thread you were on.
}
});
54. Insert JSON Directly
// Insert from a string
r.beginTransaction();
r.createObjectFromJson(Lyric.class,
"{ lyricText: "Bidi-bidi-bop", " +
"id: "864dee8a-d9ac-4e5a-a843-e051ad2d6e8a" }");
r.commitTransaction();
// Insert multiple items using a InputStream
InputStream is = new FileInputStream(new File("path_to_file"));
r.beginTransaction();
try {
r.createAllFromJson(Lyric.class, is);
r.commitTransaction();
} catch (IOException e) {
r.cancelTransaction();
}
57. ListAdapter & Recycler Adapters
dependencies {
compile ‘io.realm:android-adapters:1.4.0'
}
MyRecyclerViewAdapter extends RealmRecyclerViewAdapter<Promotion, MyRecyclerViewAdapter.ViewHolder> {
private final MyActivity activity;
public RealmRecyclerViewAdapter(@NonNull MyActivity activity, @Nullable OrderedRealmCollection<Lyric> lyrics)
{
super(promoListActivity, lyrics, true);
this.activity = activity;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.lyric_list_item, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(final ViewHolder holder, int position) {
Lyric lyric = getData().get(position);
holder.lyrcTextView.setText(lyric.getLyricText());
holder.lyricId = lyric.getId());
}
public class ViewHolder extends RecyclerView.ViewHolder {
public TextView titleView;
public String promotionId;
public ViewHolder(View view) {
super(view);
lyricTextView = (TextView) view.findViewById(R.id.lyric_itle);
}
}
58. Encryption
RealmConfiguration config = new RealmConfiguration.Builder(context)
.encryptionKey(getKey())
.build();
• The Realm file can be stored encrypted on disk by
passing an encryption key (byte [])
• All data persisted to disk is transparently encrypted and
decrypted with standard AES-256 encryption
59. Migrations
RealmConfiguration config = new RealmConfiguration.Builder(context)
.schemaVersion(2) // Must be bumped when the schema changes
.migration(new MyMigration()) // Migration handler class
.build();
• Bump the version with each change
• Point the RealmConfiguration to you custom
RealmMigration Implementation
60. Migrations
RealmConfiguration config = new RealmConfiguration.Builder(context)
.schemaVersion(2) // Must be bumped when the schema changes
.migration(new MyMigration()) // Migration handler class
.build();
public class MyMigration implements RealmMigration {
public void migrate(DynamicRealm realm, long oldVersion, long newVersion) {
RealmSchema schema = realm.getSchema(); // DynamicRealm exposes an editable schema
if (oldVersion == 0) { // Migrate to version 1: Add a new class
schema.create("Person")
.addField("name", String.class)
.addField("age", int.class);
oldVersion++; // Bump the version after each updgrade
}
if (oldVersion == 1) { // Migrate to version 2: Add PK + relationships
schema.get("Person")
.addField("id", long.class, FieldAttribute.PRIMARY_KEY)
.addRealmObjectField("favoriteDog", schema.get("Dog"))
.addRealmListField("dogs", schema.get("Dog"));
oldVersion++;
}
}
}
61. Migrations
public class MyMigration implements RealmMigration {
public void migrate(DynamicRealm realm, long oldVersion, long newVersion) {
RealmSchema schema = realm.getSchema(); // DynamicRealm exposes an editable schema
if (oldVersion == 0) { // Migrate to version 1: Add a new class
schema.create("Person")
.addField("name", String.class)
.addField("age", int.class);
oldVersion++; // Bump the version after each updgrade
}
if (oldVersion == 1) { // Migrate to version 2: Add PK + relationships
schema.get("Person")
.addField("id", long.class, FieldAttribute.PRIMARY_KEY)
.addRealmObjectField("favoriteDog", schema.get("Dog"))
.addRealmListField("dogs", schema.get("Dog"));
oldVersion++;
}
}
}