Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Realm - Droidcon 2016

Donn Felker
September 30, 2016

Realm - Droidcon 2016

So you need to store data in your mobile application? Great, now you need to work with SQLite. Writing SQL is great fun if you enjoy thinking about mapping your objects to a relational store over and over and over. But what if there was another solution? One that allowed you to work with objects and store them as such with a powerful query system. No transformations back and forth to a relational store. Well, you’re in luck, one does exist: Realm. Realm is a mobile object (MVCC) database that can do all of these things and more. In this session learn how you can rid yourself of SQL, SQLite and its binding chains so you can harness the power and speed of working with native objects in Realm.

Donn Felker

September 30, 2016
Tweet

More Decks by Donn Felker

Other Decks in Technology

Transcript

  1. ABOUT ME Android GDE Independent Consultant at American Express Caster.IO

    - A bite-sized video training for Android Devs Written a few books on Android. Co-Host of the Fragmented Podcast 2 apps in the top free category on Google Play for 5+ yrs the twitters - @donnfelker
  2. STORING DATA IN REALM IS EASY // Define your model

    class by extending the RealmObject or // annotating with the @RealmClass annotation public class Dog extends RealmObject { @PrimaryKey private int id; @Required // Name cannot be null private String name; private int age; // ... Generated getters and setters ... }
  3. SAVING OBJECTS IN REALM // Use them like regular java

    objects Dog dog = new Dog(); dog.setName("Rex"); dog.setAge("1"); // Get a Realm instance Realm realm = Realm.getDefaultInstance(); // Persist your data easily realm.beginTransaction(); realm.copyToRealm(dog); realm.commitTransaction();
  4. SAVING MOAR OBJECTS! // Get a Realm instance Realm realm

    = Realm.getDefaultInstance(); // Create and persist your data easily realm.beginTransaction(); Dog dog = realm.createObject(Dog.class); dog.setName("Rex"); dog.setAge("1"); realm.commitTransaction();
  5. EASY PEASY TRANSACTIONS try { realm.beginTransaction(); Dog dog = realm.where(Dog.class).equalTo("name",

    "Fido").findFirst(); dog.setAge(15); realm.commitTransaction(); } catch (Exception ex) { // rollback realm.cancelTransaction(); }
  6. TRANSACTIONS - RECOMMENDED APPROACH // Will automatically handle begin/commit, and

    cancel if an error happens. realm.executeTransaction(new Realm.Transaction() { @Override public void execute(Realm realm) { Dog dog = realm.where(Dog.class).equalTo("name", "Fido").findFirst(); dog.setAge(15); } })
  7. ALL YOUR DATA BELONG TO US Realm realm = Realm.getDefaultInstance();

    // Query Realm for all dogs less than 2 years old RealmResults<Dog> puppies = realm.where(Dog.class) .lessThan("age", 2) .findAll(); puppies.size(); // => 1
  8. THE FLUENT QUERY INTERFACE Realm realm = Realm.getDefaultInstance(); // Find

    all dogs who are named "Fido" or "Snoopy" RealmResults<Dog> puppies = realm.where(Dog.class) .equalTo("name", "Fido") .or() .equalTo("name", "Snoopy") .findAll();
  9. MANY-TO-ONE public class Person extends RealmObject { private String firstName;

    private String lastName; private Dog dog; // .. getters/setters }
  10. SAVING A MANY-TO-ONE RELATIONSHIP // ... create a person Dog

    dog = Realm.createObject(Dog.class); dog.setAge(2); person.setDog(dog); // save with a transaction. All done.
  11. ONE-TO-MANY / MANY-TO-MANY // Define your relationships with RealmList public

    class Person extends RealmObject { private String firstName; private String lastName; private RealmList<Dog> dogs; // ... Generated getters and setters ... }
  12. RECURSIVE RELATIONSHIPS public class Person extends RealmObject { private String

    firstName; private String lastName; private RealmList<Person> friends; // .. getters/setters }
  13. RELATIONSHIP QUERIES (LINK QUERIES) public class Person extends RealmObject {

    private String id; private String name; private RealmList<Dog> dogs; // getters and setters } public class Dog extends RealmObject { private String id; private String name; private String color; // getters and setters }
  14. RELATIONSHIP QUERIES (LINK QUERIES) // Find ALL USERS who have

    dogs named fluffy RealmResults<Person> r1 = realm.where(Person.class) .equalTo("dogs.name","Fluffy") .findAll(); app Learn more at: bit.ly/realm-relationships
  15. SETUP // Add Realm to the classpath in the root

    build.gradle file buildscript { repositories { jcenter() } dependencies { classpath "io.realm:realm-gradle-plugin:2.0.0" } } // Then ... // In your applicaitons build.gradle file apply plugin: 'realm-android' // Now you're ready to store some data!
  16. USE ANNOTATIONS @RealmClass public class Dog { // ... }

    To interact with Realm, use the static methods. e.g. - Realm.isValid(obj), Realm.addChangeListener(...), etc
  17. SERIOUSLY THOUGH, YOU GET TO WORK WITH OBJECTS RealmQuery<Dog> query

    = realm.where(Dog.class); // Add query conditions: query.equalTo("name", "Fido"); query.or().equalTo("name", "Odie"); // Execute the query: RealmResults<Dog> result1 = query.findAll(); // Or do the same all at once (the "Fluent interface"): RealmResults<Dog> result2 = realm.where(Dog.class) .equalTo("name", "Fido") .or() .equalTo("name", "odie", Case.INSENSITIVE) .findAll();
  18. #LEMMESHOWYOU Dog d1 = realm.where(Dog.class).equals("id", 123).findFirst(); // register a listener

    to get notified when the object is updated. d1.registerChangeListener(new RealmChangeListener() { @Override public void onChange() { // called once the query complete and on every update // do something now that the obj is updated } }); // assume code below is in some other thread/etc (Android Service, AsyncTask, etc) // Retrieve the same dog as above Dog d2 = realm.where(Dog.class).equals("id", 123).first(); realm.beginTransaction(); d2.setAge(12); realm.commitTransaction(); // d1's change listener gets called after the commit.*
  19. REALMRESULTS<T> ARE ALSO AUTO-UPDATING RealmResults<Dog> puppies = realm.where(Dog.class).lessThan("age", 2).first(); puppies.registerChangeListener(new

    RealmChangeListener() { @Override // Gets called any time any object that this query represents gets updated public void onChange() { // do something with the updated results } }); // in some other thread realm.beginTransaction(); Dog pup = realm.createObject(Dog.class); pup.setName("Snoop"); pup.setAge(1); realm.commitTransaction(); // At this point the puppies change listener will be invoked as the query // results have automatically been updated.
  20. WATCH THE ENTIRE REALM realm.registerChangeListener(new RealmChangeListener() { @Override // Gets

    called any time the Realm data changes public void onChange() { // do something with the updated Realm } });
  21. REALM SECURITY AES-256 ENCRYPTION IS SUPPORTED OUT OF THE BOX

    // Set up with the config byte[] key = new byte[64]; new SecureRandom().nextBytes(key); RealmConfiguration config = new RealmConfiguration.Builder(context) .encryptionKey(key) .build(); // Realm data is now encrypted Realm realm = Realm.getInstance(config);
  22. THE ONLY LIMITATION The only limitation is that you cannot

    randomly pass Realm objects between threads. If you need the same data on another thread you just need to query for that data on the that other thread. Furthermore, you can observe the changes using Realms reactive architecture. Remember - all objects are kept up to date between threads - Realm will notify you when the data changes. — Realm Docs
  23. THE GOAL OF REALMS THREADING DECISIONS The key takeaway here

    is that Realm makes it effortless to work with data on multiple threads without having to worry about consistency or performance because objects and queries are auto-updating at all times. — Realm Docs
  24. MULTI THREADING IS HARD Concurrency in software is difficult [...]

    Non-trivial multi- threaded programs are incomprehensible to humans.1 — The Problem with Threads, Edward A Lee PHD Berkeley University of California 1 The Problem with Threads PDF Link
  25. HOW ARE THREADING PROBLEMS ARE NORMALLY SOLVED? LOCKS Unfortunately, when

    you dive into the root of the problem you realize you have to lock everything during reads and writes to fully ensure that the data is consistent.
  26. THREADING OPTIONS FOR REALM ▸ Operate on Android's Main Thread2

    ▸ Use the Async API 2 Yes, it's possible, but it gives a lot of developers the heebee jeebees.
  27. THE REALM ASYNC API RealmResults<Dog> dogs = realm.where(Dog.class).findAllAsync(); // dogs

    is an empty list at this point (query is running in the BG) dogs.addRealmChangeListener(new RealmChangeListener() { @Override public void onChange() { // called once the query completes and on every update // do something with the query results } }); // As soon as the query completes the change listerner will be notified // that the results are available (and will continue to get notified) // of new updates // Working with a single object query Dog dog = realm.where(Dog.class).equalTo("age", 2).findFirstAsync(); dog.addRealmChangeListener(new RealmChangeListener() { @Override public void onChange() { // called once the query completes and on every update // do something with the dog object } });
  28. ANOTHER ASYNC API EXAMPLE RealmResults<Dog> puppies = realm.where(Dog.class).lessThan("age", 2).findAll(); puppies.size();

    // => 0 - No puppies in the Realm DB // Query and update the result asynchronously in another thread realm.executeTransactionAsync(new Realm.Transaction() { @Override public void execute(Realm realm) { // begin & end transcation calls are done for you. Dog theDog = realm.createObject(Dog.class); theDog.setAge(3); // You could also query and alter objects as well } }, new Realm.Transaction.Callback() { @Override public void onSuccess() { // Original Queries and Realm objects are automatically updated. puppies.size(); // => 1 because there is one puppy now } });
  29. RXJAVA SUPPORT // Combining Realm, Retrofit and RxJava (Using Retrolambda

    syntax for brevity) // Load all persons and merge them with their latest stats from GitHub (if they have any) Realm realm = Realm.getDefaultInstance(); GitHubService api = retrofit.create(GitHubService.class); realm.where(Person.class).isNotNull("username").findAllAsync().asObservable() .filter(persons.isLoaded) .flatMap(persons -> Observable.from(persons)) .flatMap(person -> api.user(person.getGithubUserName()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(user -> showUser(user)); EXAMPLE APP - HTTP://BIT.LY/REALM-RXJAVA
  30. I heard that you can use Realm on the main

    thread. Why is that possible? Should I?
  31. WHAT IF I NEED TO CHANGE WHAT'S IN MY MODEL?

    We Got you. USE SCHEMA MIGRATIONS
  32. // Example migration adding a new class RealmMigration MyMigration =

    new RealmMigration() { @Override public void migrate(DynamicRealm realm, long oldVersion, long newVersion) { // DynamicRealm exposes an editable schema RealmSchema schema = realm.getSchema(); // Migrate to version 1: Add a new class. // Example: // public Person extends RealmObject { // @PrimaryKey // private long id; // private String name; // private int age; // // getters and setters left out for brevity // } if (oldVersion == 0) { schema.create("Person") .addField("id", long.class, FieldAttribute.PRIMARY_KEY) .addField("name", String.class) .addField("age", int.class); oldVersion++; } } } // in your init RealmConfiguration config = new RealmConfiguration.Builder(context) .schemaVersion(1) // Must be bumped when the schema changes .migration(new MyMigration()) // Migration to run instead of throwing an exception .build()
  33. REALM IS FREE ALL PRODUCTS ARE OPEN SOURCE (ANDROID, IOS,

    REACT, ETC) CORE WILL BE OPEN SOURCED - LATER CORE IS WRITTEN IN C++, FROM THE GROUND UP.
  34. THE NOT SOO GOOD NO CUSTOM RXJAVA SCHEDULERS, YET, NO

    COMPOSITE PRIMARY KEY CONSTRAINTS, YET. PARADIGM SHIFT - NO PASSING BETWEEN THREADS.
  35. WHO'S USING THIS ON ANDROID? A LOT OF COMPANIES. HERE'S

    A FEW YOU MIGHT HAVE HEARD OF ... Genius, Starbucks, Shyp, Hyatt, IBM, Zappos, Stubhub, Skyfit, Shopsavvy, Virgin Mobile, Subway, Falcon Pro 3, Allegiant Airlines, Digi-Key, Taptalk, Cabify, Karma Wifi ...
  36. WHO'S USING THIS ON OTHER PLATFORMS (IOS)? JUST LIKE ANDROID

    - A LOT. HERE'S A FEW YOU MIGHT HAVE HEARD OF ... Groupon, McDonalds, Zipcar, BBC, ZipRecruiter, Hipmunk, Expensify, Concur, HipChat, Intuit, Oprah, Alibaba, BodyBuilding.com, L'ORÉAL ...