I'm migrating from the old Android Places API to either the new one or the compatibility library, in both approaches the auto prediction search works, but getting more details from the ID of the selected location appears to never complete.
I started with the compatibility library, the initial autoPrediction lookup works as expected. Suggesting the API key and account are fine. But getPlaceByID failed to finish.
I've switched to the new API instead, again, the new FindAutocompletePredictions works, but the fetchPlaces task never finishes.
I've boiled the code down to manually putting an ID in, only asking for LatLong, and having all the available listeners with breakpoints. They are never hit.
List<Place.Field> placeFields = Arrays.asList(Place.Field.LAT_LNG);
FetchPlaceRequest request = FetchPlaceRequest.builder("EhtHbGFzZ293IFN0cmVldCwgR2xhc2dvdywgVUsiLiosChQKEgmvXKElzUWISBFN3LArF1aEERIUChIJqZHHQhE7WgIReiWIMkOg-MQ", placeFields)
.build();
placesClient.fetchPlace(request).addOnCompleteListener(new OnCompleteListener<FetchPlaceResponse>() {
#Override
public void onComplete(#NonNull Task<FetchPlaceResponse> task) {
System.out.println("");
}
}).addOnCanceledListener(new OnCanceledListener() {
#Override
public void onCanceled() {
System.out.println("");
}
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
System.out.println("");
}
}).addOnSuccessListener(new OnSuccessListener<FetchPlaceResponse>() {
#Override
public void onSuccess(FetchPlaceResponse fetchPlaceResponse) {
System.out.println("");
}
});
I would expect to hit one of the listeners and see a place, or some reason for it to fail. Or, at least something in the logcat to say what's going on.
Use
public Task<TResult> addOnCompleteListener(#NonNull OnCompleteListener<TResult> var1)
Related
addOnFailureListner does not work for add() data, but addOnFailureListner works for get().
This is not working
WorkPlaceRef.add(DATA).addOnCompleteListener(new OnCompleteListener<DocumentReference>() {
#Override
public void onComplete(#NonNull Task<DocumentReference> task) {
//Successfully created - This one triggers when I turn on wifi again.
}
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
// Error - This addonFailureListner is not working when there are no network.
}
});
This is working
WorkPlaceRef.get().addOnCompleteListener(new OnCompleteListener<DocumentReference>() {
#Override
public void onComplete(#NonNull Task<DocumentReference> task) {
//Successfully received - This one triggers when I turn on wifi again.
}
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
// Error - this addonFialureListner triggers when there is no network.
}
});
It's not a failure to attempt to write data while offline. The Firestore SDK will write the data in a local offline cache first, and eventually synchronize that write with the server when the app comes back online. The success listener will be invoked whenever that happens.
Write failures only happen when there is some problem that can't be retried due to lack of connectivity, such as security rule violations, or exceeding some documented limit of the database.
If you want to know if some document data is not yet synchronized with the server, you can check its metadata to know if a write is pending.
#Override
public void startClient(final Callback callback) {
SmsRetrieverClient client = SmsRetriever.getClient(context);
client.startSmsRetriever();
Task<Void> task = client.startSmsRetriever();
task.addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
callback.onSuccess();
}
});
task.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
callback.onFail(e);
}
});
}
The code above is the suggested way Google encourages to use their SMS Reytriever API. This method is meant to start a client before the BroadcastReceiver looks for incoming sms messages. The problem here is that onSuccess and onFailure are never called, none of them, and only happens with a Android emulators. I put some breakpoints and logs to confirmed this, the client never notifies back what happened.
This is not a hash problem since this is only related to the initialization of the SmsRetrieverClient.
I'm really confused and don't know what's happening. To never notify a listener is a behaviour nobody would expect, I'm even thinking that this problem might be related to other factors since I recenlty formatted my computer and re-installed latest Android Studio, because before that this code was working on both emulators and physical devices.
Try removing the redundant client.startSmsRetriever(); in the second line.
Make sure the play services version on your emulator/device is > 10.2.0
You can check the play services version using -
private static final String MIN_SUPPORTED_PLAY_SERVICES_VERSION = "10.2";
public static boolean isSmsRetrieverApiAvailable(Context context) {
if (!isPlayServicesAvailable(context)) {
return false;
}
try {
String playServicesVersionName = context.getPackageManager().getPackageInfo(GoogleApiAvailability.GOOGLE_PLAY_SERVICES_PACKAGE, 0).versionName; // should be >10.2.0
return playServicesVersionName.compareTo(MIN_SUPPORTED_PLAY_SERVICES_VERSION) > 0;
} catch (PackageManager.NameNotFoundException e) {
return false;
}
}
private static boolean isPlayServicesAvailable(Context context) {
GoogleApiAvailability googleApiAvailability = GoogleApiAvailability.getInstance();
int resultCode = googleApiAvailability.isGooglePlayServicesAvailable(context);
return resultCode == ConnectionResult.SUCCESS;
I am trying firestore database on Android.
This is my code that inserts a document:
public Observable<Post> createPost(final Post post){
return Observable.create(new Observable.OnSubscribe<Post>() {
#Override
public void call(final Subscriber<? super Post> subscriber) {
try{
DocumentReference documentReference = getCollection().document();
post.setId(documentReference.getId());
documentReference.set(post).addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
subscriber.onNext(post);
subscriber.onCompleted();
}
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
subscriber.onError(e);
subscriber.onCompleted();
}
});
}catch (Exception ex){
subscriber.onError(ex);
Log.e(LOG_TAG, ex.getMessage(), ex);
}
}
});
}
The document gets inserted into the database but neither of the onSuccess nor the onFailure callbacks are invoked.
Update 1
The issue is not consistent sometimes it works, sometimes the callbacks are invoked after an hour, sometimes after 3 hours etc..
This is happening when there is no internet connection.
Update 2
The issue was reported here and it is closed. I am not sure how to guarantee the correctness of data created offline.
There does not seem to be anything wrong with your code, try perhaps the onCompleteListener callback. So add
.addOnCompleteListener((Task<Void> task) -> {
if(task.getException() != null) {
emitter.onError(task.getException());
}
if(task.isComplete()) { //try task.isSuccessful() if this is what you are looking for
emitter.onComplete();
}
});
If this does not solve your issue perhaps, use an emitter like so:
Completable completable$ = Completable.create((CompletableEmitter emitter) -> {
firebaseFirestore.collection(collection).document(document)
.delete()
.addOnSuccessListener((Void aVoid) -> emitter.onComplete())
.addOnFailureListener((Exception e) -> emitter.onError(e))
.addOnCompleteListener((Task<Void> task) -> {
if(task.getException() != null) {
emitter.onError(task.getException());
}
if(task.isComplete()) { //try task.isSuccessful()
emitter.onComplete();
}
});
});
return completable$;
Okay so I did a simple version of your question but instead of adding a post, it adds a User. The concept is the same.
Here is the method to add a user. It returns an Observable<DocumentReference> just to reference where the user was added.
public Observable<DocumentReference> insertToFirebaseFirestore$() {
UserEntity userEntity = new UserEntity();
userEntity.setEmail("myemail#myemail.com");
userEntity.setBio("I'm a cool cat!");
userEntity.setDisplayName("KoolKat!");
//Notice here I am using an ObservableEmitter instead of Subscriber like you did
return Observable.create((ObservableEmitter<DocumentReference> emitter) -> {
this.firebaseFirestore.collection("tempUsers")
.add(userEntity)
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
emitter.onError(e);
}
})
.addOnSuccessListener(new OnSuccessListener<DocumentReference>() {
#Override
public void onSuccess(DocumentReference documentReference) {
//this gets triggered when I run
emitter.onNext(documentReference);
}
})
.addOnCompleteListener(new OnCompleteListener<DocumentReference>() {
#Override
public void onComplete(#NonNull Task<DocumentReference> task) {
//this also gets triggered when I run
emitter.onNext(task.getResult());
}
});
});
}
When I run this, and place breakpoints inside onSuccess and onComplete. Both of them are triggered and I can see the output.
I call the method from the Activity as follows.
...onCreate method
insertToFirebaseFirestore$()
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.io()) //observe on io thread cause I don't need it to updateUI.
.subscribe((DocumentReference val) ->{
Log.e("USERACTIVITY", "You have uploaded " + val.getId());
});
The LogcatPrints
12-13 09:47:47.942 15007-15059/com.example.debug E/USERACTIVITY: You have uploaded sFBsF4ZmwGaDdxCEKuF6
12-13 09:47:57.563 15007-15059/com.example.debug E/USERACTIVITY: You have uploaded sFBsF4ZmwGaDdxCEKuF6.
From what I have see with yours, perhaps use an emitter within
your Observable.create.
If that doesn't work try doing the firestore call without wrapping
it in an observable
If all else, might be a connection issue, since you say it happens
intermittently
I came across this with react native.
For inserts the key is to create a new document.
example:
const userRef = firebase.firestore()
.collection("users")
.doc();
userRef.set({name: someName});
This will create the document offline and sync when you come back online.
Further calls such as this will work offline
userRef.collection("Locations").add({location: "Austin,TX"});
When using the following pattern to synchronously get data from Firebase Realtime Database:
String s = Single.create(new SingleOnSubscribe<String>() {
#Override
public void subscribe(SingleEmitter<String> e) throws Exception {
FirebaseDatabase.getInstance().getReference("path").orderByChild("child").equalTo("xyz").addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
e.onSuccess("Got it");
}
#Override
public void onCancelled(DatabaseError databaseError) {
e.onError(databaseError.toException());
}
});
}
}).blockingGet();
It will hang and create an ANR error. If I use the same Firebase "innards" outside of the Single, it fires just fine. The Single without the Firebase code inside also will fire, so it seems there is some incompatibility between the two.
Any ideas?
Firebase delivers events on ui thread, waiting for result with blockingGet deadlocks it. In my opinion you should rethink app logic and subscribe without blocking with subscribe(SingleObserver)
Since you are creating your own Single, You should use DisposableSingleObserver in subscribeWith. Secondly, you shouldn't be calling blockingGet() like that. The reason is by default the Single or any observable/Processor/Flowable you create will be subscribed (run its operations on main thread) and observe on main thread. BlockingGet() causes the mainThread to pause. It's like executing Thread.sleep() on Main Thread. This always ends in a disaster.
The best option for you would be to rethink the logic you are trying to put in to the code. Since the Firebase operations are Async by nature, you should adapt your code to async pattern.
Anyways you can do something like the following to achieve what seems likes you might be trying to do. Note that I wrote the following code here so it might have syntactical errors.
Single.create(new SingleOnSubscribe<String>() {
// your firebase code
#Override
public void subscribe(SingleEmitter<String> e) throws Exception {
FirebaseDatabase.getInstance().getReference("path").orderByChild("child").equalTo("xyz").addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
e.onSuccess("My String");
}
#Override
public void onCancelled(DatabaseError databaseError) {
e.onError(databaseError.toException());
}
});
}
})
.subscribeOn(Schedular.io())
.observeOn(AndroidThread.mainThread()) // if you aren't doing intensive/long running tasks on the data you got from firebase
.subscribeWith(new DisposableSingleObserver<String>() {
public void onSuccess(String myString) {
mMyString = myString;
}
public void onError(Throwable t) {
Timber.e("error in fetching data from firebase: %s", t);
}
});
I'm totally new to Firebase and need to know how to check if my writing task was successful because if I don't, the MainActivity starts and messes up my Register progress.
This checks if Username is already taken and registers the user if it isn't:
Query usernamequery = myRef.orderByChild("Username").equalTo(Username);
usernamequery.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
if (snapshot.exists()) {
// TODO: handle the case where the data already exists
editText.setError("Username taken!");
return;
}
else {
// TODO: handle the case where the data does not yet exist
myRef.child("Users").child(Username).child("Userid").setValue(user.getUid());
myRef.child("Users").child(Username).child("Username").setValue(Username);
startActivity(maps);
finish();
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
Toast.makeText(Username.this, "Error", Toast.LENGTH_LONG).show();
}
});
But I want the Intent to Main Activity (maps) only be fired when
myRef.child("Users").child(Username).child("Userid").setValue(user.getUid());
and the other one is finished with its task and is successful.
What can I do?
To know when a write operation has completed on the server, add a completion listener:
myRef.child("Users").child(Username).child("Userid").setValue(user.getUid(), new DatabaseReference.CompletionListener() {
void onComplete(DatabaseError error, DatabaseReference ref) {
System.err.println("Value was set. Error = "+error);
// Or: throw error.toException();
}
});
If there was an error, details will be in the error operation. If the write operation was completed without problems, the error will be null.
If you want to write to multiple locations with a single operation, you'll want to look at the update() method
The correct overload for setValue() is documented here.