最重要的是数据要和 UI 解耦,无论你用 Android Service 还是普通类。和后端的 MVC 类似,Dao 、Connection Pool 是全局对象,Controller 只是用于处理用户交互,更新用户 UI 状态。你需要将自己的 ws 业务逻辑抽象到服务层中,viewmodel 负责从服务层获取数据,处理交互事件,service 层管理连接,发送接收处理消息。
至于是否使用 Android Service ,取决于你的应用是否需要在应用界面后继续运行,你还希望服务继续运行直到系统终止服务。一般情况下,例如媒体播放、录音、推送等及时性要起高的需要放到服务中。
一般只需要用普通的类来管理,
至于 ws 数据传输,ws 一般只用于同步状态:
1. 例如多人协作文档,需要同步输入位置,锁定编辑区域。
2. 大型二进制,例如文件、图片、音频,建议分开。
3. 实时翻译等,短时低延迟要求等,使用 ws 传输数据。
下面的示例中将没有处理消息放到了一个 ShardFlow 中,UI 收集这些数据,YourService 负责管理连接创建、关闭,ServiceConnection 处理消息通讯。
class YourApplication : Application() {
lateinit var yourService: YourService
override fun onCreate() {
super.onCreate()
yourService = YourService()
}
}
// 这里用 Application 类管理全局依赖
val Context.application: YourApplication
get() = applicationContext as YourApplication
val Context.yourService: YourService
get() = application.yourService
// 服务层,用于管理链接和处理数据
class YourService {
/**
* 一个简单的没有任何附加逻辑的 WebSocket 连接 Handler ,只是把消息缓存到一个 Flow 中
* 你可以加上你自己的逻辑,比如消息解析,消息处理等,链接重试之类的
*/
class ServiceConnection {
internal var websocket: WebSocket? = null
private val messageBuffer = MutableSharedFlow<String>(
replay = 0,
extraBufferCapacity = 1,
onBufferOverflow = BufferOverflow.DROP_OLDEST
)
val receivedMessages = messageBuffer.asSharedFlow()
fun onConnected(websocket: WebSocket) {
// ...
}
fun onMessageReceived(message: String) {
// ...
}
fun disconnect(code: Int, reason: String, cause: Throwable? = null) {
// ...
}
fun sendMessage(message: String) {
// ...
}
}
private val client = OkHttpClient()
// 一个简单的 WebSocket 连接缓存,只保留一个连接
private var activeConnection: ServiceConnection? = null
@Synchronized
fun connect(): Result<ServiceConnection> {
if (activeConnection != null) {
Result.success(activeConnection!!)
}
return connectChecked().onSuccess {
activeConnection = it
}
}
private fun connectChecked(): Result<ServiceConnection> {
val request = Request.Builder()
.url("wss://echo.websocket.org")
.build()
val connection = ServiceConnection()
return kotlin.runCatching {
client.newWebSocket(request, object : WebSocketListener() {
override fun onOpen(webSocket: WebSocket, response: Response) {
connection.onConnected(webSocket)
}
override fun onMessage(webSocket: WebSocket, text: String) {
connection.onMessageReceived(text)
}
override fun onClosed(webSocket: WebSocket, code: Int, reason: String) {
connection.disconnect(code, reason)
}
override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) {
connection.disconnect(0, t.message ?: "Unknown error", t)
}
})
.apply {
connection.websocket = this
}
connection
}
}
}
// 处理 UI 状态、用户事件,和与服务层拉取数据
class YourViewModel(
application: Application,
) : AndroidViewModel(application) {
private val yourService: YourService
get() = getApplication<Application>().yourService
private var connection: YourService.ServiceConnection? = null
fun connect() {
viewModelScope.launch {
connection = yourService.connect()
.onSuccess {
it.receivedMessages.collect(::onReceiveMessage)
}.onFailure {
// handle error
}.getOrNull()
}
}
private fun onReceiveMessage(
message: String
) {
// update ui state
}
}
class YourUi: Fragment() {
private val viewModel: YourViewModel by viewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
}
override fun onStart() {
super.onStart()
viewModel.connect() // connect to service
}
} |