Kotlin Multiplatform项目探索之KMChat

Kotlin Multiplatform项目探索之KMChat

kotlin-multiplatform-use-cases
这是当前在 Kotlin Multiplatform 官方文档中提议的用例结构。Kotlin Multiplatform 中包含的许多子组件已经发布了稳定版本,即使目前不稳定的组件也在迅速更新。

本文中的演示项目包括 Compose Multiplatform - Web (Experimental)、iOS (Alpha) 和 Kotlin Multiplatform - Kotlin/Native (Beta)。在进行产品项目开发时,使用这种方法可能会导致一些问题。

通用代码随处可见

在开发软件时,各个平台上都存在重复的实现,被称为通用代码。这项任务不仅需要开发成本,还包含了许多开销,比如通信成本。JetBrains 提议使用 Kotlin Multiplatform,可以将通用代码开发为单个代码库,有效地解决了这些问题。

KMChat

这是我实现的一个聊天应用项目,旨在更好地理解官方文档中提出的用例。通过这个项目,我将学习如何使用Kotlin Multiplatform编写通用代码,并将其应用于所有平台。

我还使用Compose Multiplatform实现了所有平台的用户界面。

预览结果

  • 创建房间 → 加入房间 → 设置用户名
    Desktop / Web / AOS / iOS
    这是仅实现了聊天服务的最少功能的客户端的结果。您可以通过使用Compose多平台实现UI在所有平台上的结果。

Github

  • https://github.com/ColaGom/km-chat-common
  • https://github.com/ColaGom/km-chat-backend
  • https://github.com/ColaGom/km-chat-client

工程结构

kmchat structure
常用存储库由客户端和后端存储库中的子模块导入。

Common

  • 它包括聊天服务中使用的共同代码。
  • 我决定将演示项目的共同代码中包括DTO类和用例,这些类和用例同样包含在客户端和后端的业务逻辑中。

DTO

data class Chat
data class ChatMessage
data class ChatRoom
data class ChatUser

data class SendChat
data class CreateChatRoom
data class DeleteChatRoom

UseCase

interface UseCase<INPUT, OUTPUT> {
    
    
    suspend fun execute(input: INPUT): OUTPUT
}

interface CreateChatRoomUseCase : UseCase<CreateChatRoom, ChatRoom>
interface GetAllChatRoomUseCase : VoidUseCase<List<ChatRoom>>
interface GetChatRoomUseCase : UseCase<Long, ChatRoom?>

OverView common

overview dependency or data flow
后端和客户端项目均使用UseCaseImpl实现业务逻辑。

后端 — ktor

  • 实现了聊天室和与用户相关的API功能。
  • 使用WebSocket实现了聊天功能。
  • 存储库采用内存方式实现存储所有数据。

客户端 — Compose Multiplatform

  • 支持AOS、iOS、Web和桌面平台。
  • 使用Kotlin Multiplatform实现了所有平台的客户端逻辑。
  • 使用Compose Multiplatform实现了所有平台的用户界面。

我尝试了两种使用Compose Multiplatform的方式。

  1. 使用Compose multiplatform实现整个屏幕 (Web, Desktop)。
//in shared common
@Composable
fun ChatApp() {
    
    
    var selectedRoom by remember {
    
    
        mutableStateOf<ChatRoom?>(null)
    }

    Theme {
    
    
        Surface {
    
    
            Column(modifier = Modifier.fillMaxSize()) {
    
    
                val room = selectedRoom
                if (room != null) {
    
    
                    ChatScreen(room.id)
                    DisposableEffect(room) {
    
    
                        onDispose {
    
    
                            selectedRoom = null
                        }
                    }
                } else {
    
    
                    ChatRoomListScreen {
    
    
                        selectedRoom = it
                    }
                }
            }
        }
    }
}
  1. 在本地 UI(AOS、iOS)中实现复合 UI。
class MainActivity : ComponentActivity() {
    
    
    private val chatLauncher = registerForActivityResult(ChatActivity.Contract()) {
    
     }
    override fun onCreate(savedInstanceState: Bundle?) {
    
    
        super.onCreate(savedInstanceState)
        setContent {
    
    
            ChatRoomListScreen(
                onClickRoom = {
    
     room ->
                    chatLauncher.launch(room.id)
                }
            )
        }
    }
}
@main
struct iOSApp: App {
    
    
    var body: some Scene {
    
    
        WindowGroup {
    
    
            MainView()
        }
    }
}

protocol ListViewListener : AnyObject {
    
    
    func clickRoom(room: Km_chat_commonChatRoom)
}

class MainViewModel: ObservableObject, ListViewListener {
    
    
    @Published var selectedRoom: Km_chat_commonChatRoom?
    
    var roomId: Int64 {
    
     ... }
    var entered: Bool {
    
     ... }
    
    func clickRoom(room: Km_chat_commonChatRoom) {
    
    
        selectedRoom = room
    }
}

struct MainView: View{
    
    
    @StateObject private var viewModel = MainViewModel()
    
    var body: some View {
    
    
        ListView(listener: viewModel)
            .sheet(isPresented: $viewModel.entered, content: {
    
     ChatView(roomId: viewModel.roomId) })
    }
}

struct ChatView: UIViewControllerRepresentable {
    
    
    var roomId: Int64
    
    func makeUIViewController(context: Context) -> UIViewController {
    
    
        return ControllersKt.ChatController(roomId: roomId)
    }
    
    func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
    
    
    }
}

struct ListView: UIViewControllerRepresentable {
    
     ... }

结论

使用Kotlin Multiplatform似乎可以减少在各个层中存在的重复代码。

以下是将Kotlin Multiplatform应用于三层分层项目的两种方式的描述。

普通结构


上图显示了一个典型工作客户端和后端的结构。我假设所有项目都是按照三层结构进行开发的。在整个层中常常存在大量重复的代码。

扫描二维码关注公众号,回复: 15482997 查看本文章

使用Compose多平台技术


上图显示了KMChat的结构。我使用Kotlin多平台来共享通用代码,甚至为所有客户端实现用户界面。这种方法有潜力在某些平台(Web、iOS)上显著降低用户界面的质量,与本地(Native)相比。

共享演示业务,本地用户界面


上图仅显示演示业务层作为共享模块实现,UI作为本地UI实现。它在保持本地UI开发优势的同时,减少了多个层次的代码重复。

共享业务逻辑及平台组件构建UI


对于具有稳定性的平台,使用Compose实现UI,而其他平台使用本机UI。

参考

https://kotlinlang.org/lp/multiplatform/
https://kotlinlang.org/docs/multiplatform.html
https://www.jetbrains.com/lp/compose-multiplatform/

猜你喜欢

转载自blog.csdn.net/u011897062/article/details/131411492