Edit: 23/10/2016: This is not solved and I'm still looking for an answer. I am going to rewrite this question to make it clearer as I now know what's causing this problem.
Edit: 26/10/2016: SOMETHING FOUND: While trying to find the problem, I got a bug that helped me find something .It turns out if I have this in my Firebase database:
Campaigns{
UNQ_KEY: 1 //This is being set in the transaction
}
Rather than this:
Campaigns{
UNQ_KEY:{
count: 1 //this is being set in the transaction
}
}
The problem doesn't happen.
So, in conclusion, it's probably a recursion error.
I have this Firebase transaction:
database.runTransaction(new Transaction.Handler() {
@Override
public Transaction.Result doTransaction(MutableData mutableData) {
Long preUserIncrementedInt = Long.parseLong(b.getText().toString());
Long userIncrementedInt = ++preUserIncrementedInt;
mutableData.child("users").child(getUid()).child("count").setValue(userIncrementedInt);
Long preIncrementedTotalCount = mutableData.child("count").getValue(Long.class);
Long incrementedTotalCount = ++preIncrementedTotalCount;
mutableData.child("count").setValue(incrementedTotalCount);
return Transaction.success(mutableData);
}
@Override
public void onComplete(DatabaseError databaseError, boolean b, DataSnapshot dataSnapshot) {
if (databaseError != null)
Log.wtf(TAG,databaseError.getMessage());
}
});
This line:
mutableData.child("users").child(getUid()).child("count").setValue(userIncrementedInt);
and this one:
mutableData.child("count").setValue(incrementedTotalCount);
When I run the transaction, the same activity gets created again and opens. When I click the back button, it goes to the previous activity in the backstack. BUT, the previous activity in the backstack is the same activity itself. Like this:
Each time I click the button, a new activity (activity with the problem) is produced in the backstack.
To show you how it looks like, here's a GIF:
Why is this happening?
Why Stack Overflow error occurs in recursion? If the base case is not reached or not defined, then the stack overflow problem may arise. Let us take an example to understand this.
A recursive function calls itself, the memory for the called function is allocated on top of memory allocated to calling function and different copy of local variables is created for each function call.
How memory is allocated to different function calls in recursion? When any function is called from main (), the memory is allocated to it on the stack.
Examples of such problems are Towers of Hanoi (TOH), Inorder/Preorder/Postorder Tree Traversals, DFS of Graph, etc. What is base condition in recursion? In the recursive program, the solution to the base case is provided and the solution of the bigger problem is expressed in terms of smaller problems.
(I will be posting here as its easier to manage my comments)...
The solution is NOT found.
*UPDATE 4: Solution Found**
After many tries, and lots of comments, the solution was finally found, apparently the OP had the increment method in another class and moving it to the same class it was being used on solved the issue.
As of why this might happened, in my opinion, maybe it had to do with a concurrency issue on the transaction, maybe the problem was that it was creating a cycle on its own. Your activity starts, later you instantiated your FirebaseController
which, at some point, fired off the increment
method, making an async execution which ends up (failing in some way?) and that starts your activity again
See below for failed (but troubleshooting steps) attempts
I tried to debug. It wasn't activated on any of these lines. It takes me to a bunch of Android files (such as View.java). When I "Run to cursor" (skip the next debug breakpoint), it restarts.
Can you try to check whether the clicked view inside the runnable is not null ?
Runnable runnable = new Runnable() {
@Override
public void run() {
// if not null do this
clicked.setEnabled(true);
// if null print some log
// ...
}
};
Are you using Step Into or Step over when debugging (if you get deeper in the hierarchy of classes you might be using Step Into (F5), can you try debugging with Step Over (F6) )?
If we found the answer I will post it here:
UPDATE 1:
MutableData#setValue(java.lang.Object)
The documentation explains how this works:
Set the data at this location to the given value. The native types accepted by this method for the value correspond to the JSON types:
Boolean
Long
Double
Map<String, Object>
List<Object>
In addition, you can set instances of your own class into this location, provided they satisfy the following constraints:
The class must have a default constructor that takes no arguments The class must define public getters for the properties to be assigned. Properties without a public getter will be set to their default value when an instance is deserialized
Try using another datatype as suggested by fellow developers:
change the #setValue
parameter to an object of the list above and #getValue
also return an object, try to cast to the correct type, maybe you could use Integer
class
UPDATE 2:
Does your data schema look something like this?
{
"campaings": {
"key": {
"count": "1",
"users": {
"-JRHTHaIs-jNPLXOQivY": {
"count": "1",
...
},
...
}
...
},
"other-key": {
...
}
...
}
}
Thats what I infer from the pieces of code there, you can clarify me if I made a mistake.
// here we are searching for the current reference in Campaings/key
DatabaseReference database = FirebaseDatabase.getInstance().getReference().child("Campaigns").child(key);
int preIncrementUserCount = Integer.parseInt(button.getText().toString());
final int incrementedUserCount = ++preIncrementUserCount;
button.setText(String.valueOf(incrementedUserCount));
database.runTransaction(new Transaction.Handler() {
@Override
public Transaction.Result doTransaction(MutableData mutableData) {
// here we are searching for the current count value in
// Campaings/key/count
Integer currentValue = mutableData.child("count").getValue(Integer.class);
if (currentValue == null) {
// do something...
} else {
// here we are setting the count value in Campaings/key/count
mutableData.child("count").setValue(++currentValue);
}
// here we are setting the count value in Campaings/key/users/UUID/count
mutableData.child("users").child(getUid()).child("count").setValue(incrementedUserCount);
return Transaction.success(mutableData);
}
@Override
public void onComplete(DatabaseError databaseError, boolean committed, DataSnapshot dataSnapshot) {
if (databaseError != null) {
Log.e(TAG, "Error: " + databaseError.getMessage());
}
System.out.println("Transaction completed");
}
});
The point of the code example is to illustrate the data schema searches you are doing (again, correct me in any part if I made a mistake), but please put the databaseError
log on onComplete
for debugging purposes
UPDATE 3
From the documentation:
doTransaction() will be called multiple times and must be able to handle null data. Even if there is existing data in your remote database, it may not be locally cached when the transaction function is run.
Try changing this part:
if (currentValue == null) {
// do something...
} else {
// here we are setting the count value in Campaings/key/count
mutableData.child("count").setValue(++currentValue);
}
To something like:
if (mutableData.child("count").getValue() == null) {
// print something...
// Log.d(TAG,"Value at some point was null");
// or maybe (just to test)
// mutableData.child("count").setValue(1);
} else {
// here we are setting the count value in Campaings/key/count
mutableData.child("count").setValue(++currentValue);
}
P.S: I'm leaving the solutions if any person in the foreseable future might be troubleshooting something similar like this
It restarts your activity because it crashes somewhere. Try this code:
public void onClick(View v) {
if (mp == null){
mp = new MediaPlayer();
} else {
mp.reset();
}
try {
AssetFileDescriptor afd;
afd = getAssets().openFd("click.mp3");
mp.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
mp.prepare();
mp.start();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalStateException e){
} catch (IOException e){
} catch (Exception e){
}
v.setEnabled(false);
final View clicked = v;
Handler handler = new Handler();
Runnable runnable = new Runnable() {
@Override
public void run() {
clicked.setEnabled(true);
}
};
handler.postAtTime(runnable,System.currentTimeMillis()+100);
handler.postDelayed(runnable,100);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With