I have my MainActivity connecting by Nearby Connections 2.0 to Rpi3 with Android Things installed. And I need to start SecondActivity or ThirdActivity depending on payload I've received in MainActivity. Either second and third activities have their own PayloadCallbacks. So in order to get Payload there, I have to disconnect in MainActivity and connect again in the new activity with new PayloadCallback set in acceptConnection() method.
Is there a way to save existing connection, but change PayloadCallback?
Code can be found on github.com/Mkryglikov/BestCafe. I'm talking about ConnectActivity and ActiveOrderActivity
The typical pattern for what you're trying to do is to keep all your Nearby Connections code in one place (a class named something like NearbyConnectionsManager that all the Activities have access to), and to have all your different Activities register handlers with that class for the different BYTES Payloads you're expecting.
That way, NearbyConnectionsManager's onPayloadReceived() checks the value of the payloadString that it receives, and has a switch statement that invokes the handlePayloadString() callback method of the relevant Activity, thus keeping each Activity's logic contained within itself, while avoiding the need to disconnect and reconnect from your peers.
You may use a Service to encapsulate your Nearby Connections stuff (connection, events, errors, payloads), and allow your components like Activities and Fragments to bind and query the service (via an IBinder). Whether this service will be more persistent or short lived, it is up to your needs.
Related
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.
I am developing an android app with BLE API from android. My app needs to connect to a BLE device, and remain connected as long as it is in range and turned on. I need to read data from it, and write data to it.
I am trying to follow the MVP architecture pattern, not strictly since activities are the starting point. But anyway, I wanted to know where should I put the interaction with Bluetooth? I am searching for answers for the following questions. I have searched StackOverflow, but couldn't find what I was looking for.
Should it be in a service bounded to the UI just like in googlesample ble app ? But, I think that would break the whole mvp architecture.
Should it be a bounded service at all ? If no, what would be the best way to implement the service? In my mind, if it's not bounded to the view, and there is a callback from the background service to display something on the UI, there is a possibility of undefined behavior.
Who should initiate the Bluetooth interaction ? The application class or some activity ?
I am looking for mainly architectural guidance, and best way to go about developing this app.
Since you have the requirement that the Bluetooth connection should keep working in the background, you should have a Foreground Service somewhere running in your app process. This will make sure your app process will be kept alive, but requires an icon to be displayed in the phone/tablet's top bar.
Whether you actually put your BLE code in this service class or not doesn't matter for the functionality.
There are of course many ways to achieve good architecture but here is my approach.
My approach would be to have a singleton class that handles all your BLE scanning, connections and GATT interactions (from now on called Manager). Since some BLE operations needs an Android Context, a good way is to use the Application context as context. Either follow Static way to get 'Context' on Android? to be able to fetch that context at any time or subclass the Application class and from its onCreate call some initialization method in your Manager and pass the context. Now you can keep all BLE functionality completely separated from Android Service/Activity/Application stuff. I don't really see the point in using bounded services etc. as long as you keep everything in the same process.
To implement a scan functionality, you can have a method in your Manager that creates Scanner objects. Write the Scanner class as a wrapper to Android's BLE scanner and expose methods to start/stop scan. When you create a Scanner that method should also take an interface as argument used for callbacks (device reports and errors). This class can now be used in for example an Activity. Just make sure that the scanner gets stopped in the Activity's onStop method to avoid leakage of objects.
There are several reasons for having a wrapped custom Scanner object instead of using Android's BLE scan API directly in the Activity. First you can apply the appropriate filtering and processing of advertising packets so it handles your type of peripheral and can show high level parameters (decoded from advertising data) in your custom advertising report callback. The manager should also listen to broadcasts when Bluetooth gets started/stopped/restarted and keep track of all started Scanners so the Scanners are restarted seamlessly when Bluetooth restarts (if you want this functionality). You may also want to keep track of timestamps of all scan starts/stops so you can workaround the new restrictions in Nougat that limits it to 5 scans per 30 seconds.
Use a similar approach when you want to connect to your peripherals. You can for example let the Manager create Device objects which have methods to start/stop the connection and have a callback interface to report events. For each supported feature (for example read some remote value) you should expose a method which starts the requests and have a callback which is called when the result arrives. Then your Manager and Device class takes care of the GATT stuff (including enqueuing all your GATT requests so you only have one outstanding GATT operation at a time). Just make sure you can always abort or ignore the result when you don't want the result, for example if an Activity's onStop or onDestroy method is called.
Since you probably want to reconnect automatically in case the device gets disconnected, you should use the autoConnect flag and set it to true when establishing the connection, which assures this. Again, the Manager should keep track of all active Device objects and automatically recreate the BluetoothGatt object when Bluetooth is restarted.
To be able to display different kind of UI stuff, like for example automatically show a warning message in your Activity when Bluetooth is turned off and remove it when Bluetooth is turned on, you should be able to register Listeners to your Manager. Have a method in your Manager for registering/unregistering a listener (which is really just a Callback) object, keep track of all the listeners and when Bluetooth state change happens, call all listeners. Then in your Activity's onStart you register a listener and in onStop you unregister it. You can have a similar approach for your Device's BLE notifications, where applicable.
What's left is how you deal with different Threads. As you might know most BLE callbacks from Android's API happen on Binder threads, so you may not update the UI from them. If you otherwise in your app don't use anything other than the main thread, you can for example post all invocations of callbacks in the Manager to the main thread, or maybe move to the main thread directly when the callback from Android's BLE stack arrives (but then be aware of things like https://bugs.chromium.org/p/chromium/issues/detail?id=647673). Just make sure you never touch the same variables from different threads.
Also if you target API 23 or higher you need UI code to let the user give permission to Location to be able to start scan. I suggest you implement this in your UI code and not in the Manager, or implement some "wrapper" or helper method in the Manager to do this.
RxCentralBle provides a paradigm for use in an app. The library design clearly shows the structure of the library. In short, RxCentralBle provides reactive interfaces for the primary Bluetooth LE actions:
BluetoothDetector - detect phone Bluetooth State
Scanner - scan for peripherals
ConnectionManager - connect to a peripheral
PeripheralManager - queue operations to communicate with a peripheral
It's recommended to subscribe to these interfaces on a background thread and ensure resources and subscriptions live at the application scope i.e. member variables of your Application class. As long as your Application is running, all Bluetooth LE resources will remain alive and active.
Check out RxCentralBle's Wiki and sample app to learn more.
I am currently working on an messaging application for Android that communicates with other devices using Nearby Messages API. Since this is the first time that I work with this API, I would like to know if there is a pattern or strategy to handle the connections.
For instance, when the user changes the activity (e.g. opens a new conversation), I would like to keep the connection active, so I would like to work with a Connection Manager or something to keep listening and parsing the messages.
We kept working on our code, and finally we decided to implement a ConnectionManager as a single instance. This way all the activities in the application are able to access to the same methods. We also avoid to have several instances of GoogleApiClient, and then know if we are connected or not (e.g. isConnected() method).
However, we also needed in some methods the context or the activity, but we solved passing these parameters as arguments in those methods.
To sum up:
Singleton pattern: avoid creating several instances of the same GoogleApiClient
Proxy pattern: encapsulate GoogleApiClient methods in a class that handles the whole connection, instead of delegating this task on activities
I am developing an application which has around 8 Activities, and a class which is used to connect/receive data to/from an embedded Bluetooth chip. When I started, a Bluetooth object was initialized in my initial Activity, where there was a Handler which received messages from the Bluetooth object.
After poking around on the internet for a while, it seems like the best idea for me is to turn my class into an Application subclass. However, doing this removes the need for me to initialize an object in the MainMenu, which removes my ability to pass it the Handler used.
Does anyone know of a way to eliminate the need for a Handler, so that every time the Bluetooth Application changes it state or receives data, the current Activity can access it?
My main problem with this approach is that the Activity doesn't know when the Bluetooth Application will be sending it messages, the Application waits and listens, and then notifies the Activity when it happens.
OR
Is it bad practice for me to write the Handler into the MainMenu, have it handle messages for ALL the different activities, and then pass the Handler from Activity to Activity?
I'm going to assume that you're trying to achieve the following as it's a little unclear from your question your ultimate aim (sorry!):
Your application has several activities but only one Activity receives the data from the bluetooth device.
The other activities in in your application require the data from the bluetooth device but are not receiving it directly from the bluetooth device. Currently you're providing the data via the one activity mentioned above.
You want to NOT use a Handler to achieve this.
If my above assumptions are correct then you are going along the correct lines but you probably do not want to use a Handler.
You are quite correct in having one Activity handle all the interactions with the Bluetooth device. It simplifies things and provides a much better, cleaner way of handling the Bluetooth device. However you need to get the data from this one Activity to all the others and to achieve this you would probably want to use Broadcasts, BroadcastReceivers and Intents. See here for an overview.
However if you can you might want to take a look at using LocalBroadcastManager as this keeps any broadcasts within your own app's space. Broadcasts are global and should be avoided if you do not need to pass the data outside of your own app due to security implications.
Finally, have you considered using Fragments for your other Activities? Another disadvantage with Broadcasts is there is extra overhead associated with them. If you're keeping data within your app then you can create an interface to be implemented by each of your Fragments and your main activity just calls that interface on the Fragment that is currently selected.
You can use BroadcastReceiver class to send broadcast messages to your activities. see here http://developer.android.com/reference/android/content/BroadcastReceiver.html
When you get the data you need into the application class, you can send it to the activity you want.. just make sure that the activity has registered to receive that broadcast message..
In bluetooth chat example, I want to add another activity. When the connection is made and when the new activity is loaded, how do I still keep the connection alive??
After I load new activity, the onStop method is called and I canned send message using the connection made before. I tried the following code in my new activity:
BluetoothChat bt = new BluetoothChat();
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.special_keys);
bt.sendMessage("hello");
}
I manage to send the 'hello' but it force closes after that.. There must be an efficient way to handle this situation. Please help.
Thanks in advance.
I have recently created an Android application that communicates to a hardware device over Bluetooth. In my application, I have many Activity classes that communicate through the connection, and the application also performs continuous data logging from the connection in the background, too. Therefore, hopefully I might be able to help here. (This is my first attempt at posting an answer on StackOverflow, so go easy on me.)
In my application, I have first of all put all of the Bluetooth code into a separate dedicated class file (which I call BluetoothIF.java). The constructor in this class performs basic adapter initialization, and further methods are provided for tasks like making connections, etc. It contains the Runnable classes for establishing a connection and then transmitting / receiving over the established connection. (Essentially it is based upon the BluetoothChat example.)
I don’t need to perform a massive amount of communication over the Bluetooth connection, and I also needed to have a Service in the background to continuously monitor and log data from the connection as previously mentioned. So, I instantiated my BluetoothIF within the Service. Any Activity classes that need to exchange data over the existing Bluetooth connection do so in my application by exchanging messages with the Service.
Alternatively, if you have no need for using a Service, perhaps you should share the Bluetooth object instance across Activitys using the singleton model or by extending the Application class.