일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
- withContext
- 백준
- ServerDrivenUI
- Next Challenge
- 안드로이드
- Advanced LCA
- coroutinescope
- cancellationException
- Flow
- Kotlin
- Product Flavor
- Algorithm
- 백준2309
- 릴리즈 키해시
- hotStream
- flowon
- app-distribution
- ShapeableImageView
- java
- coldStream
- google play console
- collectLatest
- monotone stack
- coroutinecontext
- Android
- conflate
- coroutine
- TOSS 과제
- KAKAO
- SDUI
- Today
- Total
루피도 코딩한다
(Part 1) Server-Driven UI: Android with Dynamic Views 본문
📌 Backgrounds
Hi! I'm a android developer at'LGTM', and our team wanted to implement A/B testing on the Home screen. Unlike web services, we can't force Android app users to update simultaneously. So, we decided to leverage the 'Server-Driven UI' (SDUI) technique.
Today, I'd like to share how I applied the 'SDUI' approach to our project. Let's dive in!
1️⃣ Defined ViewTypes and Properties
To dynamically rearrange UI components, we had to define the necessary view types. This allowed us to match the prepared UI components on the client side with the API response. Here's the list we used:
- SectionTitle
- SectionItem
- SectionCloser
- SectionEmpty
- Unknown (In preparation for potential errors)
You might find it easier to understand when you refer to this image:

2️⃣ Specify the Required ViewType in the Enum Class
- Remember to make necessary edits if new view types are added later.
- Before introducing the ViewType enum class, developers need to define corresponding data classes required for each view type as like
SectionTitleVO
,SectionEmptyVO
etc.
enum class SduiViewType(
val viewType: String, private val viewTypeClassType: Type
) {
TITLE("sectionTitle", SectionTitleVO::class.java),
EMPTY("empty", SectionEmptyVO::class.java),
CLOSER("sectionCloser", SectionCloserVO::class.java),
ITEM("sectionItem", SectionItemVO::class.java),
UNKNOWN("sectionUnknown", SectionUnknownVO::class.java);
companion object {
fun getViewTypeByOrdinal(ordinalNum: Int): SduiViewType {
return values()[ordinalNum]
}
fun SduiViewType.getViewTypeClassType(): Type {
return this.viewTypeClassType
}
fun findClassByItsName(viewTypeString: String?): SduiViewType {
return values().find { it.viewType == viewTypeString } ?: UNKNOWN
}
}
}
3️⃣ Preparing the Response Class to Receive SDUI api
data class SduiVO(
val sceneName: String, val contents: List<SduiItemVO>
)
data class SduiItemVO(
val viewType: SduiViewType,
val theme: SduiTheme,
val content: SduiContent
)
interface SduiContent
- Note that the
sceneName
isn't crucial on the Android side. Our team used it to double-check that server's response matches each screen (activity or fragment). - Within the
Content
structure, eachSduiItemVO
includes the view type, theme, and content. - It's worth mentioning that all these classes are located in the
domain module
, as this information is needed not only in thePresentation Layer
but also in theData Layer
.
4️⃣ Delving Deeper into the SduiContent
Interface
- Can you guess why I chose
SduiContent
as an interface type? - Usually, we create data classes to facilitate JSON deserialization from the backend.
- However, in the case of SDUI (Server Driven UI), we remain uncertain about the necessary data class until the JSON reaches the client level. Think of it as a runtime decision.
- There could be required data classes for each already decided view type, such as
SectionTitle
,SectionItem
, etc. Once these classes likeSectionTitle
andSectionItem
are created, they inherit fromSduiContent
. - This approach enables us to deserialize runtime-determined classes in real-time! 🎉🎉
- This interface serves as a beacon in the data module. Moving forward, let's explore the classes within the data module.
5️⃣ Set Custom Deserializer on Network Connection
- Can you guess what we have to handle when applying SDUI?
- It's a custom deserializer, especially if you're using the 'GSON' library.
I'm using the GSON library, well-known among Android developers.
Gson doesn't support deserializing all classes.
As we used an interface in our response data class, deserialization might not work correctly.
So, we're going to handle GsonConverterFactory to let it know how to parse the interface into the proper data class.
let's delve deeper into this by exploring some real code below.
SduiViewTypeDeserializer
- My class is structured like the code below.
- First, we extract the
viewTypeName
andtheme
string from the JSON. - Next, we find the detailed data class that matches the
viewTypeName
just before this step.- At this stage, we utilize a function named
findClassByItsName()
defined in theSduiViewType
enum class. - As my project uses a multi-module and clean architecture, I can easily use the
SduiViewType
enum class located in the domain module on the app module, which containsSduiViewTypeDeserializer
.
- At this stage, we utilize a function named
- Finally, reconstruct
SduiItemVO
with using viewType, theme, and detailed data class inheriting theSduiContent
interface.
class SduiViewTypeDeserializer : JsonDeserializer<SduiItemVO> {
@Throws(JsonParseException::class)
override fun deserialize(
json: JsonElement?, typeOfT: Type, context: JsonDeserializationContext
): SduiItemVO {
val jsonObject = json?.asJsonObject ?: throw IllegalArgumentException("Json Parsing 실패")
val viewTypeString: String = jsonObject.get("viewTypeName").asString
val viewType: SduiViewType = SduiViewType.findClassByItsName(viewTypeString)
val themeString: String = jsonObject.get("theme").asString
val theme: SduiTheme = SduiTheme.findClassByItsName(themeString)
val decidedViewType = viewType.getViewTypeClassType()
val content = jsonObject.get("content").asJsonObject
val sduiContent: SduiContent = Gson().fromJson(content, decidedViewType)
return SduiItemVO(viewType, theme, sduiContent)
}
}
Great! If you've followed those steps carefully, you should now be able to receive a proper API response on the client side.
Now, it's time to dive into the UI part in our next post. See you there!
'Android' 카테고리의 다른 글
Android Booting Sequence (0) | 2024.12.20 |
---|---|
Github Action을 통해 Firebase App Distribution에 자동으로 Android 앱 배포하는 방법 (+ Slack에 노티까지!?) (2) | 2023.08.06 |
[Android] Product Flavor 사용환경에서 Crashlytics 적용하기 (feat. slack 알림 연동) (2) | 2023.06.01 |
[Android] ShapableImageView radius설정하기 (0) | 2022.11.27 |
앱을 출시를 했는데요, 출시를 못했습니다 (2편/ Kakao Login) (4) | 2022.10.06 |