$30 off During Our Annual Pro Sale. View Details »

Android Realtime Library

Android Realtime Library

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

Fumihiko Shiroyama

March 08, 2017
Tweet

More Decks by Fumihiko Shiroyama

Other Decks in Technology

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!