Skip to content

Commit

Permalink
support multiple ConvertAdapter (gson, kotlinx.serialization)
Browse files Browse the repository at this point in the history
  • Loading branch information
jaeyunn15 committed Oct 9, 2023
1 parent 5be34cf commit 5dcee88
Show file tree
Hide file tree
Showing 14 changed files with 116 additions and 24 deletions.
2 changes: 1 addition & 1 deletion .idea/deploymentTargetDropDown.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ plugins {
id 'kotlin-kapt'
id 'com.google.dagger.hilt.android'
id 'maven-publish'
id 'kotlinx-serialization'
}

android {
Expand Down Expand Up @@ -90,4 +91,6 @@ dependencies {
kapt "com.google.dagger:hilt-compiler:2.44.2"

implementation 'androidx.lifecycle:lifecycle-runtime-compose:2.6.0-alpha01'

implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.1'
}
3 changes: 2 additions & 1 deletion app/src/main/java/com/jeremy/thunder/di/SocketModule.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.jeremy.thunder.di

import android.content.Context
import com.jeremy.thunder.Thunder
import com.jeremy.thunder.event.converter.ConverterType
import com.jeremy.thunder.makeWebSocketCore
import com.jeremy.thunder.socket.SocketService
import com.jeremy.thunder.thunder
Expand Down Expand Up @@ -37,6 +37,7 @@ internal object SocketModule {
return thunder {
webSocketCore(okHttpClient.makeWebSocketCore("wss://fstream.binance.com/stream"))
setApplicationContext(context)
setConverterType(ConverterType.Serialization)
}.create()
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
package com.jeremy.thunder.socket.model

import com.google.gson.annotations.SerializedName
import kotlinx.serialization.Serializable

@Serializable
data class AllMarketTickerResponse(
@SerializedName("stream")
//@SerializedName("stream")
val stream: String = "",

@SerializedName("data")
val data: List<AllMarketTickerResponseItem> = emptyList()
//@SerializedName("data")
val data: List<AllMarketTickerResponseItem> = emptyList(),

val nickName: String = "default"
)

/*
Expand Down Expand Up @@ -38,25 +42,28 @@ data class AllMarketTickerResponse(
*
* */

@Serializable
data class AllMarketTickerResponseItem(
@SerializedName("E")
//@SerializedName("E")
val E: Long, //event time

@SerializedName("P")
val e: String,

//@SerializedName("P")
val P: String, // price change percent

@SerializedName("c")
//@SerializedName("c")
val c: String, // last Price

@SerializedName("h")
//@SerializedName("h")
val h: String, // high price

@SerializedName("l")
//@SerializedName("l")
val l: String, // low price

@SerializedName("o")
//@SerializedName("o")
val o: String, // open price

@SerializedName("s")
// @SerializedName("s")
val s: String, // symbol
)
2 changes: 2 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ plugins {
id 'org.jetbrains.kotlin.android' version '1.8.10' apply false
id 'com.android.library' version '7.4.2' apply false
id 'com.google.dagger.hilt.android' version '2.44.2' apply false
id 'org.jetbrains.kotlin.jvm' version '1.8.10' apply false
id 'org.jetbrains.kotlin.plugin.serialization' version '1.8.10' apply false
}

task clean(type: Delete) {
Expand Down
2 changes: 2 additions & 0 deletions thunder/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,6 @@ dependencies {
implementation 'com.squareup.okhttp3:logging-interceptor:4.9.2'

implementation 'com.google.code.gson:gson:2.9.0'

implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.1'
}
7 changes: 7 additions & 0 deletions thunder/src/main/java/com/jeremy/thunder/Thunder.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import com.jeremy.thunder.coroutine.CoroutineScope.scope
import com.jeremy.thunder.event.EventProcessor
import com.jeremy.thunder.event.WebSocketEvent
import com.jeremy.thunder.event.WebSocketEventProcessor
import com.jeremy.thunder.event.converter.ConverterType
import com.jeremy.thunder.internal.ServiceExecutor
import com.jeremy.thunder.internal.ThunderProvider
import com.jeremy.thunder.internal.ThunderStateManager
Expand Down Expand Up @@ -57,6 +58,7 @@ class Thunder private constructor(
private var thunderStateManager: ThunderStateManager? = null
private var context: Context? = null
private val appConnectionProvider by lazy { AppConnectionProvider() }
private var converterType: ConverterType = ConverterType.Serialization

fun webSocketCore(core: WebSocket.Factory): Builder = apply { this.webSocketCore = core }

Expand All @@ -65,6 +67,10 @@ class Thunder private constructor(
(this.context as Application).registerActivityLifecycleCallbacks(appConnectionProvider)
}

fun setConverterType(type: ConverterType) = apply {
converterType = type
}

private fun createThunderStateManager(): ThunderStateManager {
thunderStateManager = ThunderStateManager.Factory(
connectionListener = appConnectionProvider,
Expand Down Expand Up @@ -101,6 +107,7 @@ class Thunder private constructor(
private fun createServiceExecutor(): ServiceExecutor {
return ServiceExecutor.Factory(
thunderProvider = createThunderProvider(),
converterType = converterType,
scope = scope
).create()
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.jeremy.thunder.event

import com.jeremy.thunder.event.converter.Converter
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.jeremy.thunder.event

import com.jeremy.thunder.event.converter.Converter
import java.lang.reflect.Type

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.jeremy.thunder.event.converter

interface Converter<T> {
fun convert(data: String): T
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.jeremy.thunder.event.converter

sealed class ConverterType {
object Gson: ConverterType()

object Serialization: ConverterType()
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
package com.jeremy.thunder.event
package com.jeremy.thunder.event.converter

import com.google.gson.Gson
import com.google.gson.TypeAdapter
import com.google.gson.reflect.TypeToken
import java.io.StringReader
import java.lang.reflect.Type

interface Converter<T> {
fun convert(data: String): T
}

class ConvertAdapter<T> private constructor(
class GsonConvertAdapter<T> private constructor(
private val gson: Gson,
private val typeAdapter: TypeAdapter<T>,
private val type: Type
Expand All @@ -22,9 +18,9 @@ class ConvertAdapter<T> private constructor(
}

class Factory {
fun create(type: Type): ConvertAdapter<*> {
fun create(type: Type): GsonConvertAdapter<*> {
val typeAdapter = Gson().getAdapter(TypeToken.get(type))
return ConvertAdapter(Gson(), typeAdapter, type)
return GsonConvertAdapter(Gson(), typeAdapter, type)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.jeremy.thunder.event.converter

import com.jeremy.thunder.thunderLog
import kotlinx.serialization.DeserializationStrategy
import kotlinx.serialization.KSerializer
import kotlinx.serialization.SerialFormat
import kotlinx.serialization.StringFormat
import kotlinx.serialization.json.Json
import kotlinx.serialization.serializer
import okhttp3.internal.ignoreIoExceptions
import java.lang.reflect.Type

class SerializeConvertAdapter <T> private constructor(
type: Type,
private val kSerializer: KSerializer<T>
) : Converter<T> {
private fun SerialFormat.serializeAsType(type: Type): KSerializer<Any> = serializersModule.serializer(type)
class Factory {
fun create(type: Type): SerializeConvertAdapter<*> {
return SerializeConvertAdapter(type, serializer(type))
}
}

private val json = Json {
isLenient = true
useAlternativeNames = true // can use alternative key
ignoreUnknownKeys = true // prevent ignore no matching key exception
coerceInputValues = true // Set as non-null type but receive as null type. you can use default value as set true
encodeDefaults = true // You can use default value when received data does not have that value.
}

private val stringFormat: StringFormat = json.apply {
ignoreIoExceptions {
thunderLog("[IoException] Cause convert specific type is not supported.")
}
}

private val loader: DeserializationStrategy<T> = stringFormat.serializeAsType(type) as DeserializationStrategy<T>

override fun convert(data: String): T {
return kotlin.run {
stringFormat.decodeFromString(loader, data)
}
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package com.jeremy.thunder.internal

import com.google.gson.Gson
import com.jeremy.thunder.event.ConvertAdapter
import com.jeremy.thunder.event.SocketEventKeyStore
import com.jeremy.thunder.event.WebSocketEvent
import com.jeremy.thunder.event.converter.Converter
import com.jeremy.thunder.event.converter.ConverterType
import com.jeremy.thunder.event.converter.GsonConvertAdapter
import com.jeremy.thunder.event.converter.SerializeConvertAdapter
import com.jeremy.thunder.getAboutRawType
import com.jeremy.thunder.getParameterUpperBound
import kotlinx.coroutines.CoroutineScope
Expand All @@ -14,6 +17,7 @@ import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import java.lang.reflect.Method
import java.lang.reflect.ParameterizedType
import java.lang.reflect.Type

/**
* create logic by annotation
Expand All @@ -23,6 +27,7 @@ import java.lang.reflect.ParameterizedType

class ServiceExecutor internal constructor(
private val thunderProvider: ThunderProvider,
private val converterType: ConverterType,
private val scope: CoroutineScope
) {

Expand All @@ -49,7 +54,9 @@ class ServiceExecutor internal constructor(
method.requireParameterTypes { "Receive method must have zero parameter: $method" }
method.requireReturnTypeIsOneOf(ParameterizedType::class.java) { "Receive method must return ParameterizedType: $method" }
val returnType = (method.genericReturnType as ParameterizedType).getParameterUpperBound(0)
val converter = ConvertAdapter.Factory().create(returnType)
//val converter = ConvertAdapter.Factory().create(returnType)
//val converter = SerializeConvertAdapter.Factory().create(returnType)
val converter = checkConverterType(converterType, returnType)
val eventMapper = SocketEventKeyStore().findEventMapper(returnType, method.annotations, converter)
thunderProvider.observeEvent()
.map(eventMapper::mapEvent)
Expand All @@ -62,6 +69,13 @@ class ServiceExecutor internal constructor(
}
}

private fun checkConverterType(converterType: ConverterType, returnType: Type): Converter<out Any?> {
return when(converterType) {
ConverterType.Gson -> GsonConvertAdapter.Factory().create(returnType)
ConverterType.Serialization -> SerializeConvertAdapter.Factory().create(returnType)
}
}

private fun Flow<Any>.createPipeline(): ReceivePipeline<*> = ReceivePipeline(this, scope)

fun executeSend(method: Method, args: Array<out Any>) {
Expand All @@ -74,11 +88,12 @@ class ServiceExecutor internal constructor(

class Factory(
private val thunderProvider: ThunderProvider,
private val scope: CoroutineScope
private val converterType: ConverterType,
private val scope: CoroutineScope,
) {

fun create(): ServiceExecutor {
return ServiceExecutor(thunderProvider, scope)
return ServiceExecutor(thunderProvider, converterType, scope)
}
}

Expand Down

0 comments on commit 5dcee88

Please sign in to comment.