Title: Android Firebase - Looping through and updating multiple paths/queries. Triggers garbage collection?
Tags: java;android;firebase;firebase-realtime-database;garbage-collection
Question: I'm having an issue when trying to update multiple paths after looping through the queries.
Here's a quick mock up of my database:
Subjects/
- subjectId/
- subjectName
- subjectColor
Tasks/
- taskId/
- taskTitle
- taskDesc
- subjectId
- subjectName
- subjectColor
SubjectTasks/
- taskId/
- taskTitle
- taskDesc
- subjectId
- subjectName
- subjectColor
I have an activity in my app where users can edit the subjects' name and color. In order to update the subjectName and subjectColor properties of tasks with a subjectId value matching the one we edited I need to do a whole bunch of querying and looping through the Tasks and subjectTasks nodes. This is where the major performance hit happens (I think!)
It's this heavy looping and writing operation which I believe causes garbage collection to trigger, halting all threads, sending my app into an indeterminate loading screen.
Garbage collection triggered when subject is edited and saved :(
After many hours studying the heap and the memory profiler in Android Studio I have no other ideas other than posting here. It's more annoying that this was found just days before release!
Here is the code snippet of the activity where the writing of the edited subject happens:
private void WriteEditedSubject(final String uid, final String subjectName, final String primaryColour, int swatchId){
final DatabaseReference rootRef = mDatabase.getRef();
SubjectItem editedSubject = new SubjectItem(subjectName, primaryColour, swatchId);
editedSubject.setUid(subjectId);
//create map of edited values
Map<String, Object&gt; subjectValues = editedSubject.toMap();
//create map of updates to push
final Map<String, Object&gt; subjectUpdates = new HashMap<&gt;();
//update subjects node
subjectUpdates.put("/subjects/" + uid + "/" + subjectId, subjectValues);
//push updates
rootRef.updateChildren(subjectUpdates);
//todo: run a cloud function to update subject tasks and subjects using data from updated subject
//update subject tasks node where tasks match subjectId
mSubjectTasks.orderByChild("subjectId").equalTo(subjectId).addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
//create map of updates to push
Map<String, Object&gt; subjectTaskUpdates = new HashMap<&gt;();
for(DataSnapshot taskShot : dataSnapshot.getChildren()){
TaskItem task = taskShot.getValue(TaskItem.class);
if(task != null) {
task.setSubjectName(subjectName);
task.setSubjectPrimaryColour(primaryColour);
Map<String, Object&gt; taskValues = task.toMap();
subjectTaskUpdates.put("subjectTasks/" + uid + "/" + subjectId + "/" + task.getUid(), taskValues);
}
}
rootRef.updateChildren(subjectTaskUpdates);
}
@Override
public void onCancelled(DatabaseError databaseError) {
}
});
//update tasks node where tasks match subjectId
mTasks.orderByChild("subjectId").equalTo(subjectId).addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
//create map of updates to push
Map<String, Object&gt; taskUpdates = new HashMap<&gt;();
for(DataSnapshot taskShot : dataSnapshot.getChildren()){
TaskItem task = taskShot.getValue(TaskItem.class);
if(task !=  null) {
task.setSubjectName(subjectName);
task.setSubjectPrimaryColour(primaryColour);
Map<String, Object&gt; taskValues = task.toMap();
taskUpdates.put("tasks/" + uid + "/" + task.getUid(), taskValues);
}
}
rootRef.updateChildren(taskUpdates);
}
@Override
public void onCancelled(DatabaseError databaseError) {
}
});
Is there a way this operation can be cleaned up and made more efficient? One idea I had was to write a cloud function that would trigger when the subject is edited but I'm determined to find out why this is happening.
Thanks for any help. Much appreciated!
Comment: You are using `addValueEventListener()`, which leaves the listener attached.  Should you be using `addListenerForSingleValueEvent()` instead?

Comment: Exactly how many loop iterations are you talking about here?

Comment: At the moment it's less than 10 tasks in each. But can be a lot more when released.

Comment: Can you expand on this a bit please?

Comment: Why don't you use a service instead?


Here is another answer: It looks like what Bob Snyder pointed out was correct.
Replacing AddValueEventListener with AddListenerForSingleEvent fixed the problem.

{'source': 'StackExchangeDataset', 'subset': 'stackoverflow.com', 'metadata': {'AnswerCount': 1, 'CommentCount': 5, 'ContentLicense': 'CC BY-SA 3.0', 'CreationDate': '2017:00:08/28/17', 'FavoriteCount': None, 'PostTypeId': 1, 'Tags': '<java><android><firebase><firebase-realtime-database><garbage-collection>', 'Title': 'Android Firebase - Looping through and updating multiple paths/queries. Triggers garbage collection?', 'ViewCount': 450, 'id': 45927535, 'user_AboutMe': '', 'user_AccountId': 8641169.0, 'user_DisplayName': 'Tappyy', 'user_Reputation': 98.0, 'user_Views': 16.0, 'user_id': 6469823.0}}