KMM-Compose实践分享

背景

  1. 最近通过Compose写android/ios 一体式应用, 只要通过compose书写ui+两端平台差异化代码即可实现跨平台应用,这篇文章主要分享在开发过程中遇到的问题。

编译问题

问题一:提示找不到kspIosX64

sync过程中提示找不到kspIosX64这个系统版本,原因是commonMain的build.gradle.kts文件没有支持上iosX64,添加上iosX64即可。

add("kspIosX64", libs.androidx.room.compiler)提示找不到kspIosX64
解决办法

下面截图中添加绿色框内容即可


image.png
问题二:exit code: PROCESSING_ERROR

在接入room跨平台数据库后,编译ksp的android插件时候报错。主要错误原因是因为我@Database修饰的entities类,不是用@Entity修饰的类,而是一个普通的class类。

Caused by: java.lang.Exception: KSP failed with exit code: PROCESSING_ERROR
    at com.google.devtools.ksp.gradle.KspAAWorkerAction.execute(KspAATask.kt:643)
    at org.gradle.workers.internal.DefaultWorkerServer.execute(DefaultWorkerServer.java:63)
    at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:66)
    at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:62)
    at org.gradle.internal.classloader.ClassLoaderUtils.executeInClassloader(ClassLoaderUtils.java:100)
    at org.gradle.workers.internal.NoIsolationWorkerFactory$1.lambda$execute$0(NoIsolationWorkerFactory.java:62)
    at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:44)
    at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:41)
解决办法

这里定义的存储类错误了,应该是需要@Entity修饰的类,改成TodoEntity(@Entity)类即可解决上述报错问题。


image.png

学习补充注意room跨平台文档

问题三:ios编译遇到错误@ConstructedBy definition must be an 'expect' declaration.

错误详细信息如下,这里错误的原因是因为我下面的getAllCourseByYearAndMonth方法是通过Flow函数来实现的,在Android上编译运行都没问题,但是iOS平台所有的@Dao修饰的函数都必须用suspend来修饰,所以不支持Flow写法。

//错误详细内容
e: [ksp] /Users/admin/Library/kotlin_project/music_record/composeApp/src/iosMain/kotlin/com/music/classroom/db/Database.ios.kt:61: The @ConstructedBy definition must be an 'expect' declaration.

//代码错误地方
@Query("SELECT * FROM TodoEntity WHERE year =:year and month =:month")
fun getAllCourseByYearAndMonth(year: Int, month: Int): Flow<List<TodoEntity>>
解决办法

这里只需要把getAllCourseByYearAndMonth的Flow去掉,添加上suspend挂起即可。

@Query("SELECT * FROM TodoEntity WHERE year =:year and month =:month")
suspend fun getAllCourseByYearAndMonth(year: Int, month: Int): List<TodoEntity>

使用问题

问题一:数据库没办法使用
  1. 根据上面room文档写入对应依赖后,发现没办法对接到compose页面中,android提示为初始化/ios提示指针异常。其中缺少最关键代码,room文档是有提出来(room如何实例化数据库),但是没有告诉我们具体该如何使用(后面看他们提供demo才知道正确用法)。


    room如何实例化数据库
解决办法
  1. 首先通过expect声明跨平台差异函数,需要在androidMain/iosMain分别实现对应函数,但是官网的ksp:2.2.21-2.0.4 + room:2.8.3已经可以自动生成差异化函数了,所以都不需要我们自己去实现AppDatabaseConstructor函数,编译过程会生成。
@Database(entities = [TodoEntity::class], version = 1)
@ConstructedBy(AppDatabaseConstructor::class)
abstract class AppDatabase : RoomDatabase() {
    abstract fun getDao(): TodoDao
}

// The Room compiler generates the `actual` implementations.
@Suppress("KotlinNoActualForExpect")
expect object AppDatabaseConstructor : RoomDatabaseConstructor<AppDatabase> {
    override fun initialize(): AppDatabase
}
  1. 上面第一步生成了差异化代码了,我们该如何实例化数据库呢?我们需要通过Room.databaseBuilder<AppDatabase(看你定义名字)>来实例化我们的数据库(这里图【room如何实例化数据库】),只不过官网没有详细说到是通过Room.databaseBuilder来回去对应的Builder

    image.png

  2. 在原生层实例化化数据库对象要如何传递到下面子Compose View里面呢?这里我们就需要用到CompositionLocalProvider来实现了,我这里的LocalAppContainer是放在commonMain模块下的object class,provides意思是LocalAppContainer这个变量的值来源于AppContainer(iOS也类似这样子实现,这里留个动手机会给大家)。

注意:提供provider给下面所有子View访问对应属于(必须在setContent下面)

数据库实例对象传递.png

class MusicApplication: Application() {
    lateinit var container: AppContainer
    override fun onCreate() {
        super.onCreate()
        AppUtils.application = this
        container = AppContainer(Factory(this))
    }
}

class AppContainer(
    private val factory: Factory,
) {
    val dataRepository: CourseRepository by lazy {
        CourseRepository(
            database = factory.createRoomDatabase(),
            scope = CoroutineScope(Dispatchers.Default + SupervisorJob()),
        )
    }
}
  1. 直接在commonMain模块下的Compose 函数调用即可。
@Composable
fun AllCourse() {
    val db = LocalAppContainer.current.dataRepository
    ……
    val response = db.getAllCoursesByYearAndMonth(year, month)
}

版本分享

分享下当前编译通过版本:

//libs.version.toml
agp = "8.11.2"
android-compileSdk = "36"
android-minSdk = "24"
android-targetSdk = "36"
androidx-activity = "1.11.0"
androidx-appcompat = "1.7.1"
androidx-core = "1.17.0"
androidx-espresso = "3.7.0"
androidx-lifecycle = "2.9.4"
androidx-navigation = "2.9.1"
androidx-testExt = "1.3.0"
composeMultiplatform = "1.9.0"
junit = "4.13.2"
kotlin = "2.2.20"
room = "2.8.3"
sqlite = "2.6.1"
ksp = "2.2.21-2.0.4"

成果分享

android效果图
ios效果图
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容