In my application I have a socket UDP server which listens to broadcast messages and notifies the UI about the message with an interface to update the recyclerview to add the message.
I used to use a thread to listen to the port, but I'm trying to change the project to use Robert Martin's Clean Architecture, so I changed the thread to workermanager worker.
However, now I need a way to notify the viewmodel about a broadcast message received. How can I listen to the socket in worker and notify the viewmodel in a Clean Architecture-compatible way?
This is what I had previously:
Notifier interface:
interface PartyDiscovery {
fun onPartyDiscovered(party: Party)
fun onPartyEnded(party: Party)
}
This is the thread:
class PartyDiscoveryThread(val discovery: PartyDiscovery) : Thread("PartyDiscovery") {
private val TAG = "PartyDiscovery"
override fun run() {
super.run()
val buffer = ByteArray(2048)
var socket: DatagramSocket? = null
try {
Log.e(TAG, "run: Just Started")
socket = DatagramSocket(Party.defaultPort)
socket.broadcast = true
val packet = DatagramPacket(buffer, buffer.size)
socket.soTimeout = 100
while (!interrupted()) {
try {
socket.receive(packet)
val jsonData =
JsonParser.parseString(buffer.decodeToString(0, packet.length)).asJsonObject
val party = Party(
jsonData["partyId"].asLong,
jsonData["partyRoom"].asInt,
jsonData["owner"].asString
)
if (jsonData["action"].asString == "started")
discovery.onPartyDiscovered(party)
else if(jsonData["action"].asString=="ended")
discovery.onPartyEnded(party)
} catch (e: SocketTimeoutException) {
continue
}
}
} catch (e: java.lang.Exception) {
Log.e(TAG, "run: Party Discovery Crashed", e.cause)
}
socket!!.close()
Log.e(TAG, "run: Dying")
}
}
This is the activity:
class MainActivity : ComponentActivity(), PartyDiscovery {
lateinit var discovery: PartyDiscoveryThread
var isDiscovering = false
private val TAG = "MainActivity"
override fun onResume() {
super.onResume()
startDiscovery()
}
fun startDiscovery() {
if (!isDiscovering) {
discovery = PartyDiscoveryThread(this)
discovery!!.start()
isDiscovering = true
}
}
fun stopDiscovering() {
if (isDiscovering) {
discovery!!.interrupt()
isDiscovering = false
}
}
override fun onPause() {
stopDiscovering()
super.onPause()
}
override fun onPartyDiscovered(party: Party) {
// Add to recyclerview
}
override fun onPartyEnded(party: Party) {
// Remove from recyclerview
}
}
After a lot of research about Android WorkManager
i came up with the fact that workers are not a good answer for my problem because workers mostly used for isolated tasks like downloading a large file or scheduling tasks like syncing your database with your server periodically and not a good option when you want to interact with them unless your most interaction with the UI would be a notification so i used the thread
here's what i did:
activity :
@AndroidEntryPoint
class MyActivity : AppCompatActivity() {
private val activityViewModel: PartiesAroundViewModel by lazy {
ViewModelProvider(this).get(PartiesAroundViewModel::class.java)
}
override fun onCreate(savedInstanceState: Bundle?) {
lifecycle.addObserver(activityViewModel)
}
}
viewmodel:
@HiltViewModel
class PartiesAroundViewModel @Inject constructor() : ViewModel(),
DefaultLifecycleObserver {
private val repository: PartiesAroundRepository = PartiesAroundRepository()
override fun onResume(owner: LifecycleOwner) {
super.onResume(owner)
owner.lifecycle.addObserver(repository)
}
override fun onStop(owner: LifecycleOwner) {
super.onStop(owner)
owner.lifecycle.removeObserver(repository)
}
}
repositoty:
class PartiesAroundRepository : LifecycleEventObserver, PartyDiscovery {
private lateinit var discovery: PartyDiscoveryThread
private var isDiscovering = false
override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
if (event == Lifecycle.Event.ON_START) {
if (!isDiscovering) {
discovery = PartyDiscoveryThread(this)
discovery.start()
isDiscovering = true
}
} else if (event == Lifecycle.Event.ON_STOP) {
if (isDiscovering) {
discovery.interrupt()
isDiscovering = false
}
}
}
// Other codes
}