Slide 1

Slide 1 text

No content

Slide 2

Slide 2 text

No content

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

Scarlett Johansson

Slide 5

Slide 5 text

Scarlet

Slide 6

Slide 6 text

WebSocket

Slide 7

Slide 7 text

WebSocket

Slide 8

Slide 8 text

WebSocket

Slide 9

Slide 9 text

WebSocket

Slide 10

Slide 10 text

WebSocket

Slide 11

Slide 11 text

WebSocket // Create WebSocket connection. const socket = new WebSocket('ws://localhost:8080'); // Connection opened socket.addEventListener('open', function (event) { socket.send('Hello Server!'); }); // Listen for messages socket.addEventListener('message', function (event) { console.log('Message from server', data); });

Slide 12

Slide 12 text

WebSocket // Create WebSocket connection. const socket = new WebSocket('ws://localhost:8080'); // Connection opened socket.addEventListener('open', function (event) { socket.send('Hello Server!'); }); // Listen for messages socket.addEventListener('message', function (event) { console.log('Message from server', data); });

Slide 13

Slide 13 text

WebSocket // Create WebSocket connection. const socket = new WebSocket('ws://localhost:8080'); // Connection opened socket.addEventListener('open', function (event) { socket.send('Hello Server!'); }); // Listen for messages socket.addEventListener('message', function (event) { console.log('Message from server', data); });

Slide 14

Slide 14 text

WebSocket // Create WebSocket connection. const socket = new WebSocket('ws://localhost:8080'); // Connection opened socket.addEventListener('open', function (event) { socket.send('Hello Server!'); }); // Listen for messages socket.addEventListener('message', function (event) { console.log('Message from server', data); });

Slide 15

Slide 15 text

WebSocket // Create WebSocket connection. const socket = new WebSocket('ws://localhost:8080'); // Connection opened socket.addEventListener('open', function (event) { socket.send('Hello Server!'); }); // Listen for messages socket.addEventListener('message', function (event) { console.log('Message from server’, event.data); });

Slide 16

Slide 16 text

Why WebSocket?

Slide 17

Slide 17 text

Why WebSocket? • What problems does it solve? • How can it help you build a better app?

Slide 18

Slide 18 text

No content

Slide 19

Slide 19 text

No content

Slide 20

Slide 20 text

No content

Slide 21

Slide 21 text

No content

Slide 22

Slide 22 text

No content

Slide 23

Slide 23 text

No content

Slide 24

Slide 24 text

No content

Slide 25

Slide 25 text

No content

Slide 26

Slide 26 text

No content

Slide 27

Slide 27 text

No content

Slide 28

Slide 28 text

No content

Slide 29

Slide 29 text

Polling • Expensive • 90% empty response • HTTP headers • Long Latency • 2 second delay

Slide 30

Slide 30 text

Polling • Expensive • 90% empty response • HTTP headers • Long Latency • 2 second delay

Slide 31

Slide 31 text

No content

Slide 32

Slide 32 text

No content

Slide 33

Slide 33 text

No content

Slide 34

Slide 34 text

No content

Slide 35

Slide 35 text

WebSocket Integration

Slide 36

Slide 36 text

WebSocket Integration

Slide 37

Slide 37 text

No content

Slide 38

Slide 38 text

No content

Slide 39

Slide 39 text

No content

Slide 40

Slide 40 text

RecyclerView Button EditText

Slide 41

Slide 41 text

class ChatViewModel { val messages: LiveData> val isConnected: LiveData fun sendMessage(message: Message) } RecyclerView Button EditText

Slide 42

Slide 42 text

class ChatViewModel { val messages: LiveData> val isConnected: LiveData fun sendMessage(message: Message) } class OkHttp { //... }

Slide 43

Slide 43 text

WebSocket with OkHttp class OkHttp { //... }

Slide 44

Slide 44 text

WebSocket with OkHttp class OkHttp { fun newWebSocket( request: Request, listener: WebSocketListener ): WebSocket }

Slide 45

Slide 45 text

WebSocket with OkHttp class OkHttp { fun newWebSocket( request: Request, listener: WebSocketListener ): WebSocket } interface WebSocketListener { fun onOpen(response: Response) fun onMessage(text: Message) fun onMessage(bytes: ByteMessage) fun onClosed(code: Int, reason: Message) fun onFailure(t: Throwable) } interface WebSocket { fun send(text: Message) fun send(bytes: ByteMessage) fun close(code: Int, reason: Message?) fun cancel() }

Slide 46

Slide 46 text

class ChatViewModel { val messages: LiveData> val isConnected: LiveData fun sendMessage(message: Message) } class OkHttp { //... } interface WebSocketListener { //... } interface WebSocket { //... }

Slide 47

Slide 47 text

ChatView ChatViewModel OkHttp val messages val isConnected sendMessage() WebSocketListener WebSocket newWebSocket() Socket RecyclerView EditText SendButton

Slide 48

Slide 48 text

ChatView ChatViewModel OkHttp val messages val isConnected sendMessage() WebSocketListener WebSocket newWebSocket() Socket RecyclerView EditText SendButton

Slide 49

Slide 49 text

ChatView ChatViewModel OkHttp val messages val isConnected sendMessage() WebSocketListener WebSocket newWebSocket() Socket RecyclerView EditText SendButton On Opened

Slide 50

Slide 50 text

ChatView ChatViewModel OkHttp val messages val isConnected sendMessage() WebSocketListener WebSocket newWebSocket() Socket RecyclerView EditText SendButton On Message

Slide 51

Slide 51 text

ChatView ChatViewModel OkHttp val messages val isConnected sendMessage() WebSocketListener WebSocket newWebSocket() Socket RecyclerView EditText SendButton

Slide 52

Slide 52 text

ChatView ChatViewModel OkHttp val messages val isConnected sendMessage() WebSocketListener WebSocket newWebSocket() Socket RecyclerView EditText SendButton

Slide 53

Slide 53 text

Error Handling • Straightforward when the connection is open

Slide 54

Slide 54 text

Error Handling • Straightforward when the connection is open • But a connection may fail

Slide 55

Slide 55 text

Error Handling • Unstable network • Server closure

Slide 56

Slide 56 text

Error Handling

Slide 57

Slide 57 text

Error Handling

Slide 58

Slide 58 text

Error Handling

Slide 59

Slide 59 text

Error Handling

Slide 60

Slide 60 text

Error Handling

Slide 61

Slide 61 text

Error Handling

Slide 62

Slide 62 text

Error Handling

Slide 63

Slide 63 text

Error Handling

Slide 64

Slide 64 text

Error Handling

Slide 65

Slide 65 text

Error Handling

Slide 66

Slide 66 text

Error Handling

Slide 67

Slide 67 text

Error Handling

Slide 68

Slide 68 text

Error Handling

Slide 69

Slide 69 text

Error Handling

Slide 70

Slide 70 text

ChatView ChatViewModel OkHttp val messages val isConnected sendMessage() WebSocketListener WebSocket newWebSocket() Socket RecyclerView EditText SendButton

Slide 71

Slide 71 text

ChatView ChatViewModel OkHttp val messages val isConnected sendMessage() WebSocketListener WebSocket newWebSocket() Socket RecyclerView EditText SendButton Timer schedule() cancel() TimerListener

Slide 72

Slide 72 text

ChatView ChatViewModel OkHttp val messages val isConnected sendMessage() WebSocketListener WebSocket newWebSocket() Socket RecyclerView EditText SendButton Timer schedule() cancel() TimerListener On Opened

Slide 73

Slide 73 text

ChatView ChatViewModel OkHttp val messages val isConnected sendMessage() WebSocketListener WebSocket newWebSocket() Socket RecyclerView EditText SendButton Timer schedule() cancel() TimerListener On Failed

Slide 74

Slide 74 text

ChatView ChatViewModel OkHttp val messages val isConnected sendMessage() WebSocketListener WebSocket newWebSocket() Socket RecyclerView EditText SendButton Timer schedule() cancel() TimerListener On Timer Tick

Slide 75

Slide 75 text

ChatView ChatViewModel OkHttp val messages val isConnected sendMessage() WebSocketListener WebSocket newWebSocket() Socket RecyclerView EditText SendButton Timer schedule() cancel() TimerListener

Slide 76

Slide 76 text

Android Lifecycle • Your app should minimize its activity when in the background and when the device is running on battery power.

Slide 77

Slide 77 text

ChatView ChatViewModel OkHttp val messages val isConnected sendMessage() WebSocketListener WebSocket newWebSocket() Socket RecyclerView EditText SendButton Timer schedule() cancel() TimerListener

Slide 78

Slide 78 text

ChatView ChatViewModel OkHttp val messages val isConnected sendMessage() WebSocketListener WebSocket newWebSocket() Socket RecyclerView EditText SendButton Timer LifecycleOwner schedule() cancel() LifecycleListener TimerListener

Slide 79

Slide 79 text

ChatView ChatViewModel OkHttp val messages val isConnected sendMessage() WebSocketListener WebSocket newWebSocket() Socket RecyclerView EditText SendButton Timer LifecycleOwner schedule() cancel() LifecycleListener TimerListener On Lifecycle Start

Slide 80

Slide 80 text

ChatView ChatViewModel OkHttp val messages val isConnected sendMessage() WebSocketListener WebSocket newWebSocket() Socket RecyclerView EditText SendButton Timer LifecycleOwner schedule() cancel() LifecycleListener TimerListener On Lifecycle Stop

Slide 81

Slide 81 text

ChatView ChatViewModel OkHttp val messages val isConnected sendMessage() WebSocketListener WebSocket newWebSocket() Socket RecyclerView EditText SendButton Timer LifecycleOwner schedule() cancel() LifecycleListener TimerListener On Lifecycle Start

Slide 82

Slide 82 text

ChatView ChatViewModel OkHttp val messages val isConnected sendMessage() WebSocketListener WebSocket newWebSocket() Socket RecyclerView EditText SendButton Timer LifecycleOwner schedule() cancel() LifecycleListener TimerListener On Failed

Slide 83

Slide 83 text

ChatView ChatViewModel OkHttp val messages val isConnected sendMessage() WebSocketListener WebSocket newWebSocket() Socket RecyclerView EditText SendButton Timer LifecycleOwner schedule() cancel() LifecycleListener TimerListener On Timer Tick

Slide 84

Slide 84 text

ChatView ChatViewModel OkHttp val messages val isConnected sendMessage() WebSocketListener WebSocket newWebSocket() Socket RecyclerView EditText SendButton Timer LifecycleOwner schedule() cancel() LifecycleListener TimerListener On Failed

Slide 85

Slide 85 text

ChatView ChatViewModel OkHttp val messages val isConnected sendMessage() WebSocketListener WebSocket newWebSocket() Socket RecyclerView EditText SendButton Timer LifecycleOwner schedule() cancel() LifecycleListener TimerListener On Lifecycle Stop

Slide 86

Slide 86 text

ChatView ChatViewModel OkHttp val messages val isConnected sendMessage() WebSocketListener WebSocket newWebSocket() Socket RecyclerView EditText SendButton Timer LifecycleOwner schedule() cancel() LifecycleListener TimerListener

Slide 87

Slide 87 text

No content

Slide 88

Slide 88 text

ChatView ChatViewModel OkHttp val messages val isConnected sendMessage() WebSocketListener WebSocket newWebSocket() Socket RecyclerView EditText SendButton Timer LifecycleOwner schedule() cancel() LifecycleListener TimerListener

Slide 89

Slide 89 text

1000+ LOC

Slide 90

Slide 90 text

Scarlet is here to help

Slide 91

Slide 91 text

ChatView ChatViewModel OkHttp val messages val isConnected sendMessage() WebSocketListener WebSocket newWebSocket() Socket RecyclerView EditText SendButton Timer LifecycleOwner schedule() cancel() LifecycleListener TimerListener

Slide 92

Slide 92 text

ChatView ChatViewModel Scarlet val messages val isConnected sendMessage() observeIncomingMessage() sendMessage observeWebSocketEvent() Socket RecyclerView EditText SendButton

Slide 93

Slide 93 text

Scarlet • Boilerplate Code • Spaghetti Code

Slide 94

Slide 94 text

Declarative • Describing “What” instead of “How”

Slide 95

Slide 95 text

ChatView ChatViewModel Scarlet val messages val isConnected sendMessage() observeIncomingMessage() sendMessage observeWebSocketEvent() Socket RecyclerView EditText SendButton

Slide 96

Slide 96 text

odel Scarlet observeIncomingMessage() sendMessage observeWebSocketEvent() Socket interface ChatService { @Receive fun observeWebSocketEvent( ): Flowable @Receive fun observeIncomingMessage(): Flowable @Send fun sendMessage(message: Message) } val chatService = scarlet.create()

Slide 97

Slide 97 text

Declarative interface ChatService { @Receive fun observeWebSocketEvent( ): Flowable @Receive fun observeIncomingMessage(): Flowable @Send fun sendMessage(message: Message) } val chatService = scarlet.create()

Slide 98

Slide 98 text

Declarative interface ChatService { @Send fun sendMessage(message: Message) @Receive fun observeWebSocketEvent( ): Flowable @Receive fun observeIncomingMessage(): Flowable } val chatService = scarlet.create()

Slide 99

Slide 99 text

Declarative interface ChatService { @Send fun sendMessage(message: Message) @Receive fun observeWebSocketEvent( ): Flowable @Receive fun observeIncomingMessage(): Flowable } val chatService = scarlet.create()

Slide 100

Slide 100 text

Declarative interface ChatService { @Send fun sendMessage(message: Message) @Receive fun observeWebSocketEvent( ): Flowable @Receive fun observeIncomingMessage(): Flowable } val chatService = scarlet.create() chatService.sendMessage(Message("subscribe"))

Slide 101

Slide 101 text

Declarative interface ChatService { @Send fun sendMessage(message: Message) @Receive fun observeWebSocketEvent( ): Flowable @Receive fun observeIncomingMessage(): Flowable } val chatService = scarlet.create()

Slide 102

Slide 102 text

Declarative interface ChatService { @Send fun sendMessage(message: Message) @Receive fun observeWebSocketEvent( ): Flowable @Receive fun observeIncomingMessage(): Flowable } val chatService = scarlet.create() chatService.observeWebSocketEvent() .subscribe { Log.d(“Received WebSocketEvent ${it}”) }

Slide 103

Slide 103 text

Declarative interface ChatService { @Send fun sendMessage(message: Message) @Receive fun observeWebSocketEvent( ): Flowable @Receive fun observeIncomingMessage(): Flowable } val chatService = scarlet.create()

Slide 104

Slide 104 text

Declarative interface ChatService { @Send fun sendMessage(message: Message) @Receive fun observeWebSocketEvent( ): Flowable @Receive fun observeIncomingMessage(): Flowable } val chatService = scarlet.create() chatService.observeIncomingMessage() .subscribe { Log.d(“Received message ${it}”) }

Slide 105

Slide 105 text

Declarative interface ChatService { @Send fun sendMessage(message: Message) @Receive fun observeWebSocketEvent( ): Flowable @Receive fun observeIncomingMessage(): Flowable } val chatService = scarlet.create()

Slide 106

Slide 106 text

Declarative class ChatViewModel( private val chatService: ChatService ) : ViewModel() { val messages: LiveData> = LiveDataReactiveStreams.fromPublisher( chatService.observeIncomingMessage().toList() ) val isConnected: LiveData = LiveDataReactiveStreams.fromPublisher( chatService.observeWebSocketEvent() .map { it == WebSocket.Event.OnConnectionOpen } ) fun sendMessage(message: Message) { chatService.sendMessage(message) } }

Slide 107

Slide 107 text

Declarative class ChatViewModel( private val chatService: ChatService ) : ViewModel() { val messages: LiveData> = LiveDataReactiveStreams.fromPublisher( chatService.observeIncomingMessage().toList() ) val isConnected: LiveData = LiveDataReactiveStreams.fromPublisher( chatService.observeWebSocketEvent() .map { it == WebSocket.Event.OnConnectionOpen } ) fun sendMessage(message: Message) { chatService.sendMessage(message) } }

Slide 108

Slide 108 text

Declarative class ChatViewModel( private val chatService: ChatService ) : ViewModel() { val messages: LiveData> = LiveDataReactiveStreams.fromPublisher( chatService.observeIncomingMessage().scanToList() ) val isConnected: LiveData = LiveDataReactiveStreams.fromPublisher( chatService.observeWebSocketEvent() .map { it == WebSocket.Event.OnConnectionOpen } ) fun sendMessage(message: Message) { chatService.sendMessage(message) } }

Slide 109

Slide 109 text

Declarative class ChatViewModel( private val chatService: ChatService ) : ViewModel() { val messages: LiveData> = LiveDataReactiveStreams.fromPublisher( chatService.observeIncomingMessage().scanToList() ) val isConnected: LiveData = LiveDataReactiveStreams.fromPublisher( chatService.observeWebSocketEvent() .map { it == WebSocket.Event.OnConnectionOpen } ) fun sendMessage(message: Message) { chatService.sendMessage(message) } }

Slide 110

Slide 110 text

Declarative class ChatViewModel( private val chatService: ChatService ) : ViewModel() { val messages: LiveData> = LiveDataReactiveStreams.fromPublisher( chatService.observeIncomingMessage().scanToList() ) val isConnected: LiveData = LiveDataReactiveStreams.fromPublisher( chatService.observeWebSocketEvent() .map { it == WebSocket.Event.OnConnectionOpen } ) fun sendMessage(message: Message) { chatService.sendMessage(message) } }

Slide 111

Slide 111 text

Declarative interface ChatService { @Send fun sendMessage(message: Message) @Receive fun observeWebSocketEvent( ): Flowable @Receive fun observeIncomingMessage(): Flowable } class ChatViewModel( private val chatService: ChatService ) : ViewModel() { val messages: LiveData> = LiveDataReactiveStreams.fromPublisher( chatService.observeIncomingMessage().scanToList() ) val isConnected: LiveData = LiveDataReactiveStreams.fromPublisher( chatService.observeWebSocketEvent() .map { it == WebSocket.Event.OnConnectionOpen } ) fun sendMessage(message: Message) { chatService.sendMessage(message) } }

Slide 112

Slide 112 text

Modular • Code that is separated into independent components

Slide 113

Slide 113 text

odel Scarlet observeIncomingMessage() sendMessage observeWebSocketEvent() Socket interface ChatService { @Receive fun observeWebSocketEvent( ): Flowable @Receive fun observeIncomingMessage(): Flowable @Send fun sendMessage(message: Message) } val chatService = scarlet.create()

Slide 114

Slide 114 text

Scarlet observeIncomingMessage() sendMessage observeWebSocketEvent()

Slide 115

Slide 115 text

Scarlet observeIncomingMessage() sendMessage observeWebSocketEvent() OkHttp WebSocketListener WebSocket newWebSocket() Socket

Slide 116

Slide 116 text

Scarlet observeIncomingMessage() sendMessage observeWebSocketEvent() WebSocketFactory WebSocket create() OkHttp WebSocketListener WebSocket newWebSocket() Socket

Slide 117

Slide 117 text

Scarlet observeIncomingMessage() sendMessage observeWebSocketEvent() WebSocketFactory WebSocket create() BackoffStrategy backoffDurationMillisAt() OkHttp WebSocketListener WebSocket newWebSocket() Socket

Slide 118

Slide 118 text

Backoff Strategy

Slide 119

Slide 119 text

Scarlet observeIncomingMessage() sendMessage observeWebSocketEvent() WebSocketFactory WebSocket create() BackoffStrategy backoffDurationMillisAt() OkHttp WebSocketListener WebSocket newWebSocket() Socket

Slide 120

Slide 120 text

Scarlet observeIncomingMessage() sendMessage observeWebSocketEvent() WebSocketFactory WebSocket create() BackoffStrategy backoffDurationMillisAt() OkHttp WebSocketListener WebSocket newWebSocket() Socket LifecycleListener LifecycleOwner

Slide 121

Slide 121 text

Scarlet observeIncomingMessage() sendMessage observeWebSocketEvent() WebSocketFactory WebSocket create() BackoffStrategy Lifecycle subscribe() backoffDurationMillisAt() OkHttp WebSocketListener WebSocket newWebSocket() Socket LifecycleListener LifecycleOwner

Slide 122

Slide 122 text

Lifecycle • Started • Connect and keep retrying • Stopped • Disconnect

Slide 123

Slide 123 text

Lifecycle

Slide 124

Slide 124 text

Lifecycle

Slide 125

Slide 125 text

Lifecycle

Slide 126

Slide 126 text

Lifecycle

Slide 127

Slide 127 text

Scarlet observeIncomingMessage() sendMessage observeWebSocketEvent() WebSocketFactory WebSocket create() BackoffStrategy Lifecycle subscribe() backoffDurationMillisAt() OkHttp WebSocketListener WebSocket newWebSocket() Socket LifecycleListener LifecycleOwner

Slide 128

Slide 128 text

Scarlet observeIncomingMessage() sendMessage observeWebSocketEvent() WebSocketFactory WebSocket create() BackoffStrategy Lifecycle subscribe() backoffDurationMillisAt() OkHttp

Slide 129

Slide 129 text

Scarlet observeIncomingMessage() sendMessage observeWebSocketEvent() WebSocketFactory WebSocket create() BackoffStrategy Lifecycle subscribe() backoffDurationMillisAt() MockWebServer OkHttp

Slide 130

Slide 130 text

Scarlet observeIncomingMessage() sendMessage observeWebSocketEvent() WebSocketFactory WebSocket create() BackoffStrategy Lifecycle subscribe() backoffDurationMillisAt() Customized MockWebServer OkHttp

Slide 131

Slide 131 text

Scarlet observeIncomingMessage() sendMessage observeWebSocketEvent() WebSocketFactory WebSocket create() BackoffStrategy Lifecycle subscribe() backoffDurationMillisAt() Customized Linear Exponential ExponentialWithJitter Customized MockWebServer OkHttp

Slide 132

Slide 132 text

Scarlet observeIncomingMessage() sendMessage observeWebSocketEvent() WebSocketFactory WebSocket create() BackoffStrategy Lifecycle subscribe() backoffDurationMillisAt() Customized Linear Exponential ExponentialWithJitter Customized MockWebServer Android OkHttp

Slide 133

Slide 133 text

Scarlet observeIncomingMessage() sendMessage observeWebSocketEvent() WebSocketFactory WebSocket create() BackoffStrategy Lifecycle subscribe() backoffDurationMillisAt() Customized Linear Exponential ExponentialWithJitter Customized MockWebServer Android Customized OkHttp

Slide 134

Slide 134 text

Scarlet observeIncomingMessage() sendMessage observeWebSocketEvent() interface ChatService { @Receive fun observeWebSocketEvent( ): Flowable @Receive fun observeIncomingMessage(): Flowable @Send fun sendMessage(message: Message) } val chatService = scarlet.create()

Slide 135

Slide 135 text

Scarlet observeIncomingMessage() sendMessage observeWebSocketEvent() interface ChatService { @Receive fun observeWebSocketEvent( ): Flowable @Receive fun observeIncomingMessage(): Flowable @Send fun sendMessage(message: Message) } val chatService = scarlet.create()

Slide 136

Slide 136 text

Modular scarlet.create()

Slide 137

Slide 137 text

Modular const val URL = "wss://ws.tinder.com" val scarlet = Scarlet.Builder() .webSocketFactory(okHttpClient.newWebSocketFactory(URL)) .backoffStrategy(LinearBackoffStrategy()) .lifecycle(AndroidLifecycle.ofApplicationForeground(app)) .build()

Slide 138

Slide 138 text

Modular const val URL = "wss://ws.tinder.com" val scarlet = Scarlet.Builder() .webSocketFactory(okHttpClient.newWebSocketFactory(URL)) .backoffStrategy(LinearBackoffStrategy()) .lifecycle(AndroidLifecycle.ofApplicationForeground(app)) .build()

Slide 139

Slide 139 text

Modular const val URL = "wss://ws.tinder.com" val scarlet = Scarlet.Builder() .webSocketFactory(okHttpClient.newWebSocketFactory(URL)) .backoffStrategy(LinearBackoffStrategy()) .lifecycle(AndroidLifecycle.ofApplicationForeground(app)) .build()

Slide 140

Slide 140 text

Modular const val URL = "wss://ws.tinder.com" val scarlet = Scarlet.Builder() .webSocketFactory(okHttpClient.newWebSocketFactory(URL)) .backoffStrategy(LinearBackoffStrategy()) .lifecycle(AndroidLifecycle.ofApplicationForeground(app)) .build()

Slide 141

Slide 141 text

Modular const val URL = "wss://ws.tinder.com" val scarlet = Scarlet.Builder() .webSocketFactory(okHttpClient.newWebSocketFactory(URL)) .backoffStrategy(LinearBackoffStrategy()) .lifecycle(AndroidLifecycle.ofApplicationForeground(app)) .build()

Slide 142

Slide 142 text

Modular interface ChatService { @Receive fun observeWebSocketEvent( ): Flowable @Receive fun observeIncomingMessage(): Flowable @Send fun sendMessage(message: Message) } const val URL = "wss://ws.tinder.com" val scarlet = Scarlet.Builder() .webSocketFactory(okHttpClient.newWebSocketFactory(URL)) .backoffStrategy(LinearBackoffStrategy()) .lifecycle(AndroidLifecycle.ofApplicationForeground(app)) .build() val chatService = scarlet.create()

Slide 143

Slide 143 text

Modular interface ChatService { @Receive fun observeWebSocketEvent( ): Flowable @Receive fun observeIncomingMessage(): Flowable @Send fun sendMessage(message: Message) } const val URL = "wss://ws.tinder.com" val scarlet = Scarlet.Builder() .webSocketFactory(okHttpClient.newWebSocketFactory(URL)) .backoffStrategy(LinearBackoffStrategy()) .lifecycle(AndroidLifecycle.ofApplicationForeground(app)) .build() val chatService = scarlet.create()

Slide 144

Slide 144 text

Message & Stream • 2 more plugins • Serialization & deserialization • Asynchronous abstraction

Slide 145

Slide 145 text

Scarlet observeIncomingMessage() sendMessage observeWebSocketEvent() WebSocketFactory WebSocket create() BackoffStrategy Lifecycle subscribe() backoffDurationMillisAt() Customized Linear Exponential ExponentialWithJitter Customized MockWebServer Android Customized OkHttp

Slide 146

Slide 146 text

Scarlet observeIncomingMessage() sendMessage observeWebSocketEvent() WebSocketFactory OkHttp BackoffStrategy Lifecycle Android MockWebServer Linear Exponential ExponentialWithJitter

Slide 147

Slide 147 text

Scarlet observeIncomingMessage() sendMessage observeWebSocketEvent() Moshi Gson Protobuf WebSocketFactory OkHttp BackoffStrategy Lifecycle Android MockWebServer Linear Exponential ExponentialWithJitter

Slide 148

Slide 148 text

Scarlet observeIncomingMessage() sendMessage observeWebSocketEvent() MessageAdapter fromMessage() Moshi Gson Protobuf toMessage() WebSocketFactory OkHttp BackoffStrategy Lifecycle Android MockWebServer Linear Exponential ExponentialWithJitter

Slide 149

Slide 149 text

Scarlet observeIncomingMessage() sendMessage observeWebSocketEvent() MessageAdapter fromMessage() Moshi Gson Protobuf toMessage() RxJava Kotlin Coroutine WebSocketFactory OkHttp BackoffStrategy Lifecycle Android MockWebServer Linear Exponential ExponentialWithJitter

Slide 150

Slide 150 text

Scarlet observeIncomingMessage() sendMessage observeWebSocketEvent() MessageAdapter fromMessage() Moshi Gson Protobuf toMessage() StreamAdapter adapt() RxJava Kotlin Coroutine WebSocketFactory OkHttp BackoffStrategy Lifecycle Android MockWebServer Linear Exponential ExponentialWithJitter

Slide 151

Slide 151 text

Modular val scarlet = Scarlet.Builder() .webSocketFactory(okHttpClient.newWebSocketFactory(URL)) .backoffStrategy(LinearBackoffStrategy()) .lifecycle(AndroidLifecycle.ofApplicationForeground(app)) .addMessageAdapterFactory(MoshiMessageAdapter.Factory()) .addStreamAdapterFactory(RxJava2StreamAdapter.Factory()) .build()

Slide 152

Slide 152 text

Modular val scarlet = Scarlet.Builder() .webSocketFactory(okHttpClient.newWebSocketFactory(URL)) .backoffStrategy(LinearBackoffStrategy()) .lifecycle(AndroidLifecycle.ofApplicationForeground(app)) .addMessageAdapterFactory(MoshiMessageAdapter.Factory()) .addStreamAdapterFactory(RxJava2StreamAdapter.Factory()) .build()

Slide 153

Slide 153 text

Maintaining Scarlet • Easy to maintain

Slide 154

Slide 154 text

Changing Server API • Sending Typing Indicator • 2 loc

Slide 155

Slide 155 text

Changing Server API • Sending Typing Indicator • 2 loc

Slide 156

Slide 156 text

Changing Server API interface ChatService { @Receive fun observeWebSocketEvent( ): Flowable @Receive fun observeIncomingMessage(): Flowable @Send fun sendMessage(message: Message) @Send fun sendTypingIndicator(message: TypingIndicator) } val scarlet = Scarlet.Builder() .webSocketFactory(okHttpClient.newWebSocketFactory(URL)) .backoffStrategy(LinearBackoffStrategy()) .lifecycle(AndroidLifecycle.ofApplicationForeground(app)) .addMessageAdapterFactory(MoshiMessageAdapter.Factory()) .addStreamAdapterFactory(RxJava2StreamAdapter.Factory()) .build() val chatService = scarlet.create()

Slide 157

Slide 157 text

Changing Server API interface ChatService { @Receive fun observeWebSocketEvent( ): Flowable @Receive fun observeIncomingMessage(): Flowable @Send fun sendMessage(message: Message) @Send fun sendTypingIndicator(message: TypingIndicator) } val scarlet = Scarlet.Builder() .webSocketFactory(okHttpClient.newWebSocketFactory(URL)) .backoffStrategy(LinearBackoffStrategy()) .lifecycle(AndroidLifecycle.ofApplicationForeground(app)) .addMessageAdapterFactory(MoshiMessageAdapter.Factory()) .addStreamAdapterFactory(RxJava2StreamAdapter.Factory()) .build() val chatService = scarlet.create()

Slide 158

Slide 158 text

Switching JSON Library • From Moshi to Jackson • 1 loc

Slide 159

Slide 159 text

Switching JSON Library interface ChatService { @Receive fun observeWebSocketEvent( ): Flowable @Receive fun observeIncomingMessage(): Flowable @Send fun sendMessage(message: Message) } val scarlet = Scarlet.Builder() .webSocketFactory(okHttpClient.newWebSocketFactory(URL)) .backoffStrategy(LinearBackoffStrategy()) .lifecycle(AndroidLifecycle.ofApplicationForeground(app)) .addMessageAdapterFactory(MoshiMessageAdapter.Factory()) .addStreamAdapterFactory(RxJava2StreamAdapter.Factory()) .build()

Slide 160

Slide 160 text

Switching JSON Library interface ChatService { @Receive fun observeWebSocketEvent( ): Flowable @Receive fun observeIncomingMessage(): Flowable @Send fun sendMessage(message: Message) } val scarlet = Scarlet.Builder() .webSocketFactory(okHttpClient.newWebSocketFactory(URL)) .backoffStrategy(LinearBackoffStrategy()) .lifecycle(AndroidLifecycle.ofApplicationForeground(app)) .addMessageAdapterFactory(JacksonMessageAdapter.Factory()) .addStreamAdapterFactory(RxJava2StreamAdapter.Factory()) .build()

Slide 161

Slide 161 text

Write you own Lifecycle • Foreground and LoggedIn Lifecycle • < 10 loc

Slide 162

Slide 162 text

Write you own Lifecycle AndroidLifecycle.ofApplicationForeground(application)

Slide 163

Slide 163 text

Write you own Lifecycle AndroidLifecycle.ofApplicationForeground(application) loggedInLifecycle

Slide 164

Slide 164 text

Write you own Lifecycle AndroidLifecycle.ofApplicationForeground(application) .combineWith(loggedInLifecycle)

Slide 165

Slide 165 text

Write you own Lifecycle fun createLoggedInAndAppForegroundLifecycle(): Lifecycle { return AndroidLifecycle.ofApplicationForeground(application) .combineWith(loggedInLifecycle) }

Slide 166

Slide 166 text

Write you own Lifecycle interface ChatService { @Receive fun observeWebSocketEvent( ): Flowable @Receive fun observeIncomingMessage(): Flowable @Send fun sendMessage(message: Message) } val scarlet = Scarlet.Builder() .webSocketFactory(okHttpClient.newWebSocketFactory(URL)) .backoffStrategy(LinearBackoffStrategy()) .lifecycle(createLoggedInAndAppForegroundLifecycle()) .addMessageAdapterFactory(MoshiMessageAdapter.Factory()) .addStreamAdapterFactory(RxJava2StreamAdapter.Factory()) .build()

Slide 167

Slide 167 text

Predictable • Predictable code is code that can be understood just by reading it

Slide 168

Slide 168 text

Predictable • Spaghetti Code • Testable and debuggable

Slide 169

Slide 169 text

What makes Scarlet predictable? • Immutable State, Event and Transition • State Machine DSL

Slide 170

Slide 170 text

What makes Scarlet predictable? • Immutable State, Event and Transition • State Machine DSL

Slide 171

Slide 171 text

State Machine DSL

Slide 172

Slide 172 text

State Machine DSL sealed class State { object Solid : State() object Liquid : State() object Gas : State() } sealed class Event { object OnMelted : Event() object OnFroze : Event() object OnVaporized : Event() object OnCondensed : Event() }

Slide 173

Slide 173 text

State Machine DSL sealed class State { object Solid : State() object Liquid : State() object Gas : State() } sealed class Event { object OnMelted : Event() object OnFroze : Event() object OnVaporized : Event() object OnCondensed : Event() }

Slide 174

Slide 174 text

State Machine DSL sealed class State { object Solid : State() object Liquid : State() object Gas : State() } sealed class Event { object OnMelted : Event() object OnFroze : Event() object OnVaporized : Event() object OnCondensed : Event() }

Slide 175

Slide 175 text

State Machine DSL StateMachine.create { initialState(Solid) state { on { transitionTo(Liquid) } } state { on { transitionTo(Solid) } on { transitionTo(Gas) } } state { on { transitionTo(Liquid) } } }

Slide 176

Slide 176 text

State Machine DSL StateMachine.create { initialState(Solid) state { on { transitionTo(Liquid) } } state { on { transitionTo(Solid) } on { transitionTo(Gas) } } state { on { transitionTo(Liquid) } } }

Slide 177

Slide 177 text

State Machine DSL StateMachine.create { initialState(Solid) state { on { transitionTo(Liquid) } } state { on { transitionTo(Solid) } on { transitionTo(Gas) } } state { on { transitionTo(Liquid) } } }

Slide 178

Slide 178 text

State Machine DSL StateMachine.create { initialState(Solid) state { on { transitionTo(Liquid) } } state { on { transitionTo(Solid) } on { transitionTo(Gas) } } state { on { transitionTo(Liquid) } } }

Slide 179

Slide 179 text

State Machine DSL StateMachine.create { initialState(Solid) state { on { transitionTo(Liquid) } } state { on { transitionTo(Solid) } on { transitionTo(Gas) } } state { on { transitionTo(Liquid) } } }

Slide 180

Slide 180 text

State Machine DSL StateMachine.create { initialState(Solid) state { on { transitionTo(Liquid) } } state { on { transitionTo(Solid) } on { transitionTo(Gas) } } state { on { transitionTo(Liquid) } } }

Slide 181

Slide 181 text

State Machine DSL StateMachine.create { initialState(Solid) state { on { transitionTo(Liquid) } } state { on { transitionTo(Solid) } on { transitionTo(Gas) } } state { on { transitionTo(Liquid) } } }

Slide 182

Slide 182 text

State Machine DSL StateMachine.create { initialState(Solid) state { on { transitionTo(Liquid) } } state { on { transitionTo(Solid) } on { transitionTo(Gas) } } state { on { transitionTo(Liquid) } } }

Slide 183

Slide 183 text

Testing State Machine val stateMachine = StateMachine.create { // ... }

Slide 184

Slide 184 text

Testing State Machine val stateMachine = StateMachine.create { // ... } assertThat(stateMachine.state).isEqualTo(Solid) // When stateMachine.transition(OnMelted) // Then assertThat(stateMachine.state).isEqualTo(Liquid)

Slide 185

Slide 185 text

Testing State Machine val stateMachine = StateMachine.create { // ... } assertThat(stateMachine.state).isEqualTo(Solid) // When stateMachine.transition(OnMelted) // Then assertThat(stateMachine.state).isEqualTo(Liquid)

Slide 186

Slide 186 text

State Machine in Scarlet

Slide 187

Slide 187 text

State Machine in Scarlet

Slide 188

Slide 188 text

State Machine in Scarlet

Slide 189

Slide 189 text

State Machine in Scarlet

Slide 190

Slide 190 text

State Machine in Scarlet

Slide 191

Slide 191 text

State Machine in Scarlet

Slide 192

Slide 192 text

Scarlet • Declarative • Modular • Predictable

Slide 193

Slide 193 text

Scarlet v2

Slide 194

Slide 194 text

Scarlet v2 • More protocols

Slide 195

Slide 195 text

Scarlet v2 Protocol Bidirectional Topics Client-Server WebSocket SSE Socket IO STOMP MQTT

Slide 196

Slide 196 text

Scarlet v2 Protocol Bidirectional Topics Client-Server WebSocket ✔ SSE ✘ Socket IO ✔ STOMP ✔ MQTT ✔

Slide 197

Slide 197 text

Scarlet v2 Protocol Bidirectional Topics Client-Server WebSocket ✔ ✘ SSE ✘ ✘ Socket IO ✔ ✔ STOMP ✔ ✔ MQTT ✔ ✔

Slide 198

Slide 198 text

Scarlet v2 Protocol Bidirectional Topics Client-Server WebSocket ✔ ✘ SSE ✘ ✘ Socket IO ✔ ✔ STOMP ✔ ✔ MQTT ✔ ✔

Slide 199

Slide 199 text

Scarlet v2 Protocol Bidirectional Topics Client-Server WebSocket ✔ ✘ ✔ SSE ✘ ✘ ✔ Socket IO ✔ ✔ ✔ STOMP ✔ ✔ ✘ MQTT ✔ ✔ ✘

Slide 200

Slide 200 text

Scarlet v2

Slide 201

Slide 201 text

Scarlet observeIncomingMessage() sendMessage observeWebSocketEvent() MessageAdapter Moshi Gson Protobuf StreamAdapter RxJava Kotlin Coroutine WebSocketFactory OkHttp BackoffStrategy Lifecycle Android MockWebServer Linear Exponential ExponentialWithJitter

Slide 202

Slide 202 text

Scarlet observeIncomingMessage() sendMessage observeWebSocketEvent() MessageAdapter Moshi StreamAdapter RxJava WebSocketFactory OkHttp BackoffStrategy Lifecycle Android MockWebServer Linear

Slide 203

Slide 203 text

Scarlet v2 observeIncomingMessage() sendMessage observeWebSocketEvent() MessageAdapter Moshi StreamAdapter RxJava ProtocolClient WebSocket-OkHttp BackoffStrategy Lifecycle Android WebSocket-MockServer Linear SocketIo-Client SocketIo-MockServer SSE-OkHttp Stomp-Gozirra MQTT-Paho

Slide 204

Slide 204 text

Scarlet v2 • https://github.com/Tinder/Scarlet/tree/0.2.x

Slide 205

Slide 205 text

Scarlet 0.1.6 Scarlet 0.2.1-alpha4

Slide 206

Slide 206 text

Scarlet v2 Demo App

Slide 207

Slide 207 text

Scarlet observeIncomingMessage() sendMessage observeWebSocketEvent() MessageAdapter Moshi StreamAdapter RxJava ProtocolClient WebSocket-OkHttp BackoffStrategy Lifecycle Android WebSocket-MockServer Linear SocketIo-Client SocketIo-MockServer SSE-OkHttp Stomp-Gozirra MQTT-Paho

Slide 208

Slide 208 text

Scarlet • Declarative

Slide 209

Slide 209 text

Scarlet • Declarative • Modular

Slide 210

Slide 210 text

Scarlet • Declarative • Modular • Predictable

Slide 211

Slide 211 text

Scarlet • Declarative • Modular • Predictable • More protocols coming

Slide 212

Slide 212 text

Taming WebSocket with Scarlet github.com/Tinder/Scarlet github.com/Tinder/StateMachine github.com/zhxnlai