Right now my app has one single activity which has full support of WS actions with listeners and creating new instance of webSocket. I would like to decompose this to one activity which will be like a holder for several fragments.
At the beginning I would like to create webSocket in the activity and then work with created webSocket on fragments. I work with okHttp3 websockets. In general I can for example create one single viewModel where I will initialize websocket, but I need to pass also listener during WS creating:
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
on the other hand I need to have separate listeners for separate fragments. The call of .newWebSocket(...
will create a new instance of webSocket what is not good for me. But when I will have one single shared webSocket listener I will need to handle from which fragment I wait info. Also maybe I decided to do a wrong thing which won't be good.
CodePudding user response:
To use this solution you must be familiar with these fundamentals
- MVVM architecture
- Kotlin flows (reactive programming)
first, create a sealed data class to handle socket events:
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()
}
second, you must create a view model class that holds the web socket object something like this:
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)
}
in your fragments initial view model like this to your view model depends on the parent 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 code :
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
}
}
}
}
}