Search code examples
androidkotlinmemory-leakscompanion-object

kotlin lateinit in companion object for widget - leak warning


So I am quite new to kotlin and Android. I wrote an app, and it works exactly how I want it to, except that it gives me a compiler warning:

 'Do not place Android context classes in static fields; this is a memory leak'

In one file I have:

lateinit var appContext: Context private set
lateinit var appRez: Resources private set

class HackJob : Application() {
    override fun onCreate() {
        super.onCreate()
        appContext = applicationContext
        appRez = resources
        val clientthread = ClientThread()  //these three can be collapsed into one line
        val maThread = Thread(clientthread)
        maThread.start()
    }
    internal class ClientThread : Runnable { 

        ContextCompat.getMainExecutor(appContext).execute /* command = */  {
            MainActivity.otv.setBackgroundColor(Color.RED)
            MainActivity.osb.visibility = View.VISIBLE
        }

    }
}

In the other file:

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // create a value that is linked to a button called (id) start in the layout

        val tv: TextView = findViewById(R.id.rcvdData)
        val startButton = findViewById<Button>(R.id.start)
...
        otv = tv
        osb = startButton
...
   }
   companion object {
        lateinit var otv: TextView
        lateinit var osb: Button
   }
}

As I say, it seems to work just fine, even when the screen rotates. Does exactly what I want it to do!

Am I really leaking memory here? How concerned should I be about the leak warning?

When I rotate the screen, presumably android is tearing down the activity, and re-instantiating it rotated, but my companion vars(otv and osb) are updated in onCreate! Does that mean the old values lose ref count and get GCed?

I admit I don't really understand what I am doing, I just keep trying till it works. How bad is this code smell? The app is snappy and works great!


Solution

  • it seems to work just fine, even when the screen rotates

    It has issues.

    Am I really leaking memory here?

    Yes. The impact is minimal for the literal code in your question, but if you ever intend to change that code, that impact can get worse.

    When I rotate the screen, presumably android is tearing down the activity, and re-instantiating it rotated, but my companion vars(otv and osb) are updated in onCreate! Does that mean the old values lose ref count and get GCed?

    Yes. However, please bear in mind that there are other ways for an activity to be destroyed besides configuration changes, such as via a call to finish() or by system BACK navigation. In those cases, you are leaking memory. Also, you can create 2+ instances of MainActivity, in which case those properties will only point to the most-recently-instantiated MainActivity, which may cause problems.


    A lot of your trouble centers around this:

        internal class ClientThread : Runnable { 
    
            ContextCompat.getMainExecutor(appContext).execute /* command = */  {
                MainActivity.otv.setBackgroundColor(Color.RED)
                MainActivity.osb.visibility = View.VISIBLE
            }
    
        }
    

    This has several problems:

    1. You are assuming that those properties (otv, osb) are populated prior to referencing them. There is no guarantee of this.
    2. You are assuming that those properties point to the correct instance of MainActivity. If there are 2+ instances of MainActivity, that may not be correct.
    3. You are assuming that those properties point to a non-destroyed instance of MainActivity. There is no guarantee of this, though it's fairly likely.
    4. This programming approach is what is causing you to create those potentially-leaky properties in the first place.