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
55
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
Why App Signing Matters for Your Android Apps - Android Bangkok Conference 2024
akexorcist
0
87
Building the automated Android UI testing in Continuous integration at LINE MAN Wongnai
akexorcist
1
620
Building the automated Android UI testing in Continuous integration at LINE MAN Wongnai
akexorcist
1
63
What's new in Android 14 - IO Extended George Town 2023
akexorcist
1
300
All you need to know about new Logcat in Android Studio - DevFest Bangkok 2022
akexorcist
0
75
Backend for mobile app - Droidcon SG 2022
akexorcist
0
66
Backend for mobile app - Android Bangkok Conference 2022
akexorcist
1
140
Other Decks in Technology
See All in Technology
IBC 2024 動画技術関連レポート / IBC 2024 Report
cyberagentdevelopers
PRO
0
110
AWS Lambda のトラブルシュートをしていて思うこと
kazzpapa3
2
160
テストコード品質を高めるためにMutation Testingライブラリ・Strykerを実戦導入してみた話
ysknsid25
6
2.5k
Going down the RAT hole: Deep dive into the Vuln-derland of APT-class RAT Tools
nttcom
0
520
3次元点群データ「VIRTUAL SHIZUOKA』のオープンデータ化による恩恵と協働の未来/FOSS4G Japan 2024
kazz24s
0
150
メールサーバ管理者のみ知る話
hinono
1
120
Python(PYNQ)がテーマのAMD主催のFPGAコンテストに参加してきた
iotengineer22
0
450
VideoMamba: State Space Model for Efficient Video Understanding
chou500
0
190
OCI Vault 概要
oracle4engineer
PRO
0
9.7k
TanStack Routerに移行するのかい しないのかい、どっちなんだい! / Are you going to migrate to TanStack Router or not? Which one is it?
kaminashi
0
510
Lambdaと地方とコミュニティ
miu_crescent
2
360
rootlessコンテナのすゝめ - 研究室サーバーでもできる安全なコンテナ管理
kitsuya0828
3
370
Featured
See All Featured
Documentation Writing (for coders)
carmenintech
65
4.4k
The Cost Of JavaScript in 2023
addyosmani
45
6.7k
Helping Users Find Their Own Way: Creating Modern Search Experiences
danielanewman
29
2.3k
A Philosophy of Restraint
colly
203
16k
Cheating the UX When There Is Nothing More to Optimize - PixelPioneers
stephaniewalter
280
13k
Why Our Code Smells
bkeepers
PRO
334
57k
Fireside Chat
paigeccino
34
3k
Unsuck your backbone
ammeep
668
57k
Building Your Own Lightsaber
phodgson
103
6.1k
The Psychology of Web Performance [Beyond Tellerrand 2023]
tammyeverts
44
2.2k
The Art of Programming - Codeland 2020
erikaheidi
52
13k
How to Ace a Technical Interview
jacobian
276
23k
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