I have a complex application containing a server and a frontend part.
I am trying in the frontend part to update a progress monitor according to an action that takes place in the server part. The action from the server part is called remotely from the frontend. But I am having trouble getting the notifications real time to update the monitor.
My code structure looks somewhat like this:
class frontend_class1{
public void method {
List<String> strings = initializeStrings();
progressMonitor.begingTask("", noOfSteps);
reponse = frontend_class2.method2(strings);
progressMonitor.worked(1);
}
class frontend_class2{
public responseType method2(List<String> strings){
ServerClassRemote remote = new ServerClassRemote();
response = remote.serverMethod(strings);
return response;
}
class server_class{
public serverMethod(List<String> strings){
otherMethod(strings);
}
public otherMethod(List<String> strings){
someOtherMethod(strings);
}
public someOtherMethod(List<String> strings){
for (String s:Strings){
doSomethingWithString(s);
setStatus(true);
}
}
public setStatus(boolean status){
myVar = status;
}
public boolean getStatus(){
return status;
}
My intention is to send a notification to the frontend from the server side, so that the progress monitor is updated with one each time a string is done with.
That is why I included the status methods: to ask from the frontend what the status is, in a separate thread running, theoretically, simultaneously with the other method (serverMethod) and then resetting the status. Something like this:
new Thread(new Runnable() {
public void run() {
if (remote.getChanged()) {
remote.setChanged(false);
}
}
}).start();
But they don't run concurrently. How could I get the status each time a string from the list is finished so that I can update my progress monitor?
Instead of just setting the status is someOtherMethod you could send the update report with a new thread to the front end.
If you do not have a way of sending this update yet you will probably need to have the fornt_end run some kind of server to receive at least these messages. Maybe some RMI, should be easy enough to implement.
Related
Context
I'm building a Flutter Plugin above the DJK SDK. For that, I have to implement the communication with the aircraft on the native side, and I'm doing it with Java. I'm also doing it only for Android.
One of the methods of the API is boolean connectToAircraft(), which must return if the connection with the aircraft succeeded.
Expected/current behavior
After I call connectToAircraft() - which invokes the DJISDKManager.getInstance().startConnectionToProduct() method, I expected to be able to use anything related to aircraft immediately, but this doesn't happen. I have to wait a few seconds before I can retrieve data from the aircraft.
Some code
public class UavApi implements IUavApi, DJISDKManager.SDKManagerCallback {
...
private final CountDownLatch onConnectToUavFinishedSignal = new CountDownLatch(1);
...
public boolean connectToUav() throws InterruptedException {
Logger.v("connectToUav()");
DJISDKManager.getInstance().startConnectionToProduct();
synchronized (onConnectToUavFinishedSignal) {
onConnectToUavFinishedSignal.await();
}
return DJISDKManager.getInstance().getProduct() instanceof Aircraft;
}
...
#Override
public void onProductConnect(#Nullable final BaseProduct baseProduct) {
Logger.v(MessageFormat.format("onProductConnect(product: {0})", baseProduct));
if (baseProduct != null) {
handleProductConnected(baseProduct);
}
}
#Override
public void onProductChanged(#Nullable final BaseProduct baseProduct) {
Logger.v(MessageFormat.format("onProductChanged(product: {0})", baseProduct));
if (baseProduct != null) {
handleProductConnected(baseProduct);
}
}
...
private void handleProductConnected(#NonNull final BaseProduct baseProduct) {
Logger.d(MessageFormat.format("Is null? {0}", baseProduct == null ? "Yes" : "No"));
Logger.d(MessageFormat.format("Type: {0}", baseProduct.getClass().getSimpleName()));
onConnectToUavFinishedSignal.countDown();
}
...
}
Problem
The code above is what I tried to do, but it's not working and guess it's because I'm misunderstanding the use of the onProductChange() and onProductConnect() methods.
The DJISDKManager.getInstance().getProduct() is always returning null.
OBS: It's always returning null immediately after the onConnectToUavFinishedSignal.await() call finishes. After a few seconds, I get a valid instance of the aircraft.
Something I've also noticed is that sometimes the onProductChange() is called with some value that the log outputs as Unknwoun and None. What are those and how can I test for them? Like if (baseProduct == ???) doSomething()
Environment
Android 9
MSDK 4.13.1
Phantom 4 Pro
Difference
According to the SDK Docs onProductChanged is primarily used to detect when the connection status changes from only remote controller connected to a full connection between the aircraft and the SDK running on your device.
Keep in mind that when the aircraft is disconnected, this method will be called with an instance of an aircraft, but this instance will come with property isConnected as false. If you print the aircraft object to the console you will notice that if isConnected is true, it will print the aircraft name, otherwise, it will print "None".
As long for the onProductConnect, it will be called always after DJISDKManager.getInstance().registerApp() succeeded or after you manually connect to the aircraft with success using DJISDKManager.getInstance().startConnectionToProduct(). In my tests, even though the app registration succeeds, the method will return false, so you might need to check if the SDKManagerCallback::onRegister results in DJISDKError.REGISTRATION_SUCCESS.
Solution
You need to listen to component change events. Unfortunately just because the product is connected it does not mean that the individual components, such as the flight controller, camera etc are connected. You will need to implement onComponentChange and add a listener to detect when a component is connected. These don't always connect in the same order and may start to connect before or after the product is connected.
#Override
public void onComponentChange(
BaseProduct.ComponentKey componentKey,
BaseComponent oldBaseComponent,
BaseComponent newBaseComponent
) {
newBaseComponent.setComponentListener(isConnected -> {
// check if component connected and access data
if (isConnected) {
if(componentKey == ComponentKey.FLIGHT_CONTROLLER) {
// DJISDKManager.getInstance().getProduct() should no longer be null
DJISDKManager.getInstance().getProduct().getModel();
}
}
})
}
Let's say I have an android app with a single activity that contains a button. When I click the button I'd like to make several requests to a rest API that return JSON response. Then I parse the response to a java object and persist it with Room. For the http requests I implemented a Volley request queue as singleton.
The requests are asynchronous and deliver their responses back to the UI thread. There I let Room persist the objects.
I send my http request like this:
RestService.requestSomeData(context, objectId, new ResponseListener() {
#Override
public void onRestSuccess(String response) {
// parse response JSON
// call the insert method
}
#Override
public void onRestError(int code, String errorMessage) {
// handle error
}
}
Since Room forces you to dispatch the queries to worker threads, I'm using RxJava to handle the task. So, for example my Insert method returns an ArrayList of the IDs of the inserted objects wrapped in a Single<ArrayList<Integer>>. Then I call the Insert method and subscribe to the result like this:
myDisposable = MyDatabase.getInstance().myDao()
.insert(myObject)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(idList -> {
Log.d(TAG, "IDs inserted: " + idList.toString());
}, Throwable::printStackTrace);
However, I want to chain multiple requests to the server and then get notified when all are complete and the DB insertions are ready in order to update the UI (e.g. display confirm message, disable the save button). I read numerous articles but nowhere I could find how to perform this apparently easy task. Basically what I want to achieve is:
// a some sort of container for all the observables I get from the database insertions
private Object aPoolOfObservables;
RestService.requestSomeData(context, objectId, new ResponseListener() {
#Override
public void onRestSuccess(String response) {
// parse response JSON
aPoolOfObservables.add(MyDatabase.getInstance().myDao()
.insert(myObject)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()));
}
}
// repeat this n-times more
...
aPoolOfObservables.subscribe(new Listener() {
#Override
public void onComplete() {
// update UI
}
});
Then perform this request multiple times and add the responses to the collection of Single<> (or Maybe<> or Flowable<>) responses and subscribe not to every single stream but to the collection, because I only care that all the operations are complete. Chaining them by firing a request in the onRestSuccess of the previous one seems like a pretty awful solution.
Do you know if there is a RxJava mechanism that allows this?
Is there any general approach/design pattern to handle this situation? I can think of numerous cases when you'd like to e.g. enable a button only after multiple requests have been performed and delivered results. How do you create and subscribe to such event in the context of RxJava? I haven't worked a lot with reactive data so any knowledge will be appreciated.
You can wrap each request in a Single<Pair<ArrayList<Integer>, String>> to store each JSON responses per request. Then, execute them all together with Single.zip(...)
private CompositeDisposable disposables;
private ArrayList<Single<Pair<ArrayList<Integer>, String>>> singles;
RestService.requestSomeData(context, objectId, new ResponseListener() {
#Override
public void onRestSuccess(String response) {
// parse response JSON
// kotlin syntax
singles.add(
MyDatabase.getInstance().myDao().insert(myObject)
.flatMap { ids: ArrayList<String> ->
// transform single to include JSON response
return#flatMap Single.just(Pair(ids, response))
}
);
}
}
// kotlin syntax
disposables.add(
// execute all singles
Single.zip(singles) {}.subscribe()
);
I have phone contact numbers list stored in an array and called contactsString[]
and in an online database registered users numbers
I want to count how many registered users are there
and there is my code
for (i=0;i<contactsString.length-1;i++){
Phone phone=new Phone();
phone.phone=contactsString[i]
WebService.getInstance().getApi().checkNumber(phone).enqueue(new Callback<MainResponse>() {
#Override
public void onResponse(Call<MainResponse> call, Response<MainResponse> response) {
if (response.body().status==1){
availableUsers++;
}
}
#Override
public void onFailure(Call<MainResponse> call, Throwable t) {
}
});
}
my problem is the web service response is delayed so it don't count and availableUsers is printed it's initial value which is 0
I would try better sending an array of Phone objects. In this way you would get the correct answer in 1 call.
I would never do this in the way you implemented: imagine you have 500 contacts: you will be doing 500 calls to your server. Now imagine you have 100000 users with 500 contacts each
Try to customize your api call in this format. Which uses async task class.
private void phoneContact() {
new AsyncTask<String,Void,String>() {
#Override
protected String doInBackground(String ... params) {
try {
Platform http = Url_Contacts;
JSONObject resp = http.search(what,where);
Log.d(TAG, "Response: " + resp.toString());
} catch (Exception e) {
Log.e(TAG, e.getMessage());
}
return "";
}
}.execute();
}
Make sure that your service works well and the format of json with value status in there.
In onResponse, run on UIThread to update your View with the availableUsers.
The enqueue method is asynchronous. So your code should respect the multithreaded nature of it.
There are many approaches you can take:
Replace enqueue() method with execute(). But that makes all the calls synchronous. If you call it in UI Thread then whole app can stutter. Probably you will get NetworkOnMainThreadException. Not a good approach anyway.
Use RxAndroid or RxJava with Observer pattern.
Simple solution. Create a variable int callsFinished = 0;. In onResponse increment that variable. Then if that callsFinished == contactsString.length that means all calls have been done.
In your activity add a listener
void onAllCallsFinished(int availableUsers) {
//do what you want with availableUsers information
}
Call onAllCallsFinished(availableUsers) when callsFinished == contactsString.length.
There you can do what you want with that data. Update a view, call another service.
I'm trying to implement Server-sent events in the Play 2 framework (version 2.3.9) using Java. I'd like for an event to be sent to the client every time a "Message" entity is saved to the database. The entity should be sent to the client in Json format.
Message message = new Message();
//some code to populate bean here
message.save(); //save to db
//What do I do with message here?
I was thinking of making a service class that will send events.
public class SSEService {
public static void sendEvent(String data, String id, String name){
EventSource eventSource = new EventSource() {
#Override
public void onConnected() {
//no idea what to do here
}
};
EventSource.Event event = new EventSource.Event(data, id, name);
eventSource.send(event);
}
}
I would then call SSEService.sendEvent() after saving the message. Am I on the right track? What does data, id, and name correspond to in the Event constructor?
Can someone provide a good example in Java 7?
Every time my mobile app needs to access the DB, I have to write this piece of code.
//NetworkManager.getInstance().start();
ConnectionRequest request = new ConnectionRequest();
request.setUrl(WebConstants.HOST+"URL");
request.setPost(false);
request.addArgument("x",y);
request.addResponseListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
NetworkEvent event = (NetworkEvent)evt;
Exception error = event.getError();
if(error != null) {
return;
}
ConnectionRequest request = event.getConnectionRequest();
byte[] result = request.getResponseData();
String response = new String(result);
/** Process the string response **/
}
});
NetworkManager.getInstance().addToQueue(request);
This becomes really tedious when the web app constantly accesses the DB and for every call I need to copy, paste and modify same code. I tried to create a method and return the string response, but since the web service is done asynchronously, the method that calls the web service has to finish before web service method process the response. Therefore, I can't simplified this part of code. Any ideas on how to proceed?
Encapsulate the code in
/** Process the string response **/
into a Callable, then you can create a generic method that calls
callable.call()
You can use addToQueueAndWait(request) which blocks until the network operation completes and is completely safe to use on the EDT since it uses invoke and block.
You can also use something like the webservice wizard which encapsulates web calls in method calls.