Search code examples
androidlambdamemory-leakskotlin

Kotlin : safe lambdas (no memory leak)?


After having read this article about Memory Leaks, I am wondering whether using lambdas in Kotlin Android project is safe. It's true that lambda syntax makes me program with more ease, but what about the Memory Leaks ?

As an example of the problematic, I've taken a piece of code from one of my projects, where I build an AlertDialog. This code is inside the MainActivity class of my project.

fun deleteItemOnConfirmation(id: Long) : Unit {
        val item = explorerAdapter.getItemAt(id.toInt())
        val stringId = if (item.isDirectory) R.string.about_to_delete_folder else R.string.about_to_delete_file

        val dialog = AlertDialog.Builder(this).
                setMessage(String.format(getString(stringId), item.name)).setPositiveButton(
                R.string.ok, {dialog: DialogInterface, id: Int ->
                        val success = if (item.isDirectory) ExplorerFileManager.deleteFolderRecursively(item.name)
                        else ExplorerFileManager.deleteFile(item.name)
                        if (success) {
                            explorerAdapter.deleteItem(item)
                            explorerRecyclerView.invalidate()
                        }
                        else Toast.makeText(this@MainActivity, R.string.file_deletion_error, Toast.LENGTH_SHORT).show()
                    }).setNegativeButton(
                R.string.cancel, {dialog: DialogInterface, id: Int ->
                    dialog.cancel()
        })

        dialog.show()
}

My question is very simple : can the two lambdas set for positive and negative buttons lead to Memory Leaks ? (I also mean, are kotlin lambdas simply converted to Java Anonymous functions ?)

Edit : Maybe I've got my answer in this Jetbrains Topic.


Solution

  • Here's a simple example of a (potential) leak, where the closure/block captures this:

    class SomeClass {
        fun doWork() {
            doWorkAsync { onResult() } // leaks, this.onResult() captures `this`
        }
    
        fun onResult() { /* some work */ }
    }
    

    You'll need to use a WeakReference.

    fun doWork() {
        val weakThis = WeakReference(this)
        doWorkAsync { weakThis?.get()?.onResult() } // no capture, no leak!
    }