Cleanest way to dispose of Single subscriptions - java

I'm using RxHttpClient to make requests and return Single using .firstOrError(). I then subscribe to it to get the result. In order to dispose of that subscription, I used a CompositeDisposable like in the example below.
Is there some other way to do this that doesn't require so much boilerplate? Do I need to do this at all in the current situation?
This code is in an API that needs to make a request to another API to validate some data.
single = httpClient.retrieve(HttpRequest.POST("/endpoint", request), ResultDto.class)
.firstOrError()
.subscribeOn(Schedulers.io())
CompositeDisposable cd = new CompositeDisposable();
Disposable d = single.subscribe(result -> {
// ...
cd.dispose();
}, error -> {
cd.dispose();
});
cd.add(d);

Pretty much all of this is necessary, assuming you don't actually create CompositeDisposable like that and then throw it away.
You can save cd.add(d); and perhaps Disposable d= with RxJava 3 if you supply cd as the third parameter to subscribe.

Related

Drawing markers after mapbox map loads in Android Studio [Java]

It's my first project in Android Studio, basically I'm trying to develop a map with multiple markers using Mapbox. So, my problem is when loading the markers on the map it takes a lot of time to load ~3-5 seconds and the app freezes until i get the json from my API call.
Here is my retrofit2 call to API:
private void getNearbyStations() {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("***")//my API, not relevant
.addConverterFactory(GsonConverterFactory.create())
.build();
jsonPlaceHolderApi = retrofit.create(JsonPlaceHolderApi.class);
Utilizator utilizator = Utilizator.getUtilizatorInstance();
Call<ResponseNearbyStations> call = jsonPlaceHolderApi.getNearbyStations(utilizator.getAuthentificationKey(), 47.1744354, 27.5746688);//Static Lat and Long for test, in future will use current location
try {
ResponseNearbyStations body = call.execute().body();
JsonObject jsonObject = body.getData();
JsonArray ja_data = jsonObject.getAsJsonArray("stationAround");
Station[] statiiPrimite = gson.fromJson(ja_data, Station[].class);
stationList = new ArrayList<>(Arrays.asList(statiiPrimite));
} catch (IOException e) {
e.printStackTrace();
}
}
I'm saving all my Stations in an ArrayList called stationList. In Station class i have the lat and long coordinates besides other information.
Here is my addMarkers function:
private void addMarkers(#NonNull Style loadedMapStyle) {
List<Feature> features = new ArrayList<>();
for(Station statie:stationList){
features.add(Feature.fromGeometry(Point.fromLngLat(Double.valueOf(statie.getCoordinates().getLongitude()),
Double.valueOf(statie.getCoordinates().getLatitude()))));
}
loadedMapStyle.addSource(new GeoJsonSource(MARKER_SOURCE, FeatureCollection.fromFeatures(features)));
loadedMapStyle.addLayer(new SymbolLayer(MARKER_STYLE_LAYER, MARKER_SOURCE)
.withProperties(
PropertyFactory.iconAllowOverlap(true),
PropertyFactory.iconIgnorePlacement(true),
PropertyFactory.iconImage(MARKER_IMAGE),
PropertyFactory.iconOffset(new Float[]{0f, -52f})
));
}
So after several searching i find out that the "problem" here is that i'm using call.execute() in getNearbyStations() which is not async so the main thread is waiting for the Stations to load. I tried to use call.enqueue but after that i got another problem, in my function addMarkers i get NullPointerException because stationList doesn't have enough time to load in
for(Station statie:stationList){
features.add(Feature.fromGeometry(Point.fromLngLat(Double.valueOf(statie.getCoordinates().getLongitude()),
Double.valueOf(statie.getCoordinates().getLatitude()))));
}
I'm guessing that i have to use some sort of Threading to solve this problem, but i'm a beginner in using Threads for Android Studio, and i couldn't figure it out.
I think the solution would be:
1.Display the map empty
2.Add markers after they load.
In this way the user doesn't experience any freezing. Any idea how to solve this problem is welcome.
Since the problem is that you want the application not to wait of the synchronous function, I would recommend to use an asynchronous task for that. You can then execute the addMarkers function once the onPostExecute callback of the async task is invoked. But make sure to run the addMarkers only after the style has been set in your onMapReady
Please see this documentation on how to use an asynchronous task: https://developer.android.com/reference/android/os/AsyncTask
The nice side effect you get by using an aysnchronous task, is that Android will execute it in a different thread and thereby will take load off the main thread.

Server sent events being multiplicated when incoming to React

I have a simple Java Spring application that uses server sent events to send data to a frontend React app. Here is the how the backend is written:
#GetMapping(value = "/plot", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux get() {
return Flux.interval(Duration.ofMillis(1000))
.map(interval ->
currentStateMapper.map(eventRepository.getAll()).stream()
.map(plotDataMapper::map)
.collect(Collectors.toList())
);
}
What this does is probably less important but the idea is that I store recent events and the repository feeds me all of the events stored (they are stored in an infinispan cache), and then i use some mappers to get the current state and then to map it to objects suitable for visualizing with a XY plot.
It should send a message with the plotting data to the frontend every second. And when I open the endpoint with my browser (Chrome) it works like a charm. can see a new JSON appearing every second. But when I use React's event source to receive the messages instead of 1 message each second I get 6 identical messages each second. Here is the frontend implementation:
const Component = () => {
const plotDataEventSource = new EventSource(
"http://localhost:8080/tags/plot/antenna"
);
useEffect(() => {
plotDataEventSource.onmessage = e => {
console.error(e)
setData(prevState => {
/* some data handling */
return newState;
});
};
});
return ( /* html with plot */ );
}
And so the console.error() gets logged 6 times each second, identical objects. What can be the reason of this behavior?
useEffect will trigger the callback on each render - if you want it to behave like componentDidMount you should pass an empty array as the second argument.
The second argument is an array of values (usually props):
If any of the value in the array changes, the callback will be fired
after every render.
When it's not present, the callback will always be fired after every
render.
When it's an empty list, the callback will only be fired once,
similar to componentDidMount.
It is missing some important parts of your code, but the re-render may be happening because you didn't inform useEffect under what conditions it should run, and it can be achieved by a second parameter in the useEffect method.
The way you wrote it, useEffect is triggered every time your component renders. If you make a small change, passing a condition to useEffect it will run only when that condition change. For instance:
useEffect(() => {
plotDataEventSource.onmessage = e => {
console.error(e)
setData(prevState => {
/* some data handling */
return newState;
});
};
}, [data]); // <---- Assuming you are changing something called data.
When your component is rendering, useEffect will compare the value of data and will run only if it has changed.

How to correctly pass data in the Finite State machine in AKKA

I am following this example here from the doc
Here is part of the finite state machine I'm working with
startWith(ACCEPTED, new myData());
when(ACCEPTED, matchEvent(someMesage.class, MyData.class,
(someMessage, myData) -> goTo(EVALUATING).replying(EVALUATING)));
onTransition(matchState(ACCEPTED,EVALUATING, () -> {
// Here I want to update the nextState data and pass it to another actor
// But the nextState data is always the unititalized object which is new Mydata() when the FSM initializes
}));
whenUnhandled(matchAnyEvent(
(state, data) -> stay().replying("received unhandled request " + state.toString())));
initialize();
}
How do I correctly pass data between various states in the state machine?
How should the actor.tell call look like for the actor sending a message to this FSM actor
If I send the following message
MyFSM.tell(new someMessage(myData), getSelf());
It correctly matches the event and the actor changes the state to EVALUATING and sends back an EVALUATING message. BUt what I really want is, modify 'myData' based on this state change and on transition, send this modified data to another actor.
But when I send a message of type someMessage I have no way to send the existing instance of myData and it is always uninitialized as part of the initialization of the state machine.
In other words, I am trying to manage the state of myData with the finite state machine.
How can I achieve his making the best use of the framework?
A working example from the above information will be really useful!
You can use using to provide a new state, like so:
when(ACCEPTED, matchEvent(someMesage.class, MyData.class,
(someMessage, oldData) -> {
MyData newState = new MyData(); // or transform the old into a new one
return goTo(EVALUATING).using(newState).replying(EVALUATING);
}));

Use rx java to load reference data but blocking if necessary

I am trying to implement a solution for storing reference data in the database of my app.
The data is initially stored as JSON files, which I will need to sync from a server on each launch. I have a local copy of the files baked into the app. Each launch I have to check shared preferences for a version. And if it not present, I assume it is the first launch. So i need to read in the files, write the files to the database and fire on completed when that is done. The first screen expects this data to be in the database, so I will be not showing the UI for that screen in this scenario, until the process completes.
However in the future the network call to sync these files can happen asynchronously so want to be able to fire on completed on my observable as soon as i see the shared prefs have a version number and then ill kick of the update completely asynchronously
How can i set up a stream to represent this. I think the stream type will probably be void and i will just fire onCompleted/error as the subscriber doesnt care about the data, only what the process is complete
You could do something like this:
updateChecker.hasUpdates()
.flatMap(hasUpdates -> {
if (hasUpdates) {
return dataUpdater.update();
}
return Observable.just(false);
})
Assuming that
class UpdateChecker {
public Observable<Boolean> hasUpdates() {
return Observable.just(true); // Replace by API call
}
}
class DataUpdater {
public Observable<Boolean> update() {
// update the database here
return Observable.just(true);
}
}

GreenDao how to implement callbacks

I have a trouble, i need to get event/callback when i try to write to database.
I added greenDao lib to project, and i able to write/delete in db.
But no idea how to get callback after some operation under db.
In introduction to lib i read "AsyncOperationListener for asynchronous callback when operations complete".
Used this tutorial:
http://blog.surecase.eu/using-greendao-with-android-studio-ide/
Can anybody help me with this trouble?
UPD:
ok here we added some list in storage
getMyObjectDao().getSession().startAsyncSession().insertOrReplaceInTx(MyObject.class, list);
error here
List<MyObject> items = getBoxDao(c).getSession().startAsyncSession().loadAll(MyObject.class);
How can we asynchronously load data from db?
Is this correct solution?
#Override
public void onAsyncOperationCompleted(AsyncOperation operation) {
String operationIs = null;
switch (operation.getType()) {
case LoadAll:
itemsList = BoxRepository.getAllBoxes(getApplicationContext());
By default all the operations are performed synchronously, eliminating the need to get any callback. But the recent version of GreenDAO introduces AsyncSession, which can be used to perform operations asynchronously and also provides a way set listener on it. See the example below:
AsyncSession asyncSession = App.getInstance().daoSession.startAsyncSession();
asyncSession.setListener( new AsyncOperationListener() {
#Override
public void onAsyncOperationCompleted(AsyncOperation operation) {
// do whats needed
}
});
asyncSession.insert(MyObject);
Simple ask if anything unclear!

Categories