如何为两个片段和一个 activity 创建共享 webSocket?

How to create shared webSocket for two fragments and one activity?

现在我的应用程序只有一个 activity,它完全支持带有侦听器的 WS 操作并创建新的 webSocket 实例。我想把它分解成一个 activity,它就像是几个片段的支架。

一开始我想在 activity 中创建 webSocket,然后在片段上使用创建的 webSocket。我使用 okHttp3 websockets。一般来说,我可以创建一个单独的 viewModel,我将在其中初始化 websocket,但我还需要在 WS 创建期间传递监听器:

val loggingInterceptor: HttpLoggingInterceptor = HttpLoggingInterceptor()
            .setLevel(HttpLoggingInterceptor.Level.BODY)


val client = OkHttpClient.Builder()
            .addInterceptor(AuthToken(this))
            .addInterceptor(loggingInterceptor)
            .build()

val request = Request.Builder()
            .url(
                BuildConfig.CHAT_URL + UtilsClass.userInfo.applicant?.id + "/"
            )
            .addHeader("Sec-WebSocket-Protocol","android")
            .build()


val ws = client.newWebSocket(request, listener) <---- here

另一方面,我需要为不同的片段设置不同的监听器。 .newWebSocket(... 的调用将创建一个新的 webSocket 实例,这对我不利。但是当我将拥有一个共享的 webSocket 侦听器时,我将需要处理我等待信息的片段。也可能我决定做错了,这不会好。

要使用此解决方案,您必须熟悉这些基础知识

  1. MVVM 架构
  2. Kotlin 流程(响应式编程)

首先,创建一个密封数据class来处理socket事件:

sealed class SocketEvent {
    object OpenEvent : SocketEvent()
    data class CloseEvent(val code: Int, val reason: String) : SocketEvent()
    data class Error(val error: Throwable) : SocketEvent()
    data class StringMessage(val content: String) : SocketEvent()
}

其次,您必须创建一个视图模型 class 来保存网络套接字对象,如下所示:

class SocketViewModel : ViewModel() {


    private lateinit var client: OkHttpClient
    private lateinit var socket: WebSocket

    private fun attachWebSocketListener(webListener: WebSocketListener) {
        client = OkHttpClient()
        val request = Request.Builder().url("http://195.201.167.116:9502").build()
        socket = client.newWebSocket(request, webListener)
    }

    suspend fun socketEventsFlow(): Flow<SocketEvent?> = callbackFlow {
        Timber.tag("soxum").d("opening socket")
        val socketListener = object : WebSocketListener() {
            override fun onMessage(webSocket: WebSocket, text: String) {
                trySendBlocking(SocketEvent.StringMessage(text))
            }

            override fun onOpen(webSocket: WebSocket, response: Response) {
                trySendBlocking(SocketEvent.OpenEvent)
            }

            override fun onClosed(webSocket: WebSocket, code: Int, reason: String) {
                trySendBlocking(SocketEvent.CloseEvent(code, reason))
            }

            override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) {
                trySendBlocking(SocketEvent.Error(t))
                cancel("", t)
            }
        }
        attachWebSocketListener(socketListener)
        awaitClose { socket.cancel() }
    }.flowOn(Dispatchers.IO).shareIn(viewModelScope, SharingStarted.Lazily)
}

在您的片段初始视图模型中,您的视图模型取决于父视图模型 activity :

 class FirstFragment : Fragment() {

    private val viewModel by activityViewModels<SocketViewModel>()


    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {

        lifecycleScope.launch {
            viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
                viewModel.socketEventsFlow.collectLatest {
                    // TODO get event and do what you want to to
                }
            }
        }
        return inflater.inflate(
            R.layout.fragment_first,
            container,
            false
        )

    }
    
}

activity代码:

class MainActivity : AppCompatActivity() {

    private val viewModel by viewModels<SocketViewModel>()

    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        lifecycleScope.launch {
            lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
                viewModel.socketEventsFlow.collectLatest {
                    // TODO get event and do what you want to to
                }
            }
        }
     }
}