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?
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.