0

I know this question might seem duplicated, I've read like ten other threads about this same thing, but I cannot find the problem

I have this method in my activity:

public void saveResponse(final Response studentResponse, final Content content)
    fb.getReference("...").addListenerForSingleValueEvent(new ValueEventListener() {
        @Override
        public void onDataChange(final DataSnapshot dataSnapshot) {

           new Thread(new Runnable() {
               @Override
               public void run() {
                    Map<String, Object> responseMap = new HashMap<>();
                    responseMap.put("end_date", studentResponse.end_date);
                    responseMap.put("start_date", studentResponse.start_date);
                    responseMap.put("time", studentResponse.time);
                    responseMap.put("points", studentResponse.points);
                    responseMap.put("max_points", studentResponse.max_points);
                    responseMap.put("selected_options", studentResponse.selected_options);
                    if (!TextUtils.isEmpty(studentResponse.free_text))
                        responseMap.put("free_text", studentResponse.free_text);

                    DataSnapshot contentRef = dataSnapshot.child("/sections/" + currentSection + "/sections/" + currentSubsection + "/contents/" + content.id);
                    final int oldPoints = contentRef.hasChild("points") ? contentRef.child("points").getValue(int.class) : 0;
                    contentRef.getRef().setValue(responseMap);
                    contentRef.getRef().setPriority(ServerValue.TIMESTAMP);

                    DataSnapshot subSectionRef = dataSnapshot.child("/sections/" + currentSection + "/sections/" + currentSubsection);
                    long subSectionPoints = (subSectionRef.hasChild("points") ? subSectionRef.child("points").getValue(long.class) : 0) + studentResponse.points - oldPoints;
                    subSectionRef.child("points").getRef().setValue(subSectionPoints);

                    int indexOf = currentContents.indexOf(content) + 1;
                    if(indexOf > 0 && indexOf < currentContents.size()) {
                        CourseContent content = currentContents.get(indexOf);
                        subSectionRef.child("currentPosition").getRef().setValue(content.order);
                    }

                    DataSnapshot sectionRef = dataSnapshot.child("/sections/" + currentSection);
                    long sectionPoints = (sectionRef.hasChild("points") ? sectionRef.child("points").getValue(long.class) : 0) + studentResponse.points - oldPoints;
                    sectionRef.child("points").getRef().setValue(sectionPoints);

                    long coursePoints = (dataSnapshot.hasChild("points") ? dataSnapshot.child("points").getValue(long.class) : 0) + studentResponse.points - oldPoints;
                    dataSnapshot.child("points").getRef().setValue(coursePoints);
                    dataSnapshot.getRef().setPriority(MAX_SAFE_INTEGER - coursePoints);

                    int completed = 0;
                    for (DataSnapshot sect : dataSnapshot.child("sections").getChildren()) {
                        for (DataSnapshot subSect : sect.child("sections").getChildren()) {
                            int currPos = subSect.hasChild("currentPosition") ? subSect.child("currentPosition").getValue(int.class) : 0;
                            completed += currPos;
                        }
                    }

                    double progress = totalContents > 0 ? (double) completed / (double) totalContents : 0;
                    dataSnapshot.child("progress").getRef().setValue(progress);
               }
           }.start();
        }
        ...
    });
}

in a click handler I call this method, and then I change the fragment (with custom animations).

The thing is, the fragment transition is not smooth, it freezes a little, if I comment everything inside the runnable then it runs smooth. I've tried also with an AsyncTask and the same happens.

Inside the runnable, I'm just querying the dataSnapshot and its children, and setting some values (dataSnapshot.child("item").getRef().setValue(x))

Another strange thing is that if I put a breakpoint inside run(), it also works smooth.

Escobar5
  • 3,941
  • 8
  • 39
  • 62
  • maybe Android Handler instead of creating new Thread ? http://stackoverflow.com/questions/15136199/when-to-use-handler-post-when-to-new-thread – Rafal Mar 06 '17 at 22:16
  • "You should use Handler.post() whenever you want to do operations in the UI thread", I don't want this executed in the UI thread, that's why I'm creating another Thread. – Escobar5 Mar 07 '17 at 00:00
  • Please update the post to include the code in the `run()` method. – Bob Snyder Mar 07 '17 at 15:52
  • @qbix added the code inside `run()`method – Escobar5 Mar 07 '17 at 16:06
  • `saveResponse()` parameter `response` is not used. Is that a typo in the code you posted? Should it be `studentResponse`? Where are `currentSection`, `currentSubsection`, and `currentContents` declared? – Bob Snyder Mar 09 '17 at 22:54
  • Yes, sorry, the parameter of the method is studentResponse instead of response. currentSection, currentSubsection and currentContents are declared in the activity. – Escobar5 Mar 10 '17 at 14:35

3 Answers3

1

every time you call onDataChange method will create a new thread,and the strange thing: " if I put a breakpoint inside run(), it also works smooth." may be you should check out whether there are too many thread created.

ohdroid
  • 771
  • 6
  • 3
  • How can I check if there are too many threads created? And how many are too much? – Escobar5 Mar 12 '17 at 19:11
  • https://developer.android.com/studio/profile/am-methodtrace.html ,this article will tell you how to find out the root reason that slow you app down. – ohdroid Mar 13 '17 at 09:39
  • Following your advise I found out that there was another listener in another activity that was still alive and doing too much work. Thanks – Escobar5 Mar 13 '17 at 15:52
0

I think the problem is with the logic of your method.

The listener onDataChange() is active and it will respond to any change of the data. I mean if you change data inside the onDataChange() method, it will be called each time you set values with (dataSnapshot.child("item").getRef().setValue(x))), so, it is similar to do a "recursive" call without exit.

In order to fix this problem, you should obtain the key of what you want to change in the on click event and just use

mDatabase = FirebaseDatabase.getInstance().getReference(); mDatabase.child("key").child("item").setValue(x);

Check https://firebase.google.com/docs/database/android/read-and-write for more info

aaroncio
  • 307
  • 1
  • 6
  • 1
    Not exactly, addListenerForSingleValueEvent executes de callback only once, also I'm querying data first because I need the previous data to increment some values, I've tried also with a transaction, same thing. – Escobar5 Mar 06 '17 at 23:59
0

A spawned thread inherits the priority of the thread that created it. Try lowering the priority of your worker thread to prevent it from competing with the UI thread:

@Override
public void onDataChange(final DataSnapshot dataSnapshot) {
    new Thread(new Runnable() {
        @Override
        public void run() {
            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
            ...
        }).start();
Bob Snyder
  • 37,759
  • 6
  • 111
  • 158