What is Compose?
Compose is a tool library in the Jetpack series for building native Android interfaces. Jetpack is a series of libraries launched by Google to help developers standardize their code. Simply put, Use code to write UI, that is, declarative UI.
The difference between declarative UI and imperative UI is that declarative UI is more concerned with what to do, while imperative UI is concerned with how to do it.
To put it simply, we directly use View itself to display content, which is declarative, and we want to specify what content to display in XML, which is imperative.
Write the simplest interface
Let’s use a simple interface to compare Compose with the conventional xml layout. This is a very simple interface, as follows.
Our regular way of writing layout
- Define the layout file
- Introduced in Activity
<?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:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:text="Hello World!" /> </FrameLayout>
introduce
class MainActivity : ComponentActivity() {<!-- --> override fun onCreate(savedInstanceState: Bundle?) {<!-- --> super.onCreate(savedInstanceState) setContentView(R. layout. activity_main) } }
If you use Compose to write
class MainActivity : ComponentActivity() {<!-- --> override fun onCreate(savedInstanceState: Bundle?) {<!-- --> super.onCreate(savedInstanceState) setContent {<!-- --> Box( modifier = Modifier .fillMaxWidth() .fillMaxHeight() ) {<!-- --> Text( text = "Hello World!", modifier = Modifier. align(Alignment. Center) ) } } } }
Of course, this may look bloated, we can separate the layout part. as follows
class MainActivity : ComponentActivity() {<!-- --> override fun onCreate(savedInstanceState: Bundle?) {<!-- --> super.onCreate(savedInstanceState) setContent {<!-- --> mainContentView() } } } @Composable fun mainContentView() {<!-- --> Box( modifier = Modifier .fillMaxWidth() .fillMaxHeight() ) {<!-- --> Text( text = "Hello World!", modifier = Modifier. align(Alignment. Center) ) } }
From the above it can be seen that:
- Compose is very similar to Flutter. If you have a Flutter foundation, you can basically write Compose without pressure
- The layout logic is clear, and it is not difficult to get started
Why advance Compose? What’s wrong with XML?
Why Android chooses XML as its layout language
- High readability: If the layout is written in code, it is obvious that the readability of the JAVA code is very poor, and the layout logic is very unclear
- Decoupling from business logic: xml language implementation is physically isolated from java code
XML loading is time consuming
By analyzing the source code of setContentView, it can be concluded that XML loading is a very time-consuming operation. His time-consuming comes from two operations
- Load layout file into memory: IO
- Parse the file and generate the corresponding View: reflection
Of course we can avoid these two operations, there are two ways
- AsyncLayoutInflater: Asynchronous loading is to put IO and reflection into sub-threads for execution.
- X2C: Convert the layout file to write the layout in JAVA code at compile time
The problem with AsyncLayoutInflater is that it does not solve the time-consuming problem of layout loading. Even if it is placed in the child thread, we still have to wait for the layout to be loaded before we can perform view-related operations.
The problem with X2C is that writing layout code in JAVA is very unfriendly, and many attributes are not supported.
Compose vs. XML
- The UI is more intuitive: From the perspective of the layout code, the logic of Compose is very clear, which can be previewed in real time on Android Studio, and can even be refreshed in real time on the phone
- The layout is more flexible: the code-based layout method can dynamically add or modify the layout more conveniently to achieve more complex UI effects
- Easier to maintain and modify: Code-based layout files can handle business logic and achieve reuse more conveniently. It saves the findviewbyid that all kinds of plug-ins want to kill
- No need to manually update: Compose elements will automatically subscribe to the dependent data, the data changes, the interface changes automatically, no manual update is required
Using code to write functions has an effect that XML cannot achieve
Column {<!-- --> repeat(4) {<!-- --> log("repeat $it") Text(text = "Hello $it") } }
Introducing Compose
Compose basic configuration
- AS version: Android Studio Arctic Fox version
- Gradle: 7.0+
- TargetSdk / CompileSdk: 31+
- MinSdk: 21+
- Kotlin: 1.6.0+
- Compose: 1.1.1
Actual configuration
The following are the actual dependencies after the project introduces Compose
config.gradle
ext {<!-- --> android = [ compileSdkVersion: 33, buildToolsVersion: "30.0.2", ... ] }
build.gradle under the main module
apply plugin: 'kotlin-kapt' ... // Enable Jetpack Compose component feature buildFeatures {<!-- --> compose true } composeOptions {<!-- --> kotlinCompilerExtensionVersion '1.1.0' kotlinCompilerVersion "1.1.1" } kotlinOptions {<!-- --> jvmTarget = "1.8" } dependencies {<!-- --> ... // The main package of the Compose library implementation 'androidx.compose.material:material:1.3.1' implementation "com.google.accompanist:accompanist-systemuicontroller:0.28.0" // support Compose with Activity implementation 'androidx.activity:activity-compose:1.5.0' // Compose ui preview implementation 'androidx.compose.ui:ui-tooling:1.1.1' implementation 'androidx.compose.ui:ui-util:1.3.3' // Basic support related to Compose ui implementation 'androidx.compose.ui:ui:1.1.1' // Compose is a more practical component library based on ui layer encapsulation (Border, Background, Box, Image, Scroll, shapes, animations, etc.) implementation 'androidx.compose.foundation:foundation:1.1.1' // Compose ViewModels store data implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:2.5.0' // Compose LiveData implementation 'androidx.compose.runtime:runtime-livedata:1.1.1' } ...
build.gradle under the project
... buildscript {<!-- --> dependencies {<!-- --> ... classpath "com.android.tools.build:gradle:7.0.4" ... } }
gradle.properties
KotlinVersion=1.6.10
Problems after kotlin upgrade
The ‘kotlin-android-extensions’ plugin is deprecated. This plugin is very easy to use, you can directly refer to the View instance by id, but it also has some problems
- Null pointer problem after destruction
KAE solves the findviewbyid problem by generating a cache, but the activity’s onDestory and Fragment’s onDestoryView will clear the cache afterward. The problem here is that if you do some view operations during this life cycle, there will be a null pointer problem.
The following code will crash
import kotlinx.android.synthetic.main.activity_main.* class MainFragment : Fragment() {<!-- --> ... override fun onDestroyView() {<!-- --> super.onDestroyView() textView. text = "Crash!" } }
while the following will not
lateinit var textView: TextView override fun onViewCreated(view: View, savedInstanceState: Bundle?) {<!-- --> super.onViewCreated(view, savedInstanceState) textView = view.findViewById(R.id.textView) } override fun onDestroyView() {<!-- --> super.onDestroyView() textView.text = "Nothing happened." }
Of course, KAE also has some minor problems, such as non-existent ids, etc., which have little impact. Another problem is that it does not support Compose. We can use viewbinding as an alternative, it is more secure than KAE
Gradle upgrade problem
- Maven needs to add the allowInsecureProtocol attribute if it relies on http
maven {<!-- --> url "..." allowInsecureProtocol = true }
- The method of uploading aar is changed to publishing
... apply plugin: 'maven-publish' task androidSourcesJar(type: Jar) {<!-- --> archiveClassifier.set('sources') from android.sourceSets.main.java.srcDirs } publishing {<!-- --> repositories {<!-- --> maven {<!-- --> //The file is published to the following directory url = MAVEN_URL credentials {<!-- --> username MAVEN_USERNAME password MAVEN_PASSWORD } } } publications {<!-- --> aar(MavenPublication){<!-- --> groupId = groupId1 artifactId artifactId1 version versionName artifact androidSourcesJar } } }
The impact of introducing Compose
- Package size: Because of the introduction of new libraries and dependencies, it will increase. However, Google’s unpacking is relatively detailed, and only the required libraries can be imported. According to Google’s official statistics, appointments increased by 2~3M
- Compilation speed: Compose is written based on Kotlin, and introduces some new compilation tools and plug-ins, which have a certain impact on the compilation speed. However, if you use the build cache, the compilation speed has little effect. In addition, Compose supports instant refresh interface
Summary
Compose is the future trend of Android, that’s for sure. But like kotlin, it is not a “must use” solution. It will gradually replace XML and become the mainstream development method.
Issues that still need to be followed up
- What is the drawing principle of Compose? What is the difference between Compose’s Text and TextView?
- Is Compose faster than XML?
Reference
Build better apps faster with Jetpack Compose
Android Jetpack Compose – Integrate into existing projects
Goodbye, Kotlin Android Extension
throw line compose
ChatGPT