Search code examples
androidandroid-activityviewstatic

avoiding static view references but enabling a different activity to modify the views


I am using UsbSerial (com.github.felHR85:UsbSerial:4.5) to stream Serial data to my app from the virtual serial port(over USB). The MainActivity has a a handler that handles the messages from the from the USB Service, similar to the example provided: https://github.com/felHR85/UsbSerial/blob/master/example/src/main/java/com/felhr/serialportexample/MainActivity.java

My App has several Activities, and many of them have views that should change if certain data comes across the serial port. E.g., a TextView may need to update the text shown, a Button should be enabled or disabled.

In order to manipulate the views outside of the onCreate method in each activity, the easiest thing I have tried is to declare the views as private static but I have seen in many places that this is a bad practice. I am setting the View references back to null as described here (under "2. Static Views") to, I think, avoid potential memory leaks: https://blog.nimbledroid.com/2016/09/06/stop-memory-leaks.html
I still don't like the fact that Lint in Android Studio indicates "Do not place Android context classes in static fields." and that this practice seems generally frowned upon.

Each activity has a public static boolean isActive that gets set true in onResume and false in onPause

The handler from the MainActivity decides what to do with the incoming serial data depending on which activity is currently running (e.g. SecondActivity.isActive==true). It then calls a public static method from the current running activity (e.g. SecondActivity.updateViews(serialdata)) which has access to the static View reference in order to do what it needs (e.g. myTextView.setText(serialdata))

If I shouldn't keep a static reference to a view, what are alternatives to achieving what I need, i.e. updating a view element in a SecondActivity from the handler in the MainActivity, while the SecondActivity is already running?


Solution

  • The MainActivity has a a handler that handles the messages from the from the USB Service

    This is not a good plan.

    My App has several Activities, and many of them have views that should change if certain data comes across the serial port.

    This illustrates why the not-good plan is not good. The data come from the serial port is not tied exclusively to MainActivity... so why would MainActivity be handling the USB service messages?

    (it is also fairly likely that you should not have multiple activities here and instead have one activity with multiple fragments, but I'll leave that aside for now)

    what are alternatives to achieving what I need

    I don't know what is on the other end of the serial port that is causing the messages to be sent. For the purposes of this answer, I will call it a "thingy".

    Step #1: Create a ThingyRepository. This will be a class (with a singleton instance) that manages the USB serial connection and receive the messages. It will hold onto some representation of the current state based on past messages (ThingyState) and it will update that state as new messages come in.

    Step #2: Have ThingyRepository expose some sort of "reactive API". That could be something modern, such as LiveData or RxJava. It could be something older, like a callback-based API. Either way, you want ThingyRepository to deliver updated ThingyState objects to interested parties ("observers") when the state changes. So, ThingyRepository converts USB serial messages into updated states, and it emits those states as needed.

    Step #3: Have your UI observe ThingyRepository, receive the ThingyState objects, and update the UI based on those states. Ideally, you would use ViewModel to mediate those communications, to deal with Android's configuration changes (screen rotations, etc.). But, if you wanted to stay "old-school", the activity could register a callback with your ThingyRepository, where ThingyRepository can call the callback with a fresh ThingyState as the data changes based on the USB messages.

    Now, you have decoupled the state changes from any single activity. Each activity can be responsible for figuring out what needs to change in its own UI based on the new state. And, you no longer need static views.