Search code examples
androidworkerdagger-hiltclean-architectureandroid-threading

How to communicate with a worker viewmodel in Martin's Clean Architecture


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
    }
}

Solution

  • 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
    }