Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Adopting Jetpack Compose in Your Existing Proje...
Search
Somkiat Khitwongwattana
November 17, 2024
Technology
0
220
Adopting Jetpack Compose in Your Existing Project - GDG DevFest Bangkok 2024
"Adopting Jetpack Compose in your existing project" at GDG DevFest Bangkok 2024
Somkiat Khitwongwattana
November 17, 2024
Tweet
Share
More Decks by Somkiat Khitwongwattana
See All by Somkiat Khitwongwattana
Snackbar in Compose with Friendly UI Testing
akexorcist
0
180
Gemini in Android Studio - Google I/O Bangkok '25
akexorcist
0
390
Why App Signing Matters for Your Android Apps - Android Bangkok Conference 2024
akexorcist
1
670
Building the automated Android UI testing in Continuous integration at LINE MAN Wongnai
akexorcist
1
870
Building the automated Android UI testing in Continuous integration at LINE MAN Wongnai
akexorcist
1
85
What's new in Android 14 - IO Extended George Town 2023
akexorcist
1
370
All you need to know about new Logcat in Android Studio - DevFest Bangkok 2022
akexorcist
0
99
Backend for mobile app - Droidcon SG 2022
akexorcist
0
110
Backend for mobile app - Android Bangkok Conference 2022
akexorcist
1
190
Other Decks in Technology
See All in Technology
コミュニティが変えるキャリアの地平線:コロナ禍新卒入社のエンジニアがAWSコミュニティで見つけた成長の羅針盤
kentosuzuki
0
130
SRE Enabling戦記 - 急成長する組織にSREを浸透させる戦いの歴史
markie1009
0
170
登壇駆動学習のすすめ — CfPのネタの見つけ方と書くときに意識していること
bicstone
3
130
Amazon Bedrock Knowledge Basesチャンキング解説!
aoinoguchi
0
170
【Oracle Cloud ウェビナー】[Oracle AI Database + AWS] Oracle Database@AWSで広がるクラウドの新たな選択肢とAI時代のデータ戦略
oracle4engineer
PRO
2
190
Bill One急成長の舞台裏 開発組織が直面した失敗と教訓
sansantech
PRO
2
410
Ruby版 JSXのRuxが気になる
sansantech
PRO
0
170
CDK対応したAWS DevOps Agentを試そう_20260201
masakiokuda
1
430
Oracle Cloud Observability and Management Platform - OCI 運用監視サービス概要 -
oracle4engineer
PRO
2
14k
AWS DevOps Agent x ECS on Fargate検証 / AWS DevOps Agent x ECS on Fargate
kinunori
2
220
データの整合性を保ちたいだけなんだ
shoheimitani
8
3.2k
コスト削減から「セキュリティと利便性」を担うプラットフォームへ
sansantech
PRO
3
1.6k
Featured
See All Featured
Everyday Curiosity
cassininazir
0
130
The AI Revolution Will Not Be Monopolized: How open-source beats economies of scale, even for LLMs
inesmontani
PRO
3
3.1k
技術選定の審美眼(2025年版) / Understanding the Spiral of Technologies 2025 edition
twada
PRO
117
110k
Claude Code のすすめ
schroneko
67
210k
Imperfection Machines: The Place of Print at Facebook
scottboms
269
14k
Building an army of robots
kneath
306
46k
AI Search: Where Are We & What Can We Do About It?
aleyda
0
7k
Paper Plane
katiecoart
PRO
0
46k
The Invisible Side of Design
smashingmag
302
51k
Future Trends and Review - Lecture 12 - Web Technologies (1019888BNR)
signer
PRO
0
3.2k
Responsive Adventures: Dirty Tricks From The Dark Corners of Front-End
smashingmag
254
22k
Testing 201, or: Great Expectations
jmmastey
46
8.1k
Transcript
Adopting Jetpack Compose in Your Existing Project Bangkok Somkiat Khitwongwattana
Android GDE
Less code Intuitive Accelerate development Powerful Why Jetpack Compose?
Less code Intuitive Accelerate development Powerful Why Jetpack Compose?
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:orientation="vertical"> <Button
android:id="@+id/button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Button" /> </LinearLayout> Build an UI with XML
class MainActivity : AppCompatActivity() { private val binding: ActivityMainBinding by
lazy { ... } override fun onCreate(savedInstanceState: Bundle?) { ... binding.button.setOnClickListener { // Do something } } } View binding
class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) {
... setContent { ComposeAppTheme { Box( modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center, ) { Button(onClick = { // Do something }) { Text(text = "Button") Build an UI with Jetpack Compose
setContent { ComposeAppTheme { Box( ... ) { Button(onClick =
{ // Do something }) { Text(text = "Button") } } } } Build an UI with Jetpack Compose
Less code Intuitive Accelerate development Powerful Why Jetpack Compose?
<Button android:id="@+id/button3" android:text="Button 3" android:visibility="gone" /> <LinearLayout> <Button android:id="@+id/button1" android:text="Button
1" /> <Button android:id="@+id/button2" android:text="Button 2" /> </LinearLayout> Readability
<Button android:id="@+id/button3" android:text="Button 3" android:visibility="gone" /> <LinearLayout> <Button android:id="@+id/button1" android:text="Button
1" /> <Button android:id="@+id/button2" android:text="Button 2" /> </LinearLayout> Readability
val button1: Button = /* ... */ val button2: Button
= /* ... */ val button3: Button = /* ... */ val isPurchased = false button1.visibility = if (isPurchased) View.GONE else View.VISIBLE button2.visibility = if (isPurchased) View.GONE else View.VISIBLE button3.visibility = if (isPurchased) View.VISIBLE else View.GONE Readability
val button1: Button = /* ... */ val button2: Button
= /* ... */ val button3: Button = /* ... */ val isPurchased = true button1.visibility = if (isPurchased) View.GONE else View.VISIBLE button2.visibility = if (isPurchased) View.GONE else View.VISIBLE button3.visibility = if (isPurchased) View.VISIBLE else View.GONE Readability
val isPurchased: Boolean = ... Box( ... ) { if
(isPurchased) { Button( ... ) { Text("Button 3") } } else { Row( ... ) { Button( ... ) { Text("Button 1") } Spacer( ... ) Button( ... ) { Text("Button 2") } } } } Readability
Less code Intuitive Accelerate development Powerful Why Jetpack Compose?
data class Item( val id: String, val text: String, )
Dynamic list UI
<?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:id="@+id/textViewItem" android:layout_width="match_parent"
android:layout_height="wrap_content" /> </FrameLayout> Dynamic list UI
<?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <androidx.recyclerview.widget.RecyclerView android:id="@+id/recyclerView" android:layout_width="match_parent"
android:layout_height="match_parent" /> </FrameLayout> Dynamic list UI
class ItemViewHolder( private val binding: LayoutListItemBinding ) : ViewHolder(binding.root) {
fun bind(item: Item) { binding.textViewItem.text = item.text } } Dynamic list UI
class ListAdapter(private val items: List<Item>) : RecyclerView.Adapter<ItemViewHolder>() { override fun
onCreateViewHolder(parent: ViewGroup, viewType: Int) = ItemViewHolder( LayoutListItemBinding.inflate(LayoutInflater.from(parent.context), parent, false) ) override fun getItemCount(): Int = items.size override fun onBindViewHolder(holder: ItemViewHolder, position: Int) { items.getOrNull(position)?.let { holder.bind(it.text) } } } Dynamic list UI
val items: List<Item> = /* ... */ val context: Context
= /* ... */ with(binding.recyclerView) { layoutManager = LinearLayoutManager( context, LinearLayoutManager.VERTICAL, false, ) adapter = ListAdapter(items) } Dynamic list UI
@Composable fun ListContent(items: List<Item>) { LazyColumn(modifier = Modifier.fillMaxSize()) { items(
items = items, key = { item -> item.id } ) { item -> Text( modifier = Modifier.fillMaxWidth(), text = item.text, ) } } Dynamic list UI
Less code Intuitive Accelerate development Powerful Why Jetpack Compose?
var activated by remember { mutableStateOf(true) } val contentColor by
animateColorAsState( ... ) val containerColor by animateColorAsState( ... ) val scale by animateFloatAsState( ... ) Button( modifier = Modifier.scale(scale), colors = ButtonDefaults.buttonColors( containerColor = containerColor, contentColor = contentColor, ), onClick = { activated = !activated }, ) { Text(if (activated) "Activated" else "Inactivated") } Animation
Jetpack Compose 1.7.5 is now available
Declarative programming paradigm Declarative paradigm shift Dynamic content Recomposition Thinking
in Compose
Declarative paradigm shift • Relatively stateless and don't expose setter
or getter functions • Update the UI by passing the different arguments • Unidirectional data flow
State flows down
Event flows up
@Composable fun ProfileContent(name: String, status: String, onEditProfile: () -> Unit)
{ Row { Box{ Image( ... ) IconButton(onClick = onEditProfile) { ... } } Column { Text(name) Text(status) } } } State flows down and event flows up
@Composable fun ProfileContent(name: String, status: String, onEditProfile: () -> Unit)
{ Row { Box{ Image( ... ) IconButton(onClick = onEditProfile) { ... } } Column { Text(name) Text(status) } } } State flows down and event flows up
@Composable fun ProfileContent(name: String, status: String, onEditProfile: () -> Unit)
{ Row { Box{ Image( ... ) IconButton(onClick = onEditProfile) { ... } } Column { Text(name) Text(status) } } } State flows down and event flows up
@Composable fun ProfileContent(name: String, status: String, onEditProfile: () -> Unit)
{ Row { Box{ Image( ... ) IconButton(onClick = onEditProfile) { ... } } Column { Text(name) Text(status) } } } State flows down and event flows up
@Composable fun TopThreeItems(items: List<String>) { Column { HeaderContent() items.take(3).forEach {
item -> ItemContent(item) } } } Dynamic content
@Composable fun TopThreeItems(items: List<String>) { Column { HeaderContent() items.take(3).forEach {
item -> ItemContent(item) } } } Recomposition New value update
@Composable fun TopThreeItems(items: List<String>) { Column { HeaderContent() items.take(3).forEach {
item -> ItemContent(item) } } } Recomposition Skip
@Composable fun TopThreeItems(items: List<String>) { Column { HeaderContent() items.take(3).forEach {
item -> ItemContent(item) } } } Recomposition Recomposition
Get started Jetpack Compose for Android Developers https://developer.android.com/courses/jetpack-compose/course
Get started
Get started https://github.com/android/nowinandroid
Get started https://github.com/chrisbanes/tivi
Why should you adopt Jetpack Compose?
Chance for the big improvement Animate with a few lines
of code Powerful preview in Android Studio Testable without view ID Friendly for sub-component creation Benefit for teams
Powerful preview in Android Studio
@Composable fun MainScreen( ... ) { Column( ... ) {
Profile( modifier = Modifier.testTag("profile"), ... ) ... } } Testable without view ID
<node index="0" text="" resource-id="" class="android.view.View" package="com.akexorcist.devfest" content-desc="" checkable="false" checked="false" clickable="false"
enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" bounds="[0,66][79,132]"> <node index="0" text="Android" resource-id="" class="android.widget.TextView" package="com.akexorcist.devfest" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" bounds="[0,66][79,132]" /> </node> Testable without view ID
<node index="0" text="" resource-id="" class="android.view.View" package="com.akexorcist.devfest" content-desc="" checkable="false" checked="false" clickable="false"
enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" bounds="[0,66][79,132]"> <node index="0" text="Android" resource-id="" class="android.widget.TextView" package="com.akexorcist.devfest" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" bounds="[0,66][79,132]" /> </node> Testable without view ID
<node index="0" text="" resource-id="" class="android.view.View" package="com.akexorcist.devfest" content-desc="" checkable="false" checked="false" clickable="false"
enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" bounds="[0,66][79,132]"> <node index="0" text="Android" resource-id="" class="android.widget.TextView" package="com.akexorcist.devfest" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" bounds="[0,66][79,132]" /> </node> Testable without view ID
Friendly for sub-component creation
Fragment Friendly for sub-component creation Fragment Fragment Fragment ViewModel ViewModel
ViewModel ViewModel ViewModel
View Friendly for sub-component creation View View View ViewModel
Compose Friendly for sub-component creation Compose Compose Compose ViewModel ViewModel
ViewModel ViewModel ViewModel
@Composable fun FirstSection(viewModel: FirstViewModel = koinViewModel()) { ... } @Composable
fun SecondSection(viewModel: SecondViewModel = koinViewModel()) { ... } @Composable fun ThirdSection(viewModel: ThirdViewModel = koinViewModel()) { ... } @Composable fun FourthSection(viewModel: FourthViewModel = koinViewModel()) { ... } Friendly for sub-component creation
@Composable fun MainScreen(viewModel: MainViewModel = koinViewModel()) { Column { FirstSection()
SecondSection() ThirdSection() FourthSection() } } Friendly for sub-component creation
@Composable fun MainScreen(viewModel: MainViewModel = koinViewModel()) { LazyColumn { item(key
= "first") { FirstSection() } item(key = "second") { SecondSection() } item(key = "third") { ThirdSection() } item(key = "fourth") { FourthSection() } } } Friendly for sub-component creation
Activity Result Requesting Runtime Permissions ViewModel Data Streams Asynchronous Operations
Availability
Screen Navigation Dependency Injection Google Maps Navigation Drawer Bottom Sheet
Availability
Nested Scrolling Material 3 Components Dialog Snackbar Image Loading Availability
WindowInsets Handling screen size changed Availability UI Testing
Compose preview generation with Gemini
Things to know Debug build performance Profile installer for better
performance No cost from XML layout inflation Efficiently handle deep UI trees Smart recompositions
Sunflower app https://github.com/android/sunflower
APK Size Based on information from the Sunflower app Views
only 2,252 KB 3,034 KB 2,966 KB Mixed Views and Compose Compose-only
Build Time Based on information from the Sunflower app Views
only 299.47 ms 399.09 ms 342.16 ms Mixed Views and Compose Compose-only
Tivi app https://github.com/chrisbanes/tivi
Based on information from the Tivi app v1.0.0-rc01 APK Size
Views only 4.14 MB 2.71 MB 2.32 MB Fragment and Compose Compose-only
Method Count Based on information from the Tivi app v1.0.0-rc01
Views only 40,029 35,165 23,689 Fragment and Compose Compose-only
In the other side Different knowledge Few performance overhead based
on complexity Fewer third-party libraries compared to the View Limited legacy support
None
Approach to integrating Jetpack Compose New Features Simple Component or
Screen Redesign Features
Fragment Layout Layout View Layout View View Compose
Activity Activity Activity Activity Activity View View View Compose
Activity Fragment Fragment Fragment Fragment View View View Compose
Activity Activity Activity Fragment Navigation View Compose Fragment View Compose
Compose
Activity Activity Activity Fragment Navigation View Compose Fragment View Compose
Compose
Activity Fragment Fragment Fragment Navigation View Compose Fragment View Compose
Compose
Activity Fragment Fragment Fragment Navigation View Compose Fragment View Compose
Compose
Activity Fragment Fragment Fragment Navigation View Compose Fragment View Compose
Compose
Codebase Navigation Existing UI Component & Design System Analytics Testing
Things to think about
Compose in Activity Fragment View Compose View in
class MainActivity: ComponentActivity() { override fun onCreate( ... ) {
... setContent { // In Compose world } } } Compose in Activity
class HomeFragment : Fragment() { override fun onCreateView( ... ):
View { return ComposeView(requireContext()).apply { setViewCompositionStrategy( ... ) setContent { // In Compose world } } } } Compose in Fragment
Compose in View <?xml version="1.0" encoding="utf-8"?> <FrameLayout> ... <androidx.compose.ui.platform.ComposeView android:id="@+id/composeView"
android:layout_width="match_parent" android:layout_height="match_parent" /> </FrameLayout>
val composeView = findViewById<ComposeView>(R.id.composeView) composeView.apply { setViewCompositionStrategy( ... ) setContent
{ // In Compose world } } Compose in View
@Composable fun AndroidCustomView(title: String, onClick: () -> Unit) { AndroidView(
factory = { context -> CustomView(context).apply { setOnClickListener(onClick) } }, update = { view -> view.setTitle(title) } ) View in Compose
Codebase preparation for seamless feature development Start with small, incremental
changes Learning resources for future adopters Negotiate with the product's decision-maker To make it happen
API Maturity Learning resources Libraries & tools Backward compatibility Multiplatform
supported Jetpack Compose is now production-ready Performance Wide Adoption
"Any code you built with views will become technical debt"
Florina Muntenescu
Thank you Bangkok