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

Android Realtime Library

Android Realtime Library

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

3cca191bf3064fd059ea2c3d6022afbd?s=128

Fumihiko Shiroyama

March 08, 2017
Tweet

Transcript

  1. Android Realtime Libraries Fumihiko Shiroyama https://speakerdeck.com/srym/android-realtime-library

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

    Nikkei Inc. • https://github.com/srym • https://twitter.com/fushiroyama
  3. Realtime Apps

  4. https://github.com/srym/FirebaseRealTimeChat

  5. https://realm.io/news/introducing-realm-mobile-platform/

  6. Realtime Apps' Upsides

  7. Realtime Apps' Upsides • Enables Rich User Interaction • Server

    Side Message Pushing • No Pull-To-Refresh
  8. Realtime Apps' Downsides

  9. Realtime Apps' Downsides • Connection Problems • Dealing with Poor

    Signal • Hassle with Retransmission • Complex Infrastructure • Proxy Problems etc, etc...
  10. Realtime Libraries

  11. Realtime Libraries • WebSocket

  12. Realtime Libraries • WebSocket • Firebase Realtime Database

  13. Realtime Libraries • WebSocket • Firebase Realtime Database • Realm

    Mobile Platform
  14. WebSocket

  15. WebSocket 8FC4PDLFU 4FSWFS 8FC4PDLFU $MJFOU 8FC4PDLFU $MJFOU

  16. WebSocket 4FOE.FTTBHF 8FC4PDLFU $MJFOU 8FC4PDLFU 4FSWFS 8FC4PDLFU $MJFOU send "hello"

  17. WebSocket 4FOE.FTTBHF 1SPDFTT %BUB 8FC4PDLFU $MJFOU 8FC4PDLFU 4FSWFS 8FC4PDLFU $MJFOU

    send "hello" send "hello"
  18. WebSocket 4FOE.FTTBHF 1SPDFTT %BUB 4FOE.FTTBHF 4FOE.FTTBHF 8FC4PDLFU $MJFOU 8FC4PDLFU 4FSWFS

    8FC4PDLFU $MJFOU send "hello" send "hello" "hello" is sent
  19. WebSocket • https://tools.ietf.org/html/rfc6455 • HTTP Upgrade

  20. 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
  21. 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
  22. WebSocket • https://tools.ietf.org/html/rfc6455 • HTTP Upgrade • Light-Weight & Efficient

    • Minimum 2KB Header + Payload • Keeping Connection
  23. OkHttp Supports WebSocket

  24. Try it!

  25. WebSocket Server • https://github.com/theturtle32/WebSocket-Node • $ npm install websocket •

    https://github.com/theturtle32/WebSocket- Node#server-example • $ node server-example.js
  26. 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
  27. build.gradle dependencies { compile 'com.squareup.okhttp3:okhttp:3.6.0' }

  28. 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) {} };
  29. 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) {} };
  30. 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)"); }
  31. 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
  32. 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
  33. 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
  34. 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
  35. 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();
  36. send button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) {

    webSocket.send("message"); } });
  37. send button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) {

    webSocket.send("message"); } });
  38. send button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) {

    webSocket.send("message"); } });
  39. send JSON

  40. 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; } }
  41. send JSON dependencies { compile 'com.squareup.okhttp3:okhttp:3.6.0' compile 'com.google.code.gson:gson:2.8.0' }

  42. send JSON dependencies { compile 'com.squareup.okhttp3:okhttp:3.6.0' compile 'com.google.code.gson:gson:2.8.0' }

  43. 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)); } });
  44. 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)); } });
  45. 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)); } });
  46. 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()); }
  47. 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()); }
  48. WebSocket Tips

  49. WebSocket Tips • WebSocket does NOT have any room, namespace

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

    mechanism. • WebSocket does NOT support broadcasting out of the box. 4PNF *NQMFNFOUBUJPOTEP
  51. 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); } }); }
  52. 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); } }); }
  53. 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); } }); }
  54. 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); } }); }
  55. 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...
  56. Summary

  57. Good Points • Simple and Open and Free • Server

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

    namespaces • No Broadcasting
  59. Firebase Realtime Database

  60. Realtime Database 3FNPUF %BUBCBTF -PDBM %BUBCBTF -PDBM %BUBCBTF

  61. Realtime Database 3FNPUF %BUBCBTF -PDBM %BUBCBTF -PDBM %BUBCBTF %BUB 8SJUUFO

    $BMMCBDL send "hello" "hello" written!
  62. Realtime Database 3FNPUF %BUBCBTF -PDBM %BUBCBTF -PDBM %BUBCBTF %BUB 8SJUUFO

    4ZOD $BMMCBDL send "hello" "hello" written!
  63. Realtime Database 3FNPUF %BUBCBTF -PDBM %BUBCBTF -PDBM %BUBCBTF %BUB 8SJUUFO

    4ZOD 4ZOD 4ZOD $BMMCBDL $BMMCBDL send "hello" "hello" written! "hello" written!
  64. 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
  65. Data Structure

  66. Data Structure • No SQL • JSON Tree • No

    "Columns" or "Relations"
  67. Data Structure { "messages": { "one": { "m1": { "name":

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

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

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

    "shiroyama", "message": "Hello Everyone!", "timestamp": 1459361875337 }, "m2": { ... }, "m3": { ... } }, "two": { ... }, "three": { ... } } } Hello Everyone!
  71. 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": { ... } } } }
  72. 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
  73. 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
  74. Security Rules

  75. Security Rules { "rules": { "messages": { ".read": true, ".write":

    "auth != null" } } }
  76. Security Rules { "rules": { "messages": { ".read": true, ".write":

    "auth != null" } } }
  77. Security Rules { "rules": { "messages": { ".read": true, ".write":

    "auth != null" } } }
  78. Security Rules { "rules": { "messages": { ".read": true, ".write":

    "auth != null" } } }
  79. • 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
  80. Client Side

  81. Install Firebase Plugin

  82. Connect Realtime Database

  83. Add Realtime Database https://firebase.google.com/docs/database/android/start/

  84. google-services.json

  85. Firebase Console https://console.firebase.google.com/

  86. Database Reference FirebaseDatabase database = FirebaseDatabase.getInstance(); DatabaseReference messages = database.getReference("messages");

  87. Database Reference FirebaseDatabase database = FirebaseDatabase.getInstance(); DatabaseReference messages = database.getReference("messages");

  88. Database Reference FirebaseDatabase database = FirebaseDatabase.getInstance(); DatabaseReference messages = database.getReference("messages");

    NFTTBHFT
  89. Message @IgnoreExtraProperties public class Message { private String name; private

    String message; private long timestamp; public Message() { } // getters and setters }
  90. 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); } });
  91. 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); } });
  92. 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); } });
  93. 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
  94. { "messages": { "one": { "-Kei_CdbvKoms35KcA8p": { "name": "shiroyama", "message":

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

    "Hello Everyone!", "timestamp": 1459361875337 }, "m2": { ... }, "m3": { ... } }, "two": { ... }, "three": { ... } } } Hello Everyone! HFOFSBUFECZ QVTI  Send Message
  96. 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); } });
  97. 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
  98. Send Message

  99. 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
  100. 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
  101. 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
  102. 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
  103. onChildAdded() @Override public void onChildAdded(DataSnapshot snap, String s) { Message

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

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

    message = snap.getValue(Message.class); Log.d(TAG, message.getMessage()); } 5ZQF4BGF
  106. onChildAdded() D/Message: Hello Everyone! D/Message: How are you today?

  107. Sort and Filter messages.orderByChild("timestamp") .limitToFirst(50); messages.addChildEventListener(listener);

  108. Sort and Filter messages.orderByChild("timestamp") .limitToFirst(50); messages.addChildEventListener(listener);

  109. Sort and Filter messages.orderByChild("timestamp") .limitToFirst(50); messages.addChildEventListener(listener);

  110. Remove Listener @Override protected void onPause() { messages.removeEventListener(listener); super.onPause(); }

  111. Remove Listener @Override protected void onPause() { messages.removeEventListener(listener); super.onPause(); }

  112. • 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
  113. • Pull-to-Refresh • ValueEvent is triggered at last • Desc

    Ordering • setPrioritiy(timestamp * -1) • or create "desc_timestamp" property Realtime Database Tips
  114. • srym/FirebaseRealTimeChat • https://github.com/srym/FirebaseRealTimeChat • Realtime Database • Cloud Storage

    • Remote Config Realtime Database Sample
  115. Summary

  116. Good Points • Offline Support and Sync • Easy and

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

    point • No source code! • Tricky ordering limitation • Storage and Traffic fee could be expensive
  118. Realm Mobile Platform

  119. • Alternative to SQLite and Core Data • ORMapper •

    Cross Platform • https://realm.io/products/realm-mobile-database/ Realm Mobile Database
  120. Try it!

  121. 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' } }
  122. 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' } }
  123. App's build.gradle apply plugin: 'com.android.application' apply plugin: 'realm-android' android {

    }
  124. App's build.gradle apply plugin: 'com.android.application' apply plugin: 'realm-android' android {

    }
  125. Custom Application public class MyApplication extends Application { @Override public

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

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

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

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

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

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

    name; private String message; private long timestamp; public Message() { } // getters & setters } OFDFTTBSZ QSPYZUP OBUJWFDPEF
  132. 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); } }); } });
  133. 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); } }); } });
  134. 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); } }); } });
  135. 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); } }); } });
  136. 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); } }); } });
  137. 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); } });
  138. or

  139. 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); } });
  140. 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); } });
  141. 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); } });
  142. • Realm#executeTransaction • Realm#executeTransactionAsync Write to Realm

  143. 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()); } }
  144. 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()); } }
  145. 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()); } }
  146. 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()); } }
  147. 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()); } }
  148. 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);
  149. 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);
  150. 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);
  151. Realm Browser

  152. Realm Mobile Platform

  153. Realm Mobile Platform Image from https://realm.io/news/introducing-realm-mobile-platform/

  154. • Offline Support • Realtime Sync • Authentication • Access

    Control Realm Mobile Platform
  155. 3FBMN .PCJMF %BUBCBTF 3FBMN .PCJMF %BUBCBTF Realm Mobile Platform 3FBMN

    0CKFDU 4FSWFS
  156. 3FBMN 0CKFDU 4FSWFS %BUB 8SJUUFO $BMMCBDL send "hello" "hello" written!

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

    Platform 3FBMN 0CKFDU 4FSWFS 3FBMN .PCJMF %BUBCBTF 3FBMN .PCJMF %BUBCBTF
  158. %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
  159. Realm Object Server

  160. • https://realm.io/docs/realm-object-server/ • macOS • RHEL/CentOS 6 • Ubuntu 16.04

    • AWS/AMI • Developer Edition is FREE Realm Object Server
  161. • 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
  162. • http://<YOUR-SERVER>:9080/#!/setup Realm Object Server

  163. Realm Browser cat /etc/realm/admin_token.base64

  164. Client Side Modification

  165. App's build.gradle apply plugin: 'com.android.application' apply plugin: 'realm-android' android {

    } realm { syncEnabled = true }
  166. App's build.gradle apply plugin: 'com.android.application' apply plugin: 'realm-android' android {

    } realm { syncEnabled = true }
  167. 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) {} });
  168. 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
  169. 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) {} });
  170. 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) {} });
  171. 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) {} });
  172. 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) {} });
  173. 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) {} });
  174. That's all!

  175. Fine-grained Collection Notification OrderedRealmCollectionChangeListener<RealmResults<Message>> listener = new OrderedRealmCollectionChangeListener<RealmResults<Message>>() { @Override

    public void onChange(RealmResults<Message> collection, OrderedCollectionChangeSet changeSet) { } };
  176. Fine-grained Collection Notification OrderedRealmCollectionChangeListener<RealmResults<Message>> listener = new OrderedRealmCollectionChangeListener<RealmResults<Message>>() { @Override

    public void onChange(RealmResults<Message> collection, OrderedCollectionChangeSet changeSet) { } };
  177. Fine-grained Collection Notification OrderedRealmCollectionChangeListener<RealmResults<Message>> listener = new OrderedRealmCollectionChangeListener<RealmResults<Message>>() { @Override

    public void onChange(RealmResults<Message> collection, OrderedCollectionChangeSet changeSet) { } };
  178. 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);
  179. 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);
  180. 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);
  181. 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);
  182. Fine-grained Collection Notification • https://realm.io/news/realm-java-3-0-collection- notifications/ • Receive detailed callback

    • Efficient • RealmRecyclerViewAdapter is using this
  183. 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); } });
  184. 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
  185. 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
  186. 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
  187. 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); } });
  188. Summary

  189. 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
  190. Bad Points • Not really, that much! Great! • No

    server hook on Developer Edition • Realm MAY affect App's Architecture • Live Object is sometimes tricky
  191. Samples • [realm-demos/realm-draw] • https://github.com/realm-demos/realm-draw

  192. Which one did you like?

  193. Thank You!

  194. Thanks • Illustrations by ͍΍͢ͱ΍ • http://www.irasutoya.com/ • Thank you

    so much!!! • All the audiences! Thank you!