Slide 1

Slide 1 text

Sam Edwards - @HandstandSam - Square Interception Droidcon NYC September 14, 2023 #DCNYC23

Slide 2

Slide 2 text

A Call B Completion Invocation

Slide 3

Slide 3 text

A Call Chain B

Slide 4

Slide 4 text

A Call Chain B

Slide 5

Slide 5 text

Call Chain A B

Slide 6

Slide 6 text

Call Chain with Interceptors A B

Slide 7

Slide 7 text

Telephone Game ☎ A B

Slide 8

Slide 8 text

Interceptor Pattern 💻

Slide 9

Slide 9 text

https://en.wikipedia.org/wiki/Interceptor_pattern Offer a way to change, or augment the usual processing cycle. Interceptor Pattern

Slide 10

Slide 10 text

https://en.wikipedia.org/wiki/Interceptor_pattern The rest of the system does not have to know something has been added or changed and can keep working as before. Interceptor Pattern

Slide 11

Slide 11 text

Call Chain with Interceptors A B

Slide 12

Slide 12 text

Categories of Interception • Passthrough (NoOp) • Logging (Inspect) • Modifying/Transform (Change) • Circuit Breaker (Stop)

Slide 13

Slide 13 text

OkHttp Interceptors

Slide 14

Slide 14 text

OkHttp Interceptor Examples

Slide 15

Slide 15 text

Adding Interceptors for OkHttp val okHttpClient = OkHttpClient.Builder() .build()

Slide 16

Slide 16 text

Adding Interceptors for OkHttp val okHttpClient = OkHttpClient.Builder() .addInterceptor(MyInterceptor) .build()

Slide 17

Slide 17 text

Adding Interceptors for OkHttp val okHttpClient = OkHttpClient.Builder() .addInterceptor(MyInterceptor) .addInterceptor(MyInterceptor2 ) .build()

Slide 18

Slide 18 text

Adding Interceptors for OkHttp val okHttpClient = OkHttpClient.Builder() .addInterceptor(MyInterceptor) .addInterceptor(MyInterceptor2 ) .addInterceptor(MyInterceptor3 ) .build()

Slide 19

Slide 19 text

Passthrough Interceptor 🤷

Slide 20

Slide 20 text

Passthrough Interceptor Interceptor { chain -> val request = chain.request() val response = chain.proceed(request) response }

Slide 21

Slide 21 text

Logging Interceptor 📝

Slide 22

Slide 22 text

Logging Interceptor Interceptor { chain -> val request = chain.request() val response = chain.proceed(request) println(" - - - Request Headers ---") println(request.headers) println(" - - - Response Headers ---") println(response.headers) response }

Slide 23

Slide 23 text

Modifying Interceptor 🥷

Slide 24

Slide 24 text

Modifying Interceptor Interceptor { chain -> val newRequest = chain.request() .newBuilder() .addHeader("Droidcon", "NYC") .build() chain.proceed(newRequest) }

Slide 25

Slide 25 text

Short Circuit Interceptor ⚡

Slide 26

Slide 26 text

Short Circuit Interceptor val ShortCircuitInterceptor = Interceptor { chain -> val request = chain.request() Response.Builder() .code(200) .protocol(Protocol.HTTP_2 ) .request(request) .message("") .body("Short Circuited".toResponseBody()) .build() }

Slide 27

Slide 27 text

Caching Interceptor 🧠

Slide 28

Slide 28 text

Caching Interceptor private val cache = mutableMapOf ( ) Interceptor { chain -> val request = chain.request() val bodyStr = cache[request.url] ? : run { val response = chain.proceed(request) response.body?.string().also { cache[request.url] = it } } buildResponseWithBodyStr(request, bodyStr) }

Slide 29

Slide 29 text

OkHttp Interceptors are Beautiful 🌈 • Composable • Flexible • Powerful

Slide 30

Slide 30 text

Pop Quiz! 🏈

Slide 31

Slide 31 text

American Football Interception 🏈

Slide 32

Slide 32 text

When the ball is caught by the defense, instead of the offense. 🏈

Slide 33

Slide 33 text

A Interception American Football B

Slide 34

Slide 34 text

A Interception American Football B 🏈

Slide 35

Slide 35 text

Interception American Football 🏈 B A

Slide 36

Slide 36 text

Recap

Slide 37

Slide 37 text

Interceptor Pattern Benefits • Clean Abstraction • Flexible • Testable • Adaptable • Composable • Injectable

Slide 38

Slide 38 text

Kotlin Flows

Slide 39

Slide 39 text

Chaining Flows and Intercepting flowOf(1, 2, 3 ) .collect()

Slide 40

Slide 40 text

Chaining Flows and Intercepting flowOf(1, 2, 3 ) .onEach { println("Logged $it") } .collect() Logged 1 Logged 2 Logged 3

Slide 41

Slide 41 text

Chaining Flows and Intercepting flowOf(1, 2, 3 ) .onEach { println("Logged $it") } .map { it + 100 } .collect() Logged 1 Logged 2 Logged 3

Slide 42

Slide 42 text

Chaining Flows and Intercepting flowOf(1, 2, 3 ) .onEach { println("Logged $it") } .map { it + 100 } .onEach { println("Collected $it") } .collect() Logged 1 Collected 101 Logged 2 Collected 102 Logged 3 Collected 103

Slide 43

Slide 43 text

Kotlin Flows are Beautiful 🌈 • Composable • Flexible • Powerful

Slide 44

Slide 44 text

Iterating Kotlin Collections

Slide 45

Slide 45 text

Chaining Actions and Intercepting listOf(1, 2, 3 ) .onEach { println("Logged $it") } Logged 1 Logged 2 Logged 3

Slide 46

Slide 46 text

Chaining Actions and Intercepting listOf(1, 2, 3 ) .onEach { println("Logged $it") } .map { it + 100 } Logged 1 Logged 2 Logged 3

Slide 47

Slide 47 text

Chaining Actions and Intercepting listOf(1, 2, 3 ) .onEach { println("Logged $it") } .map { it + 100 } .onEach { println("Collected $it") } Logged 1 Logged 2 Logged 3 Collected 101 Collected 102 Collected 103

Slide 48

Slide 48 text

Interfaces Enable Injection

Slide 49

Slide 49 text

Interfaces Enable Injection interface Logger { fun log(message: String) }

Slide 50

Slide 50 text

Interfaces Enable Injection interface Logger { fun log(message: String) } class SystemOutLogger : Logger { override fun log(message: String) { println(message) } }

Slide 51

Slide 51 text

Interfaces Enable Injection interface Logger { fun log(message: String) } class SystemOutLogger : Logger { override fun log(message: String) { println(message) } } class AndroidLogger : Logger { override fun log(message: String) { Log.d("Logger", message) } }

Slide 52

Slide 52 text

Interfaces Enable Interception

Slide 53

Slide 53 text

A Interfaces Enable Interception B SystemOutLogger Logger

Slide 54

Slide 54 text

A Interfaces Enable Interception B Logger SystemOutLogger Logger

Slide 55

Slide 55 text

A Interfaces Enable Interception B Logger SystemOutLogger Logger Logger

Slide 56

Slide 56 text

Interfaces Enable Interception A B Logger SystemOutLogger Logger Logger Logger

Slide 57

Slide 57 text

Interfaces Enable Fakes

Slide 58

Slide 58 text

A Interfaces Enable Fakes B Interface 3rd Party Lib Fake

Slide 59

Slide 59 text

Kotlin Delegates

Slide 60

Slide 60 text

Kotlin Delegates Passthrough class PassthroughDelegateLogger( private val delegate: Logger ) : Logger { override fun log(message: String) { delegate.log(message) } override fun error(message: String) { delegate.error(message) } }

Slide 61

Slide 61 text

Kotlin Delegates Logging class LoggingDelegateLogger( private val delegate: Logger ) : Logger by delegate { override fun log(message: String) { println("Logged $message") delegate.log(message) } }

Slide 62

Slide 62 text

Kotlin Delegates Modifying class AddTimestampLogger( private val delegate: Logger ) : Logger by delegate { override fun log(message: String) { delegate.log("${Date()} $message") } }

Slide 63

Slide 63 text

Kotlin Delegates Short Circuit class ShortCircuitNoErrorsDelegateLogger( private val delegate: Logger ) : Logger by delegate { override fun log(message: String) { delegate.log("${Date()} $message") } override fun error(message: String) { // Do Not Delegate } }

Slide 64

Slide 64 text

Interception Recap

Slide 65

Slide 65 text

Categories of Interception • Passthrough (NoOp) • Logging (Inspect) • Modifying/Transform (Change) • Circuit Breaker (Stop)

Slide 66

Slide 66 text

Why? • Clean Abstraction • Flexible • Testable • Adaptable • Composable • Injectable

Slide 67

Slide 67 text

Takeaways • Use Interfaces • OkHttp “Interception” • Kotlin Flow “Interception” • Kotlin Delegate “Interception” • Design for “Interception”

Slide 68

Slide 68 text

BONUS LIVE DEMO Java Reflection Dynamic Proxies

Slide 69

Slide 69 text

Sam Edwards - @HandstandSam - Square #DCNYC23 conferenceChain.proceed(talk)