I want to achieve something like this:
MyCall<MyResponse> call = service.login(loginRequest);
call.enqueue(
new Runnable() {
#Override
public void run() {
// onResponse
}
}, new Runnable() {
#Override
public void run() {
// onFailure
}
});
so that when I have to make a call I don't need to parse the response in each callback and the code is a little lighter and reusable.
I tried the code from this snippet but I get the error:
Unable to create call adapter for XXXX.
What am I missing?
I did not extend Call but I extend Callback, it's also help to make the code is a little lighter and reusable.
public abstract class CustomCallback<T> implements Callback<T> {
#Override
public void onResponse(final Response<T> response, Retrofit retrofit) {
Runnable runnable = new Runnable() {
#Override
public void run() {
onRequestSuccess(response);
}
};
runnable.run();
}
#Override
public void onFailure(final Throwable t) {
Runnable runnable = new Runnable() {
#Override
public void run() {
onRequestFail(t);
}
};
runnable.run();
}
public abstract void onRequestSuccess(Response<T> response);
public abstract void onRequestFail(Throwable t);
}
Then when you call enqueue():
call.enqueue(new CustomCallback<YourObject>() {
#Override
public void onRequestSuccess(final Response<YourObject> response)
{
}
#Override
public void onRequestFail(final Throwable t) {
}
});
Hope it helps
If you're referring to the snippet below Jake Wharton's post then the issue should be that you're including a call to RxJavaCallAdapterFactory.create() & your call interface is not an observable. So remove that line.
Related
I have Recycler Viewer that displays data from Fire Base db however initial List contains around 4k elements. I am trying to show only first 15 elements instead of waiting for full list to be loaded however not sure how to do it.
I am trying to take(x) elements via Subscriber however it does not improve reading performance (it still waits for 4k elements from Firebase DB). How to speed up this?
Subscriber - Presenter
#Override
public void onBindViewHolder(final ListContentFragment.ViewHolder holder, int position) {
modelInterface.getDataFromFireBase("FinalSymbols")
.take(15)
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<DataSnapshot>() {
#Override
public void accept(DataSnapshot dataFromDb) throws Exception {
//update TextView inside Recycler Viewer
holder.name.setText(dataFromDb.child(String.valueOf(holder.getAdapterPosition())).child("description").getValue().toString());
holder.description.setText(dataFromDb.child(String.valueOf(holder.getAdapterPosition())).child("categoryName").getValue().toString());
}
}
);
}
Publisher - source of Data (FireBase db)
#Override
public Flowable<DataSnapshot> getDataFromFireBase(final String childName) {
return Flowable.create(new FlowableOnSubscribe<DataSnapshot>() {
#Override
public void subscribe(final FlowableEmitter<DataSnapshot> e) throws Exception {
databaseReference.child(childName).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
e.onNext(dataSnapshot);
e.onComplete();
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
}, BackpressureStrategy.BUFFER);
I believe you need to use the method limitToFirst().
Something like this:
#Override
public Flowable<DataSnapshot> getDataFromFireBase(final String childName) {
return Flowable.create(new FlowableOnSubscribe<DataSnapshot>() {
#Override
public void subscribe(final FlowableEmitter<DataSnapshot> e) throws Exception {
databaseReference.child(childName).limitToFirst(15).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
e.onNext(dataSnapshot);
e.onComplete();
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
}, BackpressureStrategy.BUFFER);
When I delete the first node "List" in Firebase, the second node "List 2" is automatically deleted. I think this is happening due to same key of child of both nodes. Is there any way to stop another node from being deleting?
Here's my code which I'm using to copy data from "List" to "List 2" and then delete the node "List". When I delete the one node the other also get deletd.
ref.child("List").child(cardTitle).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
ref.child("SHOP_ITEM").child("List 2).setValue(dataSnapshot.getValue());
ref.child("SHOP_ITEM").child("List").removeValue();
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
I had the same problem, so I used a work around; I simply added a cancel value to it. Something like cancel:"true".
In your case DATABASEREF1 = List1 and DATABASEREF2 = List2..
This was what worked for me. Here is the code:
DATABASEREF1.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
HashMap<String,String> hMdataSnapshot = (HashMap<String, String>) dataSnapshot.getValue();
hMdataSnapshot.put("canceled","true");
DATABASEREF2.setValue(hMdataSnapshot).addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
DATABASEREF1.removeValue().addOnCompleteListener(new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
if (task.isSuccessful()) {
//DO SOMETHING
} else {
//DO SOMETHING
}
}
});
}
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
//DO SOMETHING
}
});
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
I have a multiple buttons; When I press new button, previous(with another button) running request should be interrupted and new runs. How to realize it?
for (button : Buttons) {
button.setOnClickListener(b -> networkApi.getLongContentFromUrl(url)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<JsonElement>() {
#Override
public void onCompleted() {}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(JsonElement jsonElement) {
//do with result
}
}));
}
You can have a common SerialSubscription and assign your subscriber to it on button click. It will unsubscribe and thus cancel your previous stream:
SerialSubscription serial = new SerialSubscription();
for (Button btn : buttons) {
btn.setOnClickListener(e -> {
Subscriber<JsonElement> s = new Subscriber<JsonElement>() {
#Override
public void onCompleted() {}
#Override
public void onError(Throwable e) {}
#Override
public void onNext(JsonElement jsonElement) {
//do with result
}
};
serial.set(s);
networkApi.getLongContentFromUrl(url)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(s);
});
}
I'm using a GridPane, where I'm loading my data from a DB to the Pane, creating rows and columns dynamically. It works fine, but sometimes, if there is a lot of stuff (about 30 rows and 30 columns, and every cell has a EventListener) to load, it takes more than a second to see the loaded stuff.
So, I thought, that it will be great to add a ProgressBar or a ProgressIndicator. I tried that, but I realized that it does not depend on the loops, which are filling and creating the dynamic GridPane, but on the fact that the "loading and applying" takes a lot of time. My ProgressIndicator jumps from 0 to 100 and that's not what I want.
Do you have some ideas how to solve that problem? I read some articles about Preloaders, and I'm looking for a similar functionality, but I can't use Preloaders for that problem.
Best regards,
Edit: Now I got a solution, which works, but I don't think that someone else would write such code :S.
I created a class called LoaderDienstplan:
public class LoaderDienstplan extends Task {
//Some member variables for starting call() method
#Override
protected Object[] call() throws Exception{
// calling DB and service
updateProgress(1,1);
return; // Returning an ObjectArray with Lists,Data and a reference to my Controllerclass
}
#Override
public void run(){
final Object[] b = call();
Platform.runLater(new Runnable(){
#Override
public void run(){
//Updating GridPane
}
});
}
}
public class LoaderDienstplan extends Task {
MyInterface listener;
//Some member variables for starting call() method
#Override
protected Object[] call() throws Exception{
// calling DB and service
updateProgress(1,1);
return; // Returning an ObjectArray with Lists,Data and a reference to my Controllerclass
}
public void setListener(MyInterface listener) {
this.listener = listener;
}
#Override
public void run() {
final Object[] b = call();
Platform.runLater(new Runnable(){
#Override
public void run() {
//Updating GridPane
listener.updateGUI(something);
}
});
}
}
And the interface:
public interface MyInterface {
void updateGUI(Something something);
void loadingFinished();
}
And from your Controller class, must implements MyInterface:
LoaderDienstplan load = new LoaderDienstplan(...);
load.setListener(this);
Make this class:
public class Loader implements Runnable {
private final MyController listener;
private final List<File> stuff;
public DoubleProperty progressProperty;
public Loader(MyController listener, List<File> stuffToLoad) {
this.stuff = stuffToLoad;
progressProperty = new SimpleDoubleProperty();
this.listener = listener;
}
private void updateProgress(double value, double max) {
progressProperty.set(value / max);
}
public DoubleProperty progressProperty() {
return progressProperty;
}
#Override
public void run() {
Platform.runLater(new Runnable() {
public void run() {
//load the stuffs
}
});
//each time you load another stuff, do:
updateProgress(index, totalAmountOfStuffs);
//when finished, do:
listener.finished();
}
}
Put a veil and an indicator around the GridPane, like this (i made the veil (Region) and indicator (ProgressIndicator) from the JavaFX SceneBuilder):
//lets say your progress indicator is called progressIndicator and the shaded `Region` is called veil
progressIndicator.setMaxSize(150, 150);
veil.setStyle("-fx-background-color: rgba(0, 0, 0, 0.4)");
And run the Loader class from another thread like this:
private void startLoading() {
progressIndicator.setVisible(true);
veil.setVisible(true);
Loader loader = new Loader(this, stuffToLoad);
progressIndicator.progressProperty().bind(loader.progressProperty());
new Thread(loader).start();
}
#Override
public void finished() {
progressIndicator.setVisible(false);
veil.setVisible(false);
}
What it looks like for me:
I have a static BorderPane with ContextMenu insight Task
Task task = new Task()
{
#Override
protected Void call() throws Exception
{
Platform.runLater(new Runnable()
{
#Override
public void run()
{
try
{
contextMenu = new ContextMenu();
MenuItem item1 = new MenuItem("About");
item1.setOnAction(new EventHandler<ActionEvent>()
{
#Override
public void handle(ActionEvent e)
{
System.out.println("About");
}
});
MenuItem item2 = new MenuItem("Preferences");
item2.setOnAction(new EventHandler<ActionEvent>()
{
#Override
public void handle(ActionEvent e)
{
System.out.println("Preferences");
}
});
MenuItem item3 = new MenuItem("Close");
item3.setOnAction(new EventHandler<ActionEvent>()
{
#Override
public void handle(ActionEvent e)
{
}
});
contextMenu.getItems().addAll(item1, item2, item3);
bp.setOnContextMenuRequested(new EventHandler<ContextMenuEvent>()
{
#Override
public void handle(ContextMenuEvent event)
{
contextMenu.show(bp, event.getScreenX(), event.getScreenY());
event.consume();
}
});
bp.addEventHandler(MouseEvent.MOUSE_PRESSED, new EventHandler<MouseEvent>()
{
#Override
public void handle(MouseEvent event)
{
contextMenu.hide();
}
});
}
catch (Exception ex)
{
ex.printStackTrace();
}
finally
{
}
}
});
return null;
}
};
new Thread(task).start();
I noticed that when I close the component which holds the BorderPane the Java Threads are not disposed they are still initialized into the memory. I'm not sure is this caused by the static BorderPane. After the Task is completed the Java Thread should be disposed. Any idea why is this happening?
The problem is not a Task, but the anonymous classes in your Runnable.
In the next piece of code:
bp.setOnContextMenuRequested(new EventHandler<ContextMenuEvent>()
{
#Override
public void handle(ContextMenuEvent event) {
//...
}
});
you introduce an anonymous class extending EventHandler which holds inner link to a Runnable. To solve that you can use nested static class instead.
P.S.: Unfortunately you can't make anonymous class static in Java, see Is it possible to make anonymous inner classes in Java static?