Search code examples
androidmultithreadingkotlinmemory-leaksrunnable

Could this Runnable class cause memory leaks?


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

        }
    }

Solution

  • 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).