Firebase runTransaction not working - MutableData is null
You'll want to follow the pattern used in the Firebase documentation for handling transactions and handle the case where there's no current value your transaction callback:
public Transaction.Result doTransaction(MutableData currentData) {
long value = 0;
if(currentData.getValue() != null) {
String numQuestions = (String) currentData.getValue();
value = Long.parseLong(numQuestions, 16);
}
value++;
String incHex = Long.toHexString(value);
currentData.setValue(incHex);
return Transaction.success(currentData);
}
The reason you need to do this is that Firebase may (and often will) execute your transaction callback multiple times and you need to cater for that.
- When you first call
runTransaction()
the Firebase client will immediately invoke yourdoTransaction()
callback with its current guess for the current data. Quite often this will benull
. - Your code returns the next value based on the current value. In the case above, if the current value was
null
the new value will be1
. - The Firebase client then sends both the assumed current value and the new value to the server.
- If the actual stored value is the same as the assumed current value, the Firebase server writes the new value you specified.
- If the actual stored values is different from the assumed current value, the Firebase server rejects the new value and sends the actual current value to the client.
- At this stage the client goes back to step 1, with the now updated assumed current value.
If this does not explain the behavior you're seeing, you might want to check what values are passed into onComplete()
.
Firebase Transaction MutableData showing Nil
This is the expect behavior. When you start a transaction the SDK immediately calls your transaction handler with its best guess of the current value of the node. Most often that guess will be nil
, as the client has no better knowledge yet.
So you will have to not abort the transaction, but instead set another value on the mutable data, for example 0
. It doesn't really matter much what that value is, as the transaction will be retried anyway.
For more background on this, see:
- Firebase realtime database transaction handler gets called twice most of the time, with an ASCII chart of the flow
- Firebase runTransaction not working - MutableData is null
- how to handle null values on firebase transaction api
Avoid obtaining null currentData at first reading in a Firebase transaction
On the first invocation of doTransaction
the currentData
will likely be empty. This is not related to the existence of data in the database, but on knowledge of that data in the client. It being empty is fully expected, and your code should handle it.
So if the existing data would not exist, what should the transaction do? Even just leaving it empty is fine, as long as you do something valid it will auto-correct on the next try (or one of the ones after that).
Also see:
- Firebase runTransaction not working - MutableData is null
- how to handle null values on firebase transaction api
ValueEventListener returning a snapshot with null value for a brief second after .runTransaction()
firebaser here
Getting null
as the current value in your transaction handler initially is the expected behavior. The transaction handler is called with the current guess of the client for the value, which often will be null. If the initial guess is incorrect, your transaction handler will be called again with an updated guess for the current value. See a.o. https://stackoverflow.com/a/57134276/209103
The solution is to return a value if you get null
, even if you know that value will never actually be written to the database:
if (anObject != null) {
...
}
else {
mutableData.setValue("This is needed to get the correct flow"); // br>}
If you don't want Firebase to fire local events for the transaction values right away, but only once the transaction is confirmed, you can pass a boolean second argument to runTransaction
to indicate this:
https://firebase.google.com/docs/reference/android/com/google/firebase/database/DatabaseReference#runTransaction(com.google.firebase.database.Transaction.Handler,%20boolean)
Firebase Transaction: When data is downloaded and why every ValueEventListener get called with null when a transation is running
When you execute this code:
FirebaseDatabase.getInstance().getReference()
.child("...")
.child("building")
.runTransaction
Firebase will read all data under the .../building
path. For this reason it is recommended to run transactions as low in the JSON tree as possible. If this is not an option for you, consider running the transaction in something like Cloud Functions (maybe even with maxInstances
set to 1
) to reduce the contention on the transaction.
To you second question: this is the expected behavior. Your transaction handler is immediately called with the client's best guess to the current value of the node, which in most cases will be null
. While this may never be possible in your use-case, you will still have to handle the null
by returning a value.
For a longer explanation of this, see:
- Firebase realtime database transaction handler gets called twice most of the time
- Firebase runTransaction not working - MutableData is null
- Strange behaviour of firebase transaction
Android Java Firebase runTransaction deletes values when classes are not exactly the same
A transaction must provide the complete new value for the location that you run it on. You can't run a transaction on API.ref.databaseSpecificUser(id)
and then only provide a value for a single property under it.
You have a few options in your scenario:
Don't use the (incomplete)
User
class for callingsetValue()
, but instead only update the specific property directly on themutableData
. So that'd be something likemutableData.child("count").setValue(42)
. This way themutableData
will still contain all properties, even when theUser
class doesn't.Run the transaction directly on the property that you want to modify, so:
API.ref.databaseSpecificUser(id).child("count").runTransaction(...
. That way themutableData
will only be for the property that you're modifying, so nothing else will be deleted. Here too, you won't be using yourUser
class, as you're reading/writing a single property.Use the newer
ServerValue.increment(1)
operation to increment the count. This operation is much more efficient, and typically leads to simpler code. Something like:ServerValue.increment(1).child("count").setValue(ServerValue.increment(1));
Related Topics
Android Listview Not Refreshing After Notifydatasetchanged
How to Execute Async Task Repeatedly After Fixed Time Intervals
How Does One Use Glide to Download an Image into a Bitmap
How to Capture the Android Device Screen Content
How to Open a Second Activity on Click of Button in Android App
How to Set Layout_Weight Attribute Dynamically from Code
Android: Taking Complete Control of Phone(Kiosk Mode), Is It Possible? How
Calling Activity Class Method from Service Class
Android Canvas: Drawing Too Large Bitmap
Android Inject_Events Permission
Finish All Activities at a Time
How to Start Service Using Alarm Manager in Android
Android How to Work with Asynctasks Progressdialog