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

Android Realtime Library

Android Realtime Library

DroidKaigi 2017
「Androidリアルタイム通信アプリ作成Tips」
のスライドです。

Avatar for Fumihiko Shiroyama

Fumihiko Shiroyama

March 08, 2017
Tweet

More Decks by Fumihiko Shiroyama

Other Decks in Technology

Transcript

  1. About Me • Fumihiko Shiroyama • Android App Developer •

    Nikkei Inc. • https://github.com/srym • https://twitter.com/fushiroyama
  2. Realtime Apps' Upsides • Enables Rich User Interaction • Server

    Side Message Pushing • No Pull-To-Refresh
  3. Realtime Apps' Downsides • Connection Problems • Dealing with Poor

    Signal • Hassle with Retransmission • Complex Infrastructure • Proxy Problems etc, etc...
  4. WebSocket GET /chat HTTP/1.1 Host: server.example.com Upgrade: websocket Connection: Upgrade

    Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== Origin: http://example.com Sec-WebSocket-Protocol: chat, superchat Sec-WebSocket-Version: 13
  5. WebSocket GET /chat HTTP/1.1 Host: server.example.com Upgrade: websocket Connection: Upgrade

    Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== Origin: http://example.com Sec-WebSocket-Protocol: chat, superchat Sec-WebSocket-Version: 13
  6. WebSocket Server • https://github.com/theturtle32/WebSocket-Node • $ npm install websocket •

    https://github.com/theturtle32/WebSocket- Node#server-example • $ node server-example.js
  7. WebSocket Server • https://github.com/theturtle32/WebSocket-Node • $ npm install websocket •

    https://github.com/theturtle32/WebSocket- Node#server-example • $ node server-example.js 8IBUFWFS ZPVMJLF
  8. WebSocketListener WebSocketListener listener = new WebSocketListener() { @Override public void

    onOpen(WebSocket ws, Response res) {} @Override public void onClosing(WebSocket ws, int code, String reason) {} @Override public void onClosed(WebSocket ws, int code, String reason) {} @Override public void onFailure(WebSocket ws, Throwable t, Response res) {} @Override public void onMessage(WebSocket ws, final String text) {} @Override public void onMessage(WebSocket ws, ByteString bytes) {} };
  9. WebSocketListener WebSocketListener listener = new WebSocketListener() { @Override public void

    onOpen(WebSocket ws, Response res) {} @Override public void onClosing(WebSocket ws, int code, String reason) {} @Override public void onClosed(WebSocket ws, int code, String reason) {} @Override public void onFailure(WebSocket ws, Throwable t, Response res) {} @Override public void onMessage(WebSocket ws, final String text) {} @Override public void onMessage(WebSocket ws, ByteString bytes) {} };
  10. WebSocketListener @Override public void onMessage(WebSocket ws, final String text) {

    Log.d(TAG, "onMessage(text): " + text); } @Override public void onMessage(WebSocket ws, ByteString bytes) { Log.d(TAG, "onMessage(binary)"); }
  11. WebSocketListener @Override public void onMessage(WebSocket ws, final String text) {

    Log.d(TAG, "onMessage(text): " + text); } @Override public void onMessage(WebSocket ws, ByteString bytes) { Log.d(TAG, "onMessage(binary)"); } 5FYU #JOBSZ
  12. OkHttpClient client = new OkHttpClient(); Request request = new Request.Builder()

    .url("ws://example.com:8080") .build(); final WebSocket webSocket = client.newWebSocket(request, listener); // Trigger shutdown of the dispatcher's executor // so this process can exit cleanly. client.dispatcher().executorService().shutdown(); WebSocket
  13. OkHttpClient client = new OkHttpClient(); Request request = new Request.Builder()

    .url("ws://example.com:8080") .build(); final WebSocket webSocket = client.newWebSocket(request, listener); // Trigger shutdown of the dispatcher's executor // so this process can exit cleanly. client.dispatcher().executorService().shutdown(); WebSocket
  14. OkHttpClient client = new OkHttpClient(); Request request = new Request.Builder()

    .url("ws://example.com:8080") .build(); final WebSocket webSocket = client.newWebSocket(request, listener); // Trigger shutdown of the dispatcher's executor // so this process can exit cleanly. client.dispatcher().executorService().shutdown(); WebSocket
  15. WebSocket OkHttpClient client = new OkHttpClient(); Request request = new

    Request.Builder() .url("ws://example.com:8080") .build(); final WebSocket webSocket = client.newWebSocket(request, listener); // Trigger shutdown of the dispatcher's executor // so this process can exit cleanly. client.dispatcher().executorService().shutdown();
  16. Message public class Message { @SerializedName("sender_id") public final long senderId;

    @SerializedName("message") public final String message; public Message(long senderId, String message) { this.senderId = senderId; this.message = message; } }
  17. send JSON button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v)

    { Message message = new Message(1234, "hi there!"); webSocket.send(gson.toJson(message)); } });
  18. send JSON button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v)

    { Message message = new Message(1234, "hi there!"); webSocket.send(gson.toJson(message)); } });
  19. send JSON button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v)

    { Message message = new Message(1234, "hi there!"); webSocket.send(gson.toJson(message)); } });
  20. receive Message @Override public void onMessage(WebSocket ws, final String text)

    { Message message = gson.fromJson(text, Message.class); Log.d(TAG, "onMessage(text): " + message.toString()); }
  21. receive Message @Override public void onMessage(WebSocket ws, final String text)

    { Message message = gson.fromJson(text, Message.class); Log.d(TAG, "onMessage(text): " + message.toString()); }
  22. WebSocket Tips • WebSocket does NOT have any room, namespace

    mechanism. • WebSocket does NOT support broadcasting out of the box.
  23. WebSocket Tips • WebSocket does NOT have any room, namespace

    mechanism. • WebSocket does NOT support broadcasting out of the box. 4PNF *NQMFNFOUBUJPOTEP
  24. WebSocket Server wsServer.on('request', function(request) { var connection = request.accept(); connection.on('message',

    function(message) { if (message.type === 'utf8') { wsServer.broadcastUTF(message.utf8Data); } else if (message.type === 'binary') { wsServer.broadcastBytes(message.binaryData); } }); }
  25. WebSocket Server wsServer.on('request', function(request) { var connection = request.accept(); connection.on('message',

    function(message) { if (message.type === 'utf8') { wsServer.broadcastUTF(message.utf8Data); } else if (message.type === 'binary') { wsServer.broadcastBytes(message.binaryData); } }); }
  26. WebSocket Server wsServer.on('request', function(request) { var connection = request.accept(); connection.on('message',

    function(message) { if (message.type === 'utf8') { wsServer.broadcastUTF(message.utf8Data); } else if (message.type === 'binary') { wsServer.broadcastBytes(message.binaryData); } }); }
  27. WebSocket Server wsServer.on('request', function(request) { var connection = request.accept(); connection.on('message',

    function(message) { if (message.type === 'utf8') { wsServer.broadcastUTF(message.utf8Data); } else if (message.type === 'binary') { wsServer.broadcastBytes(message.binaryData); } }); }
  28. WebSocket Tips • WebSocket does NOT have any room, namespace

    mechanism. • WebSocket does NOT support broadcasting out of the box. • You can use socket.io for room, namespace, connection retry, fallbacks etc...
  29. Good Points • Simple and Open and Free • Server

    Hook Point • Binary Support • Can Scale • Redis Pub/Sub
  30. Bad Points • No Offline Support • No rooms /

    namespaces • No Broadcasting
  31. Realtime Database 3FNPUF %BUBCBTF -PDBM %BUBCBTF -PDBM %BUBCBTF %BUB 8SJUUFO

    4ZOD 4ZOD 4ZOD $BMMCBDL $BMMCBDL send "hello" "hello" written! "hello" written!
  32. Realtime Database • by Google • Every client looks at

    Local Database • Offline Support! • Data are synced transparently • Handy and robust Security Rule • Don't worry about scaling
  33. Data Structure { "messages": { "one": { "m1": { "name":

    "eclarke", "message": "The relay seems to be malfunctioning.", "timestamp": 1459361875337 }, "m2": { ... }, "m3": { ... } }, "two": { ... }, "three": { ... } } } .FTTBHF 5SFF
  34. Data Structure { "messages": { "one": { "m1": { "name":

    "eclarke", "message": "The relay seems to be malfunctioning.", "timestamp": 1459361875337 }, "m2": { ... }, "m3": { ... } }, "two": { ... }, "three": { ... } } } $IBUSPPN POF
  35. Data Structure { "messages": { "one": { "m1": { "name":

    "shiroyama", "message": "Hello Everyone!", "timestamp": 1459361875337 }, "m2": { ... }, "m3": { ... } }, "two": { ... }, "three": { ... } } } Hello Everyone!
  36. Data Structure { "messages": { "one": { "m1": { "name":

    "shiroyama", "message": "Hello Everyone!", "timestamp": 1459361875337 }, "m2": { ... }, "m3": { ... } }, "two": { ... }, "three": { ... } } } Hello Everyone!
  37. Data Structure { "chats": { "one": { "title": "Room 1",

    "lastMessage": "shiroyama: Hello Everyone!", "timestamp": 1459361875666 }, "two": { ... }, "three": { ... } }, "members": { "one": { "shiroyama": true, "tanaka": true }, "two": { ... }, "three": { ... } }, "messages": { "one": { "m1": { "name": "shiroyama", "message": "Hello Everyone!", "timestamp": 1459361875337 }, "m2": { ... }, "m3": { ... } } } }
  38. Data Structure { "chats": { "one": { "title": "Room 1",

    "lastMessage": "shiroyama: Hello Everyone!", "timestamp": 1459361875666 }, "two": { ... }, "three": { ... } }, "members": { "one": { "shiroyama": true, "tanaka": true }, "two": { ... }, "three": { ... } }, "messages": { "one": { "m1": { "name": "shiroyama", "message": "Hello Everyone!", "timestamp": 1459361875337 }, "m2": { ... }, "m3": { ... } } } } EFOPSNBMJ[JOH
  39. Data Structure { "chats": { "one": { "title": "Room 1",

    "lastMessage": "shiroyama: Hello Everyone!", "timestamp": 1459361875666 }, "two": { ... }, "three": { ... } }, "members": { "one": { "shiroyama": true, "tanaka": true }, "two": { ... }, "three": { ... } }, "messages": { "one": { "m1": { "name": "shiroyama", "message": "Hello Everyone!", "timestamp": 1459361875337 }, "m2": { ... }, "m3": { ... } } } } EFOPSNBMJ[JOH
  40. • https://firebase.google.com/docs/database/security/ • .read, .write, .validate • Predefined Variables •

    https://firebase.google.com/docs/database/security/ securing-data?hl=en#predefined_variables Security Rules
  41. Message @IgnoreExtraProperties public class Message { private String name; private

    String message; private long timestamp; public Message() { } // getters and setters }
  42. Send Message button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v)

    { Message message = new Message(); message.setName(name); message.setMessage(text); message.setTimestamp(timestamp); messages.push().setValue(message); } });
  43. Send Message button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v)

    { Message message = new Message(); message.setName(name); message.setMessage(text); message.setTimestamp(timestamp); messages.push().setValue(message); } });
  44. Send Message button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v)

    { Message message = new Message(); message.setName(name); message.setMessage(text); message.setTimestamp(timestamp); messages.push().setValue(message); } });
  45. Send Message button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v)

    { Message message = new Message(); message.setName(name); message.setMessage(text); message.setTimestamp(timestamp); messages.push().setValue(message); } }); QVTIUPMJTU
  46. { "messages": { "one": { "-Kei_CdbvKoms35KcA8p": { "name": "shiroyama", "message":

    "Hello Everyone!", "timestamp": 1459361875337 }, "m2": { ... }, "m3": { ... } }, "two": { ... }, "three": { ... } } } Hello Everyone! Send Message
  47. { "messages": { "one": { "-Kei_CdbvKoms35KcA8p": { "name": "shiroyama", "message":

    "Hello Everyone!", "timestamp": 1459361875337 }, "m2": { ... }, "m3": { ... } }, "two": { ... }, "three": { ... } } } Hello Everyone! HFOFSBUFECZ QVTI  Send Message
  48. Send Message button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v)

    { Message message = new Message(); message.setName(name); message.setMessage(text); message.setTimestamp(timestamp); messages.push().setValue(message); } });
  49. Send Message button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v)

    { Message message = new Message(); message.setName(name); message.setMessage(text); message.setTimestamp(timestamp); messages.push().setValue(message); } }); XSJUF
  50. ChildEventListener listener = new ChildEventListener() { @Override public void onChildAdded(DataSnapshot

    snap, String s) {} @Override public void onChildChanged(DataSnapshot snap, String s) {} @Override public void onChildRemoved(DataSnapshot snap) {} @Override public void onChildMoved(DataSnapshot snap, String s) {} @Override public void onCancelled(DatabaseError error) {} }; messages.addChildEventListener(listener); Receive Message
  51. ChildEventListener listener = new ChildEventListener() { @Override public void onChildAdded(DataSnapshot

    snap, String s) {} @Override public void onChildChanged(DataSnapshot snap, String s) {} @Override public void onChildRemoved(DataSnapshot snap) {} @Override public void onChildMoved(DataSnapshot snap, String s) {} @Override public void onCancelled(DatabaseError error) {} }; messages.addChildEventListener(listener); Receive Message
  52. ChildEventListener listener = new ChildEventListener() { @Override public void onChildAdded(DataSnapshot

    snap, String s) {} @Override public void onChildChanged(DataSnapshot snap, String s) {} @Override public void onChildRemoved(DataSnapshot snap) {} @Override public void onChildMoved(DataSnapshot snap, String s) {} @Override public void onCancelled(DatabaseError error) {} }; messages.addChildEventListener(listener); Receive Message
  53. ChildEventListener listener = new ChildEventListener() { @Override public void onChildAdded(DataSnapshot

    snap, String s) {} @Override public void onChildChanged(DataSnapshot snap, String s) {} @Override public void onChildRemoved(DataSnapshot snap) {} @Override public void onChildMoved(DataSnapshot snap, String s) {} @Override public void onCancelled(DatabaseError error) {} }; messages.addChildEventListener(listener); Receive Message
  54. onChildAdded() @Override public void onChildAdded(DataSnapshot snap, String s) { Message

    message = snap.getValue(Message.class); Log.d(TAG, message.getMessage()); }
  55. onChildAdded() @Override public void onChildAdded(DataSnapshot snap, String s) { Message

    message = snap.getValue(Message.class); Log.d(TAG, message.getMessage()); }
  56. onChildAdded() @Override public void onChildAdded(DataSnapshot snap, String s) { Message

    message = snap.getValue(Message.class); Log.d(TAG, message.getMessage()); } 5ZQF4BGF
  57. • ValueEventListener • Fetch single item / at once •

    ChildEventListener • Fetch from list (add/change/remove/move) • https://firebase.google.com/docs/database/android/ read-and-write Listeners
  58. • Pull-to-Refresh • ValueEvent is triggered at last • Desc

    Ordering • setPrioritiy(timestamp * -1) • or create "desc_timestamp" property Realtime Database Tips
  59. Good Points • Offline Support and Sync • Easy and

    Robust Security Rules • Good Interaction with other Firebase services • Easy to scale
  60. Bad Points • No binary support • No server hook

    point • No source code! • Tricky ordering limitation • Storage and Traffic fee could be expensive
  61. • Alternative to SQLite and Core Data • ORMapper •

    Cross Platform • https://realm.io/products/realm-mobile-database/ Realm Mobile Database
  62. Project's build.gradle buildscript { repositories { jcenter() } dependencies {

    classpath 'com.android.tools.build:gradle:2.2.3' classpath 'io.realm:realm-gradle-plugin:3.0.0' } }
  63. Project's build.gradle buildscript { repositories { jcenter() } dependencies {

    classpath 'com.android.tools.build:gradle:2.2.3' classpath 'io.realm:realm-gradle-plugin:3.0.0' } }
  64. Custom Application public class MyApplication extends Application { @Override public

    void onCreate() { super.onCreate(); Realm.init(this); RealmLog.setLevel(Log.VERBOSE); } }
  65. Custom Application public class MyApplication extends Application { @Override public

    void onCreate() { super.onCreate(); Realm.init(this); RealmLog.setLevel(Log.VERBOSE); } }
  66. Custom Application public class MyApplication extends Application { @Override public

    void onCreate() { super.onCreate(); Realm.init(this); RealmLog.setLevel(Log.VERBOSE); } }
  67. Message Model public class Message extends RealmObject { private String

    name; private String message; private long timestamp; public Message() { } // getters & setters }
  68. Message Model public class Message extends RealmObject { private String

    name; private String message; private long timestamp; public Message() { } // getters & setters }
  69. Message Model public class Message extends RealmObject { private String

    name; private String message; private long timestamp; public Message() { } // getters & setters } OFDFTTBSZ
  70. Message Model public class Message extends RealmObject { private String

    name; private String message; private long timestamp; public Message() { } // getters & setters } OFDFTTBSZ QSPYZUP OBUJWFDPEF
  71. Write to Realm button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View

    v) { Realm realm = Realm.getDefaultInstance(); realm.executeTransaction(new Realm.Transaction() { @Override public void execute(Realm realm) { final Message message = realm.createObject(Message.class); message.setName(name); message.setMessage(text); message.setTimestamp(timestamp); } }); } });
  72. Write to Realm button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View

    v) { Realm realm = Realm.getDefaultInstance(); realm.executeTransaction(new Realm.Transaction() { @Override public void execute(Realm realm) { final Message message = realm.createObject(Message.class); message.setName(name); message.setMessage(text); message.setTimestamp(timestamp); } }); } });
  73. Write to Realm button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View

    v) { Realm realm = Realm.getDefaultInstance(); realm.executeTransaction(new Realm.Transaction() { @Override public void execute(Realm realm) { final Message message = realm.createObject(Message.class); message.setName(name); message.setMessage(text); message.setTimestamp(timestamp); } }); } });
  74. Write to Realm button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View

    v) { Realm realm = Realm.getDefaultInstance(); realm.executeTransaction(new Realm.Transaction() { @Override public void execute(Realm realm) { final Message message = realm.createObject(Message.class); message.setName(name); message.setMessage(text); message.setTimestamp(timestamp); } }); } });
  75. Write to Realm button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View

    v) { Realm realm = Realm.getDefaultInstance(); realm.executeTransaction(new Realm.Transaction() { @Override public void execute(Realm realm) { final Message message = realm.createObject(Message.class); message.setName(name); message.setMessage(text); message.setTimestamp(timestamp); } }); } });
  76. Write to Realm final Message message = new Message(); message.setName(name);

    message.setMessage(text); message.setTimestamp(timestamp); Realm realm = Realm.getDefaultInstance(); realm.executeTransaction(new Realm.Transaction() { @Override public void execute(Realm realm) { realm.copyToRealm(message); } });
  77. or

  78. Write to Realm final Message message = new Message(); message.setName(name);

    message.setMessage(text); message.setTimestamp(timestamp); Realm realm = Realm.getDefaultInstance(); realm.executeTransaction(new Realm.Transaction() { @Override public void execute(Realm realm) { realm.copyToRealm(message); } });
  79. Write to Realm final Message message = new Message(); message.setName(name);

    message.setMessage(text); message.setTimestamp(timestamp); Realm realm = Realm.getDefaultInstance(); realm.executeTransaction(new Realm.Transaction() { @Override public void execute(Realm realm) { realm.copyToRealm(message); } });
  80. Write to Realm final Message message = new Message(); message.setName(name);

    message.setMessage(text); message.setTimestamp(timestamp); Realm realm = Realm.getDefaultInstance(); realm.executeTransaction(new Realm.Transaction() { @Override public void execute(Realm realm) { realm.copyToRealm(message); } });
  81. Read from Realm Realm realm = Realm.getDefaultInstance(); RealmResults<Message> messages =

    realm .where(Message.class) .findAll(); if (!messages.isEmpty()) { for (Message message : messages) { Log.d(TAG, "message: " + message.getMessage()); } }
  82. Read from Realm Realm realm = Realm.getDefaultInstance(); RealmResults<Message> messages =

    realm .where(Message.class) .findAll(); if (!messages.isEmpty()) { for (Message message : messages) { Log.d(TAG, "message: " + message.getMessage()); } }
  83. Read from Realm Realm realm = Realm.getDefaultInstance(); RealmResults<Message> messages =

    realm .where(Message.class) .findAll(); if (!messages.isEmpty()) { for (Message message : messages) { Log.d(TAG, "message: " + message.getMessage()); } }
  84. Read from Realm Realm realm = Realm.getDefaultInstance(); RealmResults<Message> messages =

    realm .where(Message.class) .findAll(); if (!messages.isEmpty()) { for (Message message : messages) { Log.d(TAG, "message: " + message.getMessage()); } }
  85. Read from Realm Realm realm = Realm.getDefaultInstance(); RealmResults<Message> messages =

    realm .where(Message.class) .findAll(); if (!messages.isEmpty()) { for (Message message : messages) { Log.d(TAG, "message: " + message.getMessage()); } }
  86. Change Listener RealmChangeListener<RealmResults<Message>> changListener = new RealmChangeListener<RealmResults<Message>>() { @Override public

    void onChange(RealmResults<Message> element) { for (Message msg : element) { Log.d(TAG, "message: " + msg.getMessage()); } } }; messages.addChangeListener(changListener);
  87. Change Listener RealmChangeListener<RealmResults<Message>> changListener = new RealmChangeListener<RealmResults<Message>>() { @Override public

    void onChange(RealmResults<Message> element) { for (Message msg : element) { Log.d(TAG, "message: " + msg.getMessage()); } } }; messages.addChangeListener(changListener);
  88. Change Listener RealmChangeListener<RealmResults<Message>> changListener = new RealmChangeListener<RealmResults<Message>>() { @Override public

    void onChange(RealmResults<Message> element) { for (Message msg : element) { Log.d(TAG, "message: " + msg.getMessage()); } } }; messages.addChangeListener(changListener);
  89. 3FBMN 0CKFDU 4FSWFS %BUB 8SJUUFO $BMMCBDL send "hello" "hello" written!

    Realm Mobile Platform 3FBMN .PCJMF %BUBCBTF 3FBMN .PCJMF %BUBCBTF
  90. %BUB 8SJUUFO 4ZOD $BMMCBDL send "hello" "hello" written! Realm Mobile

    Platform 3FBMN 0CKFDU 4FSWFS 3FBMN .PCJMF %BUBCBTF 3FBMN .PCJMF %BUBCBTF
  91. %BUB 8SJUUFO 4ZOD 4ZOD 4ZOD $BMMCBDL $BMMCBDL send "hello" "hello"

    written! "hello" written! Realm Mobile Platform 3FBMN 0CKFDU 4FSWFS 3FBMN .PCJMF %BUBCBTF 3FBMN .PCJMF %BUBCBTF
  92. • https://realm.io/docs/realm-object-server/ • macOS • RHEL/CentOS 6 • Ubuntu 16.04

    • AWS/AMI • Developer Edition is FREE Realm Object Server
  93. • Ubuntu 16.04 • curl -s https://packagecloud.io/install/repositories/realm/realm/ script.deb.sh | sudo

    bash • sudo apt-get update • sudo apt-get install realm-object-server-developer • sudo systemctl enable realm-object-server • sudo systemctl start realm-object-server Realm Object Server
  94. Login final SyncCredentials cred = SyncCredentials.usernamePassword(ID, PASSWORD, true); SyncUser.loginAsync(cred, AUTH_URL,

    new SyncUser.Callback() { @Override public void onSuccess(SyncUser user) { final SyncConfiguration syncConfiguration = new SyncConfiguration.Builder(user, REALM_URL).build(); Realm.setDefaultConfiguration(syncConfiguration); realm = Realm.getDefaultInstance(); } @Override public void onError(ObjectServerError error) {} });
  95. Login final SyncCredentials cred = SyncCredentials.usernamePassword(ID, PASSWORD, true); SyncUser.loginAsync(cred, AUTH_URL,

    new SyncUser.Callback() { @Override public void onSuccess(SyncUser user) { final SyncConfiguration syncConfiguration = new SyncConfiguration.Builder(user, REALM_URL).build(); Realm.setDefaultConfiguration(syncConfiguration); realm = Realm.getDefaultInstance(); } @Override public void onError(ObjectServerError error) {} }); $SFBUF6TFS 'JSTU5JNF
  96. Login final SyncCredentials cred = SyncCredentials.usernamePassword(ID, PASSWORD, true); SyncUser.loginAsync(cred, AUTH_URL,

    new SyncUser.Callback() { @Override public void onSuccess(SyncUser user) { final SyncConfiguration syncConfiguration = new SyncConfiguration.Builder(user, REALM_URL).build(); Realm.setDefaultConfiguration(syncConfiguration); realm = Realm.getDefaultInstance(); } @Override public void onError(ObjectServerError error) {} });
  97. Login final SyncCredentials cred = SyncCredentials.usernamePassword(ID, PASSWORD, true); SyncUser.loginAsync(cred, AUTH_URL,

    new SyncUser.Callback() { @Override public void onSuccess(SyncUser user) { final SyncConfiguration syncConfiguration = new SyncConfiguration.Builder(user, REALM_URL).build(); Realm.setDefaultConfiguration(syncConfiguration); realm = Realm.getDefaultInstance(); } @Override public void onError(ObjectServerError error) {} });
  98. Login final SyncCredentials cred = SyncCredentials.usernamePassword(ID, PASSWORD, true); SyncUser.loginAsync(cred, AUTH_URL,

    new SyncUser.Callback() { @Override public void onSuccess(SyncUser user) { final SyncConfiguration syncConfiguration = new SyncConfiguration.Builder(user, REALM_URL).build(); Realm.setDefaultConfiguration(syncConfiguration); realm = Realm.getDefaultInstance(); } @Override public void onError(ObjectServerError error) {} });
  99. Login final SyncCredentials cred = SyncCredentials.usernamePassword(ID, PASSWORD, true); SyncUser.loginAsync(cred, AUTH_URL,

    new SyncUser.Callback() { @Override public void onSuccess(SyncUser user) { final SyncConfiguration syncConfiguration = new SyncConfiguration.Builder(user, REALM_URL).build(); Realm.setDefaultConfiguration(syncConfiguration); realm = Realm.getDefaultInstance(); } @Override public void onError(ObjectServerError error) {} });
  100. Login final SyncCredentials cred = SyncCredentials.usernamePassword(ID, PASSWORD, true); SyncUser.loginAsync(cred, AUTH_URL,

    new SyncUser.Callback() { @Override public void onSuccess(SyncUser user) { final SyncConfiguration syncConfiguration = new SyncConfiguration.Builder(user, REALM_URL).build(); Realm.setDefaultConfiguration(syncConfiguration); realm = Realm.getDefaultInstance(); } @Override public void onError(ObjectServerError error) {} });
  101. Fine-grained Collection Notification public void onChange(RealmResults<Message> collection, OrderedCollectionChangeSet changeSet) {

    OrderedCollectionChangeSet.Range[] insertions = changeSet.getInsertionRanges(); for (OrderedCollectionChangeSet.Range insertion : insertions) { int index = insertion.startIndex; int length = insertion.length; for (int i = index; i < index + length; i++) { Message message = collection.get(i); Log.d(TAG, "message: " + message.getMessage()); } } } messages.addChangeListener(realmChangeListener);
  102. Fine-grained Collection Notification public void onChange(RealmResults<Message> collection, OrderedCollectionChangeSet changeSet) {

    OrderedCollectionChangeSet.Range[] insertions = changeSet.getInsertionRanges(); for (OrderedCollectionChangeSet.Range insertion : insertions) { int index = insertion.startIndex; int length = insertion.length; for (int i = index; i < index + length; i++) { Message message = collection.get(i); Log.d(TAG, "message: " + message.getMessage()); } } } messages.addChangeListener(realmChangeListener);
  103. Fine-grained Collection Notification public void onChange(RealmResults<Message> collection, OrderedCollectionChangeSet changeSet) {

    OrderedCollectionChangeSet.Range[] insertions = changeSet.getInsertionRanges(); for (OrderedCollectionChangeSet.Range insertion : insertions) { int index = insertion.startIndex; int length = insertion.length; for (int i = index; i < index + length; i++) { Message message = collection.get(i); Log.d(TAG, "message: " + message.getMessage()); } } } messages.addChangeListener(realmChangeListener);
  104. Fine-grained Collection Notification public void onChange(RealmResults<Message> collection, OrderedCollectionChangeSet changeSet) {

    OrderedCollectionChangeSet.Range[] insertions = changeSet.getInsertionRanges(); for (OrderedCollectionChangeSet.Range insertion : insertions) { int index = insertion.startIndex; int length = insertion.length; for (int i = index; i < index + length; i++) { Message message = collection.get(i); Log.d(TAG, "message: " + message.getMessage()); } } } messages.addChangeListener(realmChangeListener);
  105. Let others read/write Realm realm = user.getManagementRealm(); realm.executeTransaction(new Realm.Transaction() {

    @Override public void execute(Realm realm) { Boolean mayRead = true; Boolean mayWrite = null; boolean mayManage = false; PermissionChange change = new PermissionChange(realmUrl, anotherUserID, mayRead, mayWrite, mayManage); realm.insert(change); } });
  106. Let others read/write Realm realm = user.getManagementRealm(); realm.executeTransaction(new Realm.Transaction() {

    @Override public void execute(Realm realm) { Boolean mayRead = true; Boolean mayWrite = null; boolean mayManage = false; PermissionChange change = new PermissionChange(realmUrl, anotherUserID, mayRead, mayWrite, mayManage); realm.insert(change); } }); "MMPX
  107. Let others read/write Realm realm = user.getManagementRealm(); realm.executeTransaction(new Realm.Transaction() {

    @Override public void execute(Realm realm) { Boolean mayRead = true; Boolean mayWrite = null; boolean mayManage = false; PermissionChange change = new PermissionChange(realmUrl, anotherUserID, mayRead, mayWrite, mayManage); realm.insert(change); } }); "TJUJT
  108. Let others read/write Realm realm = user.getManagementRealm(); realm.executeTransaction(new Realm.Transaction() {

    @Override public void execute(Realm realm) { Boolean mayRead = true; Boolean mayWrite = null; boolean mayManage = false; PermissionChange change = new PermissionChange(realmUrl, anotherUserID, mayRead, mayWrite, mayManage); realm.insert(change); } }); %JTBMMPX
  109. Let others read/write Realm realm = user.getManagementRealm(); realm.executeTransaction(new Realm.Transaction() {

    @Override public void execute(Realm realm) { Boolean mayRead = true; Boolean mayWrite = null; boolean mayManage = false; PermissionChange change = new PermissionChange(realmUrl, anotherUserID, mayRead, mayWrite, mayManage); realm.insert(change); } });
  110. Good Points • Offline Support and Sync • Intuitive Query

    & Relation • You can use this on-premises • Developer Edition is FREE • Server Side Hook is Available on Enterprise Edition • Load Balancing is Available on Enterprise Edition
  111. Bad Points • Not really, that much! Great! • No

    server hook on Developer Edition • Realm MAY affect App's Architecture • Live Object is sometimes tricky