Programmer001
Programmer001

Reputation: 2310

WeakReference to function type parameter null behavior

I understand that a WeakReference will allow the garbage collector to clean up its reference if it is no longer needed as a strong reference (such as an Activity being destroyed or ViewModel being cleaned up). But I'm trying to understand a case where my WeakReference to a function is returning a null reference at random points when the function's owning instance hasn't been garbage collected...

Consider the following ViewModel:

class SampleVM(dao: MyRoomDao) : ViewModel() {
    private val mLiveData: LiveData<List<Object>> = dao.loadAll()
    private val mMediatorLiveData: MediatorLiveData<List<Object>> = MediatorLiveData()

    init {
        mMediatorLiveData.addSource(mLiveData, { dbResult ->
            MyAsyncTask(
               mMediatorLiveData::setValue
        ).execute(dbResult ?: ArrayList())
    })

    ...
}

I pass the results of the original LiveData for further processing to an AsyncTask as well as a function type reference to setValue so the AsyncTask can call the mediator's setValue() in onPostExecute().

Consider the following AsyncTask:

class MyAsyncTask(onFinishCallback: (List<Object>) -> Unit) : AsyncTask<...,...,...>() {
   private val mOnFinishCallback = WeakReference<(List<Object>) -> Unit>(onFinishCallback)

   ...(overrides)...

   override fun onPostExecute(result: List<Object>?) {
      result?.let {
         mOnFinishCallback.get()?.invoke(it)
   }

I would think so long as onCleared() hasn't been called on the ViewModel, and it hasn't been garbage collected, that the WeakReference will always contain a non-null reference. But it so happens that there are two cases I've seen where mOnFinishCallback.get() returns null.

  1. While the app is running, restart the app via Android Studio by pressing Run (Shift + F10). The app will close, restart and my RecyclerView which before had items, is now blank. Via some sysouts, I can see the WeakReference has a null reference.
  2. While adding new items to the list via a FAB (processes an insert through a DAO instance) and watching my logs, most of the time the WeakReference is still referencing my the MediatorLiveData's setValue() function but at random times it will be null, but the next time I insert an item and this process repeats it is not null.

Any idea why this might be? If my ViewModel, and thus MediatorLiveData are still active and not being garbage collected, shouldn't the MediatorLiveData's setValue() function reference be valid the whole time?

Upvotes: 0

Views: 1105

Answers (1)

Mark
Mark

Reputation: 9929

When your method MediatorLiveData::addSource returns then there is no strong reference to you're functional interface (where you want to invoke mMediatorLiveData::setValue) meaning it is up for GC.

There is never an instance variable reference to the function itself, its only a method parameter.

You should hold a reference to the functional interface itself, for as long as you need it, so it can invoke setValue.

Upvotes: 0

Related Questions