Two or three things about Compose: preliminary understanding

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

  1. Define the layout file
  2. 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