I have created a class and extended it with Runnable
. This class is supposed to do operation in the background and post results on UI thread. However, the variables I am using this Runnable
class belong to the outter class(Parent class). Example:
Class MainFragment {
onStart() {
val runnable = BackgroundRunnable()
Thread(runnable).start()
}
private inner class BackgroundRunnable: Runnable {
onRun() {
//using MainFragment`s member variables here
//performing background ops that can take more than 48ms
}
getActivity().runOnUiThread(object : Runnable {
override fun run() {
//notfiyDataSetChanged()
}
}
}
}
As you can see from above example code, I am using inner
to access MainFragment
class member variables. This would cause a memory leak? If so how to avoid it?
Here is the actual code I wrote:
private inner class BackgroundRunnable : Runnable {
override fun run() {
list!!.clear()
listItems!!.clear()
list = dbHandler!!.readAllNotes()
for (noteReader in list!!.iterator()) {
val note = UserNotes()
note.noteTitle = noteReader.noteTitle
note.noteText = noteReader.noteText
note.noteID = noteReader.noteID
note.noteColor = noteReader.noteColor
note.noteEncrypted = noteReader.noteEncrypted
note.noteCheckList = noteReader.noteCheckList
note.noteDate = noteReader.noteDate
note.noteTempDel = noteReader.noteTempDel
note.noteArchived = noteReader.noteArchived
note.noteReminderID = noteReader.noteReminderID
note.noteReminderText = noteReader.noteReminderText
listItems!!.add(note)
}
activity!!.runOnUiThread(object : Runnable {
override fun run() {
adapter!!.notifyDataSetChanged()
return
}
})
}
}
Using Weak reference
private class BackgroundRunnable internal constructor(context: NotesFrag) : Runnable {
private val weakReference = WeakReference(context)
override fun run() {
val parentClass = weakReference.get()
parentClass!!.list!!.clear()
parentClass.listItems!!.clear()
parentClass.list = parentClass.dbHandler!!.readAllNotes()
for (noteReader in parentClass.list!!.iterator()) {
val note = UserNotes()
note.noteTitle = noteReader.noteTitle
note.noteText = noteReader.noteText
note.noteID = noteReader.noteID
note.noteColor = noteReader.noteColor
note.noteEncrypted = noteReader.noteEncrypted
note.noteCheckList = noteReader.noteCheckList
note.noteDate = noteReader.noteDate
note.noteTempDel = noteReader.noteTempDel
note.noteArchived = noteReader.noteArchived
note.noteReminderID = noteReader.noteReminderID
note.noteReminderText = noteReader.noteReminderText
parentClass.listItems!!.add(note)
}
parentClass.activity!!.runOnUiThread(object : Runnable {
override fun run() {
parentClass.adapter!!.notifyDataSetChanged()
return
}
})
}
}
Non-static inner classes can leak memory if they outlive the container object.
In your case, Fragment
will not get GC'ed until the Runnable
is finished because the inner class is holding an implicit reference to the containing Fragment
.
To solve this, generally the recommendation is to hold on to a WeakReference
to the object from which you would need data from the containing class or a WeakReference
to the Activity/Fragment, if you are accessing the Activity/Fragment directly (like you do with getActivity()
in the Runnable
).