3

I am running a transaction on click of a layout inside my adapter, but when I click the layout, the transaction runs successfully. But it creates another item inside my RecyclerView. I tried notifydatasetChanged() inside transaction but it creates an error:

CalledFromWrongThreadException: Only the original thread that created a view 
hierarchy can touch its views.

Then I tried putting notifyDataSetChanged() outside the transaction, but it's not working. Below is what I am doing:

holder.follow.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(final View view) {
                DatabaseReference mWorkDatabase = 
                FirebaseDatabase.getInstance().getReference()
               .child("WorkforWorkMen").child(work_id).child("follower");
                mWorkDatabase.runTransaction(new Transaction.Handler() {
                    @Override
                    public Transaction.Result doTransaction(MutableData 
                     mutableData) {
                        if(holder.followText.getText().equals("Follow")) {
                            holder.followText.setText("Following");
                            FirebaseAuth mAuth = FirebaseAuth.getInstance();
                            String current_user = 
                            mAuth.getCurrentUser().getUid();
                            DatabaseReference mFollowingDatabase = 
                            FirebaseDatabase.getInstance().getReference()
                  .child("WorkFollowing").child(current_user).child(work_id);
                            final Map fol = new HashMap();
                            fol.put("follower",work_id);
                            mFollowingDatabase.setValue(fol);
                            mutableData.setValue
                  (Integer.parseInt(mutableData.getValue().toString()) + 1);
                        }

                    else {
                            holder.followText.setText("Follow");
                            FirebaseAuth mAuth = FirebaseAuth.getInstance();
                            String current_user = 
                            mAuth.getCurrentUser().getUid();
                            DatabaseReference mFollowingDatabase = 
                            FirebaseDatabase.getInstance().getReference()
                  .child("WorkFollowing").child(current_user).child(work_id);
                            mFollowingDatabase.setValue(null);
                            mutableData.setValue(Integer
                           .parseInt(mutableData.getValue().toString()) - 1);
                        }

                        return Transaction.success(mutableData);
                    }
                    @Override
                    public void onComplete(DatabaseError databaseError, 
                    boolean b, DataSnapshot dataSnapshot) {
                        Toast.makeText(context,"error on complete"+ 
                  String.valueOf(databaseError), Toast.LENGTH_SHORT).show();
                        Log.i("eror here", "onComplete: 
                  "+String.valueOf(databaseError));
                    }
                });
                new Handler(Looper.getMainLooper()).post(new Runnable() {
                    @Override
                    public void run() {
                        Toast.makeText(context, "here", 
                          Toast.LENGTH_SHORT).show();
                        notifyDataSetChanged();
                    }
                });

            }
        });

i think when i add the data to my list the change in the value of followers cause it to add a new item, i am not sure though

 private void loadMessages(){
    if(keys.size() == 0){
        messagesList.clear(); 
        Toast.makeText(getContext(), " size zero no results found", 
    Toast.LENGTH_SHORT).show();
    }
    else {
        messagesList.clear();
        for (int i = 0; i < 5; i++) {
            if (i>=keys.size()){
                Toast.makeText(getContext(), "list is less than 5 item", 
            Toast.LENGTH_SHORT).show();
            }
            else {
                listPos = i;
                String id = keys.get(i);
                mWorkDatabase.child(id).addValueEventListener(new ValueEventListener() {
                    @Override
                    public void onDataChange(DataSnapshot dataSnapshot) {
                        String messageKey = dataSnapshot.getKey();
                        NewWork message = dataSnapshot.getValue(NewWork.class);
                        itemPos++;
                        messagesList.add(message);
                        mAdapter.notifyDataSetChanged();
                        mRecyclerView2.scrollToPosition(messagesList.size() - 1);
                    }
                    @Override
                    public void onCancelled(DatabaseError databaseError) {
                    }
                });
            }
        }
    }
}
Jimmy
  • 87
  • 2
  • 9

2 Answers2

1

You are calling notifyDataSetChanged() from background thread you need to call it from ui thread as follows

new Handler(Looper.getMainLooper()).post(new Runnable() {
            @Override
            public void run() {

                notifyDataSetChanged();
            }
        });
Adeel Turk
  • 897
  • 8
  • 23
1

The Firebase database client, runs all network operations in a background thread. This means that you cannot create or modify views within that background thread. You can modify the UI only in the thread where those views were created. However, the thread in which you are allowed to make those changes is the main thread. So what you need to do is to get the following line of code out of the transaction.

notifyDataSetChanged();

Note: The entire view tree is single threaded. So, you must always be on the UI thread when calling any method on any view. If you are doing work on other threads and want to update the state of a view from that thread, you should use a Handler.

You can find much more here.

Alex Mamo
  • 130,605
  • 17
  • 163
  • 193
  • I get it what you are trying to say but I am still a bit confused cause even after using the new Handler(Looper.getMainLooper()).post(new Runnable() { @Override public void run() { notifyDataSetChanged(); } }); the item are still duplicating on my click event – Jimmy Jun 19 '18 at 12:54
  • Have you tried to get `notifyDataSetChanged();` method out of the transaction completely? Do still get this error `Only the original thread that created a view hierarchy can touch its views.`? – Alex Mamo Jun 19 '18 at 12:59
  • no i am not getting this error now but i am still getting duplicate items in my recycler view. i have placed it outside the transaction – Jimmy Jun 19 '18 at 13:07
  • That's good, it means that half of your problem is solved. Regarding the duplicate, to fix this change the logic of your code. Create a single transaction and use that if statement inside it. Don't create two separate transactions. – Alex Mamo Jun 19 '18 at 13:12
  • i changed my code above and now i am getting the error back again – Jimmy Jun 19 '18 at 13:30
  • You'll get that error every time you are trying to modify the UI in another thread other then the one that was created. Try just to use the logic inside the transaction and modify the UI outside the transaction. – Alex Mamo Jun 19 '18 at 13:34
  • but i am runnning this on one item only so the only ui that i am changing is the one that is in the list it should work on it – Jimmy Jun 19 '18 at 13:36
  • it doesnt make sense, cause the my list has one item in it only so the only UI that i am changing is the ui of the item so it should change and besides if i call notifydataset changed should notify the adapter of the changes – Jimmy Jun 19 '18 at 13:40
  • Yes, but don't change it inside the transactions. Get it outside. If this is still happening, please edit your post by adding the changed code. – Alex Mamo Jun 19 '18 at 13:42
  • it is outside my transaction it is inside my onclick listener i have changed the code please check again – Jimmy Jun 19 '18 at 13:46
  • Try to make the code using your logic, as simple as **[this](https://stackoverflow.com/questions/48307610/how-to-save-users-score-in-firebase-and-retrieve-it-in-real-time-in-android-stud)**. – Alex Mamo Jun 19 '18 at 13:53
  • i did that but its still not working, i have to do the increment inside the adapter class only. i am storing somevalues from geoquery from which i am filling my list on which my adapter is working so i have to do it inside my adapter class only. the problem i guess is that adapter is not getting notified of the changes in database. so when i click on button of my item in listview the value is increased by 1 and that is fine but as soon as the value is changed in the database it adds one more item in my list , thats the problem i am facing. – Jimmy Jun 20 '18 at 08:23
  • 1
    I think that you should post another question containing all the new stuff that you have already done so me and other users can take a closer look at it. – Alex Mamo Jun 20 '18 at 08:29
  • sorry for late reply i will do that for sure and thankx for the help – Jimmy Jun 23 '18 at 19:36