Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

About Me • Fumihiko Shiroyama • Android App Developer • Nikkei Inc. • https://github.com/srym • https://twitter.com/fushiroyama

Slide 3

Slide 3 text

Realtime Apps

Slide 4

Slide 4 text

https://github.com/srym/FirebaseRealTimeChat

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

Realtime Apps' Upsides

Slide 7

Slide 7 text

Realtime Apps' Upsides • Enables Rich User Interaction • Server Side Message Pushing • No Pull-To-Refresh

Slide 8

Slide 8 text

Realtime Apps' Downsides

Slide 9

Slide 9 text

Realtime Apps' Downsides • Connection Problems • Dealing with Poor Signal • Hassle with Retransmission • Complex Infrastructure • Proxy Problems etc, etc...

Slide 10

Slide 10 text

Realtime Libraries

Slide 11

Slide 11 text

Realtime Libraries • WebSocket

Slide 12

Slide 12 text

Realtime Libraries • WebSocket • Firebase Realtime Database

Slide 13

Slide 13 text

Realtime Libraries • WebSocket • Firebase Realtime Database • Realm Mobile Platform

Slide 14

Slide 14 text

WebSocket

Slide 15

Slide 15 text

WebSocket 8FC4PDLFU 4FSWFS 8FC4PDLFU $MJFOU 8FC4PDLFU $MJFOU

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

WebSocket • https://tools.ietf.org/html/rfc6455 • HTTP Upgrade

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

WebSocket • https://tools.ietf.org/html/rfc6455 • HTTP Upgrade • Light-Weight & Efficient • Minimum 2KB Header + Payload • Keeping Connection

Slide 23

Slide 23 text

OkHttp Supports WebSocket

Slide 24

Slide 24 text

Try it!

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

build.gradle dependencies { compile 'com.squareup.okhttp3:okhttp:3.6.0' }

Slide 28

Slide 28 text

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) {} };

Slide 29

Slide 29 text

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) {} };

Slide 30

Slide 30 text

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)"); }

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

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();

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

send JSON

Slide 40

Slide 40 text

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; } }

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

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)); } });

Slide 44

Slide 44 text

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)); } });

Slide 45

Slide 45 text

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)); } });

Slide 46

Slide 46 text

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()); }

Slide 47

Slide 47 text

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()); }

Slide 48

Slide 48 text

WebSocket Tips

Slide 49

Slide 49 text

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

Slide 50

Slide 50 text

WebSocket Tips • WebSocket does NOT have any room, namespace mechanism. • WebSocket does NOT support broadcasting out of the box. 4PNF *NQMFNFOUBUJPOTEP

Slide 51

Slide 51 text

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); } }); }

Slide 52

Slide 52 text

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); } }); }

Slide 53

Slide 53 text

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); } }); }

Slide 54

Slide 54 text

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); } }); }

Slide 55

Slide 55 text

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...

Slide 56

Slide 56 text

Summary

Slide 57

Slide 57 text

Good Points • Simple and Open and Free • Server Hook Point • Binary Support • Can Scale • Redis Pub/Sub

Slide 58

Slide 58 text

Bad Points • No Offline Support • No rooms / namespaces • No Broadcasting

Slide 59

Slide 59 text

Firebase Realtime Database

Slide 60

Slide 60 text

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

Slide 61

Slide 61 text

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

Slide 62

Slide 62 text

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

Slide 63

Slide 63 text

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

Slide 64

Slide 64 text

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

Slide 65

Slide 65 text

Data Structure

Slide 66

Slide 66 text

Data Structure • No SQL • JSON Tree • No "Columns" or "Relations"

Slide 67

Slide 67 text

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

Slide 68

Slide 68 text

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

Slide 69

Slide 69 text

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

Slide 70

Slide 70 text

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

Slide 71

Slide 71 text

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": { ... } } } }

Slide 72

Slide 72 text

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

Slide 73

Slide 73 text

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

Slide 74

Slide 74 text

Security Rules

Slide 75

Slide 75 text

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

Slide 76

Slide 76 text

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

Slide 77

Slide 77 text

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

Slide 78

Slide 78 text

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

Slide 79

Slide 79 text

• 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

Slide 80

Slide 80 text

Client Side

Slide 81

Slide 81 text

Install Firebase Plugin

Slide 82

Slide 82 text

Connect Realtime Database

Slide 83

Slide 83 text

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

Slide 84

Slide 84 text

google-services.json

Slide 85

Slide 85 text

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

Slide 86

Slide 86 text

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

Slide 87

Slide 87 text

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

Slide 88

Slide 88 text

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

Slide 89

Slide 89 text

Message @IgnoreExtraProperties public class Message { private String name; private String message; private long timestamp; public Message() { } // getters and setters }

Slide 90

Slide 90 text

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); } });

Slide 91

Slide 91 text

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); } });

Slide 92

Slide 92 text

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); } });

Slide 93

Slide 93 text

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

Slide 94

Slide 94 text

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

Slide 95

Slide 95 text

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

Slide 96

Slide 96 text

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); } });

Slide 97

Slide 97 text

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

Slide 98

Slide 98 text

Send Message

Slide 99

Slide 99 text

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

Slide 100

Slide 100 text

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

Slide 101

Slide 101 text

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

Slide 102

Slide 102 text

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

Slide 103

Slide 103 text

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

Slide 104

Slide 104 text

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

Slide 105

Slide 105 text

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

Slide 106

Slide 106 text

onChildAdded() D/Message: Hello Everyone! D/Message: How are you today?

Slide 107

Slide 107 text

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

Slide 108

Slide 108 text

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

Slide 109

Slide 109 text

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

Slide 110

Slide 110 text

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

Slide 111

Slide 111 text

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

Slide 112

Slide 112 text

• 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

Slide 113

Slide 113 text

• Pull-to-Refresh • ValueEvent is triggered at last • Desc Ordering • setPrioritiy(timestamp * -1) • or create "desc_timestamp" property Realtime Database Tips

Slide 114

Slide 114 text

• srym/FirebaseRealTimeChat • https://github.com/srym/FirebaseRealTimeChat • Realtime Database • Cloud Storage • Remote Config Realtime Database Sample

Slide 115

Slide 115 text

Summary

Slide 116

Slide 116 text

Good Points • Offline Support and Sync • Easy and Robust Security Rules • Good Interaction with other Firebase services • Easy to scale

Slide 117

Slide 117 text

Bad Points • No binary support • No server hook point • No source code! • Tricky ordering limitation • Storage and Traffic fee could be expensive

Slide 118

Slide 118 text

Realm Mobile Platform

Slide 119

Slide 119 text

• Alternative to SQLite and Core Data • ORMapper • Cross Platform • https://realm.io/products/realm-mobile-database/ Realm Mobile Database

Slide 120

Slide 120 text

Try it!

Slide 121

Slide 121 text

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' } }

Slide 122

Slide 122 text

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' } }

Slide 123

Slide 123 text

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

Slide 124

Slide 124 text

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

Slide 125

Slide 125 text

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

Slide 126

Slide 126 text

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

Slide 127

Slide 127 text

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

Slide 128

Slide 128 text

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

Slide 129

Slide 129 text

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

Slide 130

Slide 130 text

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

Slide 131

Slide 131 text

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

Slide 132

Slide 132 text

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); } }); } });

Slide 133

Slide 133 text

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); } }); } });

Slide 134

Slide 134 text

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); } }); } });

Slide 135

Slide 135 text

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); } }); } });

Slide 136

Slide 136 text

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); } }); } });

Slide 137

Slide 137 text

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); } });

Slide 138

Slide 138 text

or

Slide 139

Slide 139 text

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); } });

Slide 140

Slide 140 text

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); } });

Slide 141

Slide 141 text

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); } });

Slide 142

Slide 142 text

• Realm#executeTransaction • Realm#executeTransactionAsync Write to Realm

Slide 143

Slide 143 text

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

Slide 144

Slide 144 text

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

Slide 145

Slide 145 text

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

Slide 146

Slide 146 text

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

Slide 147

Slide 147 text

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

Slide 148

Slide 148 text

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

Slide 149

Slide 149 text

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

Slide 150

Slide 150 text

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

Slide 151

Slide 151 text

Realm Browser

Slide 152

Slide 152 text

Realm Mobile Platform

Slide 153

Slide 153 text

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

Slide 154

Slide 154 text

• Offline Support • Realtime Sync • Authentication • Access Control Realm Mobile Platform

Slide 155

Slide 155 text

3FBMN .PCJMF %BUBCBTF 3FBMN .PCJMF %BUBCBTF Realm Mobile Platform 3FBMN 0CKFDU 4FSWFS

Slide 156

Slide 156 text

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

Slide 157

Slide 157 text

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

Slide 158

Slide 158 text

%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

Slide 159

Slide 159 text

Realm Object Server

Slide 160

Slide 160 text

• https://realm.io/docs/realm-object-server/ • macOS • RHEL/CentOS 6 • Ubuntu 16.04 • AWS/AMI • Developer Edition is FREE Realm Object Server

Slide 161

Slide 161 text

• 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

Slide 162

Slide 162 text

• http://:9080/#!/setup Realm Object Server

Slide 163

Slide 163 text

Realm Browser cat /etc/realm/admin_token.base64

Slide 164

Slide 164 text

Client Side Modification

Slide 165

Slide 165 text

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

Slide 166

Slide 166 text

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

Slide 167

Slide 167 text

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) {} });

Slide 168

Slide 168 text

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

Slide 169

Slide 169 text

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) {} });

Slide 170

Slide 170 text

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) {} });

Slide 171

Slide 171 text

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) {} });

Slide 172

Slide 172 text

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) {} });

Slide 173

Slide 173 text

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) {} });

Slide 174

Slide 174 text

That's all!

Slide 175

Slide 175 text

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

Slide 176

Slide 176 text

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

Slide 177

Slide 177 text

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

Slide 178

Slide 178 text

Fine-grained Collection Notification public void onChange(RealmResults 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);

Slide 179

Slide 179 text

Fine-grained Collection Notification public void onChange(RealmResults 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);

Slide 180

Slide 180 text

Fine-grained Collection Notification public void onChange(RealmResults 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);

Slide 181

Slide 181 text

Fine-grained Collection Notification public void onChange(RealmResults 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);

Slide 182

Slide 182 text

Fine-grained Collection Notification • https://realm.io/news/realm-java-3-0-collection- notifications/ • Receive detailed callback • Efficient • RealmRecyclerViewAdapter is using this

Slide 183

Slide 183 text

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); } });

Slide 184

Slide 184 text

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

Slide 185

Slide 185 text

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

Slide 186

Slide 186 text

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

Slide 187

Slide 187 text

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); } });

Slide 188

Slide 188 text

Summary

Slide 189

Slide 189 text

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

Slide 190

Slide 190 text

Bad Points • Not really, that much! Great! • No server hook on Developer Edition • Realm MAY affect App's Architecture • Live Object is sometimes tricky

Slide 191

Slide 191 text

Samples • [realm-demos/realm-draw] • https://github.com/realm-demos/realm-draw

Slide 192

Slide 192 text

Which one did you like?

Slide 193

Slide 193 text

Thank You!

Slide 194

Slide 194 text

Thanks • Illustrations by ͍΍͢ͱ΍ • http://www.irasutoya.com/ • Thank you so much!!! • All the audiences! Thank you!