Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

recursion causing mutableData.setValue() causing the same activity to be produced in backstack?

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:

IMAGE

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:

GIF

Why is this happening?

like image 848
Ali Bdeir Avatar asked Sep 30 '16 16:09

Ali Bdeir


People also ask

Why Stack Overflow error occurs in recursion?

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.

How does a recursive function call itself?

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?

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.

What is an example of a recursive problem?

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.


2 Answers

(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

like image 74
Ivan Alburquerque Avatar answered Oct 20 '22 17:10

Ivan Alburquerque


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);

}
like image 32
Liem Vo Avatar answered Oct 20 '22 15:10

Liem Vo