如何使用 Kotlin Multiplatform 创建一个 Mac 应用

kotlin 官方推出了 Kotlin Multiplatform 技术(后面简称KMP),可以让我们开发多平台的应用。这一篇文章将介绍如何使用 KMP 开发一个在 Mac 上使用的应用。

开发环境搭建

首先我们需要在 apple store 中安装 xcode

安装完成后,我们从 KMP官网 下载对应的模版。如下图所示,由于这里我们只需要开发 Mac 上的应用,因此只需要选中 Desktop 选项,然后点击 DOWNLOAD 下载模版。

下载完模版后解压,并使用 Android studio 打开对应的项目文件。

应用打包

如下图所示,Android studio 打开项目后,我们可以在右侧的 Gradle 中找到 composeApp -> Tasks ->compose desktop -> packageDmg ,点击执行 packageDmg 任务,把当前的应用打包。

打包成功后,就可以在 build -> compose -> binaries -> main -> dmg 中找到打包成的应用来。

在 mac 中安装该应用,打开就可以看到如下图的界面了,这个界面是我们下载的模块的默认实现。

实现adb截图功能

前面介绍了如何搭建环境、并且打包成一个 mac 应用。现在我们来修改模版的代码,来实现一个简单的adb截图的应用。如下图所示,点击截图的按钮,就可以给手机截图,并主动导出截图的文件到指定目录下。

首先我们先来看看,下载模版的目录结构,如下图所示:

在模版的 src 目录下,有 commonMaindesktopMain 两个目录。commonMain存放的是所有平台上公共的代码,由于我们只需要在 mac 上运行,所以这里就不需要关注;desktopMain 则是对应的mac平台的代码。其中 main.kt 是应用的启动入口;App.kt 是我们写逻辑和UI的位置;Platform.kt 则是存放平台相关函数的地方。

由于我们只是简单地实现一个截图的功能,可以直接往 App.kt 上修改。在 Kotlin Multiplatform 中,UI是使用 jetpack compose 来声明的,具体组件的使用可以看 # Jetpack compose------基础控件。代码示例如下:

kotlin 复制代码
@Composable
@Preview
fun App() {
    MaterialTheme {
        Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) {
            Button(onClick = { capture() }) {
                Text("截图")
            }
        }
    }
}

/**
 * 截图
 */
fun capture() {
    CoroutineScope(Dispatchers.IO).launch {
        runCatching {
            val storePath = getDownloadsDirectoryPath()
            val captureImageName = generateFileName()
            // 截图
            val process = Runtime.getRuntime().exec("adb shell screencap -p /sdcard/$captureImageName")
            // 等待截图完成
            while (process.isAlive) {
                delay(500)
            }
            // 导出到下载目录下
            Runtime.getRuntime().exec("adb pull /sdcard/$captureImageName $storePath")
            // 打开下载目录
            val downloadDir = File(storePath)
            if (Desktop.isDesktopSupported()) {
                val desktop = Desktop.getDesktop()
                if (downloadDir.exists()) {
                    desktop.open(downloadDir)
                } else {
                    println("Download directory does not exist.")
                }
            } else {
                println("Desktop is not supported.")
            }
        }
    }
}

/**
 * 获取下载目录路径
 */
fun getDownloadsDirectoryPath(): String {
    val userHome = System.getProperty("user.home")
    val downloadsPath = Paths.get(userHome, "Downloads")
    val downloadsDir = downloadsPath.toFile()

    if (downloadsDir.exists() && downloadsDir.isDirectory) {
        return downloadsDir.path
    } else {
        throw IllegalStateException("Downloads directory not found")
    }
}

/**
 * 生成文件名
 */
fun generateFileName(): String {
    val dateFormat = SimpleDateFormat("yyyyMMddHHmmss")
    val currentDate = Date()
    val timeStamp = dateFormat.format(currentDate)
    return "$timeStamp.png"
}

点击截图,就可以看到弹出来下载目录的弹窗,效果如下图所示:

相关推荐
aqi0021 小时前
FFmpeg开发笔记(九十六)采用Kotlin+Compose的视频编辑器OpenVideoEditor
android·ffmpeg·kotlin·音视频·流媒体
Lei活在当下2 天前
【日常知识积累】Kotlin let 函数、inline 函数以及 DSL
android·kotlin·编程语言
橙子199110162 天前
Scaffold
android·kotlin·android jetpack
程序员老刘3 天前
Kotlin vs Dart:当“优雅”变成心智负担,我选择了更简单的 Dart
flutter·kotlin·dart
QING6183 天前
Kotlin协程:Job.cancel() 和 Scope.cancel() 的区别详解!!!
android·kotlin·android jetpack
alexhilton3 天前
Jetpack ViewModel内幕:内部机制与跨平台设计
android·kotlin·android jetpack
QING6184 天前
Kotlin Flow 的 emit 和 tryEmit 有什么区别 ?
android·kotlin·android jetpack
Kapaseker4 天前
面试官最爱问的 Android 数据传递问题
android·kotlin
I'm Jie5 天前
Gradle 多模块依赖集中管理方案,Version Catalogs 详解(Kotlin DSL)
android·java·spring boot·kotlin·gradle·maven
zhangphil5 天前
Kotlin管道Channel融合flow流,协程实现Android废弃的AsyncTaskLoader(A)
kotlin