Compose二三事:初步认识

Compose 是什么?

Compose是Jetpack系列中用于构建原生Android界面的工具库,Jetpack是Google推出的一系列帮助开发者规范代码的库。简单来说就是用代码写UI,也就是声明式UI

声明式UI和命令式UI的区别在于,声明式UI更关心做什么,而命令式UI关心怎么做。
简单来讲,我们直接使用View本身去显示内容,就是声明式,而我们要指定XML要去呈现什么内容,就是命令式。

写一个最简单的界面

下面用一个简单的界面对比一下Compose和常规的xml布局,这是一个非常简单的界面,如下。

在这里插入图片描述

我们常规的写布局的方式

  1. 定义布局文件
  2. 在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>

引入

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }
}

如果用Compose来写

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)
                )
            }
        }
    }
}

当然这样可能看起来比较臃肿,我们可以把布局部分独立出来。如下

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)
        )
    }
}

从以上可以看出:

  • Compose跟Flutter很像,如果有Flutter基础可基本上写Compose无压力
  • 布局逻辑清晰,入手难度不高

为什么要推进Compose?XML有什么问题?

为什么Android选择XML作为的布局语言

  • 可读性高:如果用代码写布局,很明显JAVA代码可读性非常差,布局逻辑非常不清晰
  • 与业务逻辑解耦:xml语言实现与java代码物理隔离

XML加载比较耗时

通过分析setContentView源码,可以得出XML加载是个非常耗时的操作。他的耗时来源于两个操作

  • 将布局文件加载到内存中:IO
  • 解析文件,生成对应的View:反射

当然我们可以避开这两个操作,有两种方式

  • AsyncLayoutInflater:异步加载,就是将IO和反射放到子线程去执行。
  • X2C:将布局文件在编译时转换成用JAVA代码写布局

AsyncLayoutInflater存在的问题是,他并没有解决掉布局加载耗时问题,即使放到了子线程,我们还是要等待布局加载完成,才能进行view的相关操作。

X2C存在的问题是,用JAVA写布局代码很不友好,而且很多属性不支持。

Compose 对比 XML

  • UI更加直观:从布局代码上来看,Compose的逻辑非常清晰,可以在Android Studio上实时预览,甚至可以在手机上实时刷新
  • 布局更加灵活:基于代码的布局方式,可以更方便的动态添加或修改布局,实现更复杂的UI效果
  • 更易于维护和修改:基于代码的布局文件,可以更方便的处理业务逻辑以及实现复用。省去了各种插件都想干掉的findviewbyid
  • 不需要手动更新:Compose的元素会对依赖的数据自动进行订阅,数据改变了,界面自动改变,不需要手动更新

在这里插入图片描述

使用代码写函数还有一个XML不能实现的效果

  Column {
        repeat(4) {
            log("repeat $it")
            Text(text = "Hello $it")
        }
    }

引入Compose

Compose基础配置

  • AS版本:Android Studio Arctic Fox 版本
  • Gradle:7.0 +
  • TargetSdk / CompileSdk:31 +
  • MinSdk:21 +
  • Kotlin:1.6.0+
  • Compose:1.1.1

实际配置

以下是项目引入Compose后实际的依赖情况

config.gradle

ext {
    android = [
            compileSdkVersion: 33,
            buildToolsVersion: "30.0.2",
        		...
    ]
  }

主module下build.gradle

	apply plugin: 'kotlin-kapt'
	...
  
    // 启用Jetpack Compose组件特性
    buildFeatures {
        compose true
    }

    composeOptions {
        kotlinCompilerExtensionVersion '1.1.0'
        kotlinCompilerVersion "1.1.1"
    }

    kotlinOptions {
        jvmTarget = "1.8"
    }


dependencies {
    ...
    // Compose 库的主体包
    implementation 'androidx.compose.material:material:1.3.1'
    implementation "com.google.accompanist:accompanist-systemuicontroller:0.28.0"
    // 将 Activity 支持 Compose
    implementation 'androidx.activity:activity-compose:1.5.0'
    // Compose ui 预览
    implementation 'androidx.compose.ui:ui-tooling:1.1.1'
    implementation 'androidx.compose.ui:ui-util:1.3.3'
    // Compose ui 相关的基础支持
    implementation 'androidx.compose.ui:ui:1.1.1'
    // Compose 基于 ui 层封装的更实用的组件库 (Border, Background, Box, Image, Scroll, shapes, animations, etc.)
    implementation 'androidx.compose.foundation:foundation:1.1.1'
    // Compose ViewModels 存储数据
    implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:2.5.0'
    // Compose LiveData
    implementation 'androidx.compose.runtime:runtime-livedata:1.1.1'
}
	...

项目下build.gradle

...
buildscript {
    dependencies {
        ...
        classpath "com.android.tools.build:gradle:7.0.4"
				...
    }
}

gradle.properties

KotlinVersion=1.6.10

kotlin 升级后的问题

废弃了 ‘kotlin-android-extensions’ 插件。这个插件非常好用,可以直接通过id来引用View实例,但是他也存在一些问题

  • 销毁之后的空指针问题

KAE通过生成缓存解决findviewbyid问题,但是Activity的onDestory和Fragment的onDestoryView之后会清除缓存,这里导致的问题就是如果在这个生命周期时做一些view的操作,会出现空指针问题
如下代码会出现崩溃

import kotlinx.android.synthetic.main.activity_main.*
class MainFragment : Fragment() {
    ...

    override fun onDestroyView() {
        super.onDestroyView()

        textView.text = "Crash!"
    }
}

而下面不会

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."
}

当然KAE也存在一些小问题,比如不存在的id等等,这些影响不大,还有个问题是它不支持Compose。我们可以使用viewbinding作为替代方案,它比KAE更安全

Gradle升级问题

  • maven依赖http的需要新增allowInsecureProtocol属性
        maven {
            url "..."
            allowInsecureProtocol = true
        }
  • 上传aar的方式变为publishing
...
apply plugin: 'maven-publish'

task androidSourcesJar(type: Jar) {
    archiveClassifier.set('sources')
    from android.sourceSets.main.java.srcDirs
}

publishing {
    repositories {
        maven {
            //文件发布到下面目录
            url = MAVEN_URL
            credentials {
                username MAVEN_USERNAME
                password MAVEN_PASSWORD
            }
        }
    }

    publications {
        aar(MavenPublication){
            groupId = groupId1
            artifactId artifactId1
            version versionName
            artifact androidSourcesJar
        }
    }
}

引入Compose的影响

  • 包体积:因为引入了新的库和依赖,所以会有所增加,不过Google的拆包都比较细,可以只引入需要的库。根据Google官方的统计,约会增加2~3M
  • 编译速度:Compose基于Kotlin编写,并且引入了一些新的编译工具和插件,对编译速度有一定的影响。不过如果使用构建缓存,编译速度影响不大,另外,Compose支持即时刷新界面

总结

Compose是未来Android的趋势,这是肯定的。但是他跟kotlin一样,不是一种“必须使用”的方案,他会逐步的替代XML,成为主流的开发方式。

仍需要跟进的问题

  • Compose的绘制原理是什么,Compose的Text和TextView区别在哪
  • Compose比XML快吗?

参考

使用 Jetpack Compose 更快地打造更出色的应用

Android Jetpack Compose —— 集成到现有项目中

要再见了吗,Kotlin Android Extension

扔物线compose

ChatGPT

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值