Android Realtime Library

Android Realtime Library

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

3cca191bf3064fd059ea2c3d6022afbd?s=128

Fumihiko Shiroyama

March 08, 2017
Tweet

Transcript

  1. 2.

    About Me • Fumihiko Shiroyama • Android App Developer •

    Nikkei Inc. • https://github.com/srym • https://twitter.com/fushiroyama
  2. 7.

    Realtime Apps' Upsides • Enables Rich User Interaction • Server

    Side Message Pushing • No Pull-To-Refresh
  3. 9.

    Realtime Apps' Downsides • Connection Problems • Dealing with Poor

    Signal • Hassle with Retransmission • Complex Infrastructure • Proxy Problems etc, etc...
  4. 14.
  5. 18.
  6. 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
  7. 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
  8. 24.
  9. 25.

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

    https://github.com/theturtle32/WebSocket- Node#server-example • $ node server-example.js
  10. 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
  11. 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) {} };
  12. 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) {} };
  13. 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)"); }
  14. 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
  15. 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
  16. 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
  17. 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
  18. 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();
  19. 39.
  20. 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; } }
  21. 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)); } });
  22. 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)); } });
  23. 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)); } });
  24. 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()); }
  25. 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()); }
  26. 49.

    WebSocket Tips • WebSocket does NOT have any room, namespace

    mechanism. • WebSocket does NOT support broadcasting out of the box.
  27. 50.

    WebSocket Tips • WebSocket does NOT have any room, namespace

    mechanism. • WebSocket does NOT support broadcasting out of the box. 4PNF *NQMFNFOUBUJPOTEP
  28. 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); } }); }
  29. 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); } }); }
  30. 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); } }); }
  31. 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); } }); }
  32. 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...
  33. 56.
  34. 57.

    Good Points • Simple and Open and Free • Server

    Hook Point • Binary Support • Can Scale • Redis Pub/Sub
  35. 58.

    Bad Points • No Offline Support • No rooms /

    namespaces • No Broadcasting
  36. 63.

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

    4ZOD 4ZOD 4ZOD $BMMCBDL $BMMCBDL send "hello" "hello" written! "hello" written!
  37. 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
  38. 66.
  39. 67.

    Data Structure { "messages": { "one": { "m1": { "name":

    "eclarke", "message": "The relay seems to be malfunctioning.", "timestamp": 1459361875337 }, "m2": { ... }, "m3": { ... } }, "two": { ... }, "three": { ... } } } .FTTBHF 5SFF
  40. 68.

    Data Structure { "messages": { "one": { "m1": { "name":

    "eclarke", "message": "The relay seems to be malfunctioning.", "timestamp": 1459361875337 }, "m2": { ... }, "m3": { ... } }, "two": { ... }, "three": { ... } } } $IBUSPPN POF
  41. 69.

    Data Structure { "messages": { "one": { "m1": { "name":

    "shiroyama", "message": "Hello Everyone!", "timestamp": 1459361875337 }, "m2": { ... }, "m3": { ... } }, "two": { ... }, "three": { ... } } } Hello Everyone!
  42. 70.

    Data Structure { "messages": { "one": { "m1": { "name":

    "shiroyama", "message": "Hello Everyone!", "timestamp": 1459361875337 }, "m2": { ... }, "m3": { ... } }, "two": { ... }, "three": { ... } } } Hello Everyone!
  43. 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": { ... } } } }
  44. 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
  45. 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
  46. 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
  47. 89.

    Message @IgnoreExtraProperties public class Message { private String name; private

    String message; private long timestamp; public Message() { } // getters and setters }
  48. 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); } });
  49. 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); } });
  50. 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); } });
  51. 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
  52. 94.

    { "messages": { "one": { "-Kei_CdbvKoms35KcA8p": { "name": "shiroyama", "message":

    "Hello Everyone!", "timestamp": 1459361875337 }, "m2": { ... }, "m3": { ... } }, "two": { ... }, "three": { ... } } } Hello Everyone! Send Message
  53. 95.

    { "messages": { "one": { "-Kei_CdbvKoms35KcA8p": { "name": "shiroyama", "message":

    "Hello Everyone!", "timestamp": 1459361875337 }, "m2": { ... }, "m3": { ... } }, "two": { ... }, "three": { ... } } } Hello Everyone! HFOFSBUFECZ QVTI  Send Message
  54. 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); } });
  55. 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
  56. 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
  57. 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
  58. 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
  59. 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
  60. 103.

    onChildAdded() @Override public void onChildAdded(DataSnapshot snap, String s) { Message

    message = snap.getValue(Message.class); Log.d(TAG, message.getMessage()); }
  61. 104.

    onChildAdded() @Override public void onChildAdded(DataSnapshot snap, String s) { Message

    message = snap.getValue(Message.class); Log.d(TAG, message.getMessage()); }
  62. 105.

    onChildAdded() @Override public void onChildAdded(DataSnapshot snap, String s) { Message

    message = snap.getValue(Message.class); Log.d(TAG, message.getMessage()); } 5ZQF4BGF
  63. 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
  64. 113.

    • Pull-to-Refresh • ValueEvent is triggered at last • Desc

    Ordering • setPrioritiy(timestamp * -1) • or create "desc_timestamp" property Realtime Database Tips
  65. 115.
  66. 116.

    Good Points • Offline Support and Sync • Easy and

    Robust Security Rules • Good Interaction with other Firebase services • Easy to scale
  67. 117.

    Bad Points • No binary support • No server hook

    point • No source code! • Tricky ordering limitation • Storage and Traffic fee could be expensive
  68. 119.

    • Alternative to SQLite and Core Data • ORMapper •

    Cross Platform • https://realm.io/products/realm-mobile-database/ Realm Mobile Database
  69. 120.
  70. 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' } }
  71. 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' } }
  72. 125.

    Custom Application public class MyApplication extends Application { @Override public

    void onCreate() { super.onCreate(); Realm.init(this); RealmLog.setLevel(Log.VERBOSE); } }
  73. 126.

    Custom Application public class MyApplication extends Application { @Override public

    void onCreate() { super.onCreate(); Realm.init(this); RealmLog.setLevel(Log.VERBOSE); } }
  74. 127.

    Custom Application public class MyApplication extends Application { @Override public

    void onCreate() { super.onCreate(); Realm.init(this); RealmLog.setLevel(Log.VERBOSE); } }
  75. 128.

    Message Model public class Message extends RealmObject { private String

    name; private String message; private long timestamp; public Message() { } // getters & setters }
  76. 129.

    Message Model public class Message extends RealmObject { private String

    name; private String message; private long timestamp; public Message() { } // getters & setters }
  77. 130.

    Message Model public class Message extends RealmObject { private String

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

    Message Model public class Message extends RealmObject { private String

    name; private String message; private long timestamp; public Message() { } // getters & setters } OFDFTTBSZ QSPYZUP OBUJWFDPEF
  79. 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); } }); } });
  80. 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); } }); } });
  81. 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); } }); } });
  82. 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); } }); } });
  83. 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); } }); } });
  84. 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); } });
  85. 138.

    or

  86. 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); } });
  87. 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); } });
  88. 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); } });
  89. 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()); } }
  90. 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()); } }
  91. 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()); } }
  92. 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()); } }
  93. 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()); } }
  94. 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);
  95. 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);
  96. 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);
  97. 156.

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

    Realm Mobile Platform 3FBMN .PCJMF %BUBCBTF 3FBMN .PCJMF %BUBCBTF
  98. 157.

    %BUB 8SJUUFO 4ZOD $BMMCBDL send "hello" "hello" written! Realm Mobile

    Platform 3FBMN 0CKFDU 4FSWFS 3FBMN .PCJMF %BUBCBTF 3FBMN .PCJMF %BUBCBTF
  99. 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
  100. 160.

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

    • AWS/AMI • Developer Edition is FREE Realm Object Server
  101. 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
  102. 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) {} });
  103. 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
  104. 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) {} });
  105. 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) {} });
  106. 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) {} });
  107. 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) {} });
  108. 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) {} });
  109. 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);
  110. 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);
  111. 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);
  112. 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);
  113. 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); } });
  114. 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
  115. 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
  116. 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
  117. 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); } });
  118. 188.
  119. 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
  120. 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
  121. 193.