Problem with LiveData upon return from camera

I am obviously having trouble with the LiveData observer pattern. This first surfaced in Ch. 15 (see: Suspect name is not saving to database), but I kept going, and now it has resurfaced again - though now the image does save. If I come back in to the app, I do see the image. Doing some reading, the crux of the problem seems connected to lateinit not being thread safe, and the Room API is loading data from the database on a background thread.

When I launch the camera, take a picture, and then return to CrimeFragment I am getting the following error

java.lang.RuntimeException: Unable to resume activity 
  {criminalintentbnr4k/criminalintentbnr4k.MainActivity}: java.lang.RuntimeException: 
  Failure delivering result ResultInfo{who=null, request=65539, result=-1, 
  data=Intent {  }} to activity {criminalintentbnr4k/criminalintentbnr4k.MainActivity}:
  kotlin.UninitializedPropertyAccessException: lateinit property photoFile has not 
  been initialized

This is matching the prior problem that my CrimeFragment is being destroyed and detached when the implicit intent launches. When the camera returns, the life cycle flow is:

  • onCreate()
  • onAttach()
  • onCreateView()
  • onViewCreated()
  • onActivityCreated()
  • onStart()
  • onActivityResult()
  • updatePhotoView()

And at this point the LiveData hasn’t been returned so photoFile hasn’t been initialized by the observer and the line

if( photoFile.exists() )

fires the error above.

Currently, there seem to be three fixes:

  1. Make photoFile nullable - this is an ugly fix and requires a number of checks and hacks (I forced a double bang operator in there to work the URI generation)

  2. initialize photoFile and photoUri inside of onCreate() - both of these values only depend upon the crime ID. We have this value from the fragment arguments so we can assign a value at this point. We’d likely want to abstract/encapsulate this process to not duplicate it between the CrimeRepository and Crime classes and our now forced CrimeFragment addition.

  3. check if photoFile has been initialized - I would argue this is the cleanest solution and least obtrusive.

    if( this@CrimeFragment::photoFile.isInitialized && photoFile.exists() )

Should option 3 be an official fix to Listing 16.15? It would seem so due to the multithreading nature of the Room API.

I find it curious that this situation is not touched upon in the code or the writeup, so I am concerned I’m doing something wrong with my setup or process.

Cleaning up my trail of unintended errors (see Suspect name is not saving to database and Two problems with revokeUriPermission()), this is not a concern any longer.

However, this fix seems harmless to keep in place despite the minimal overhead of the extra boolean check. It also demonstrates the lateinit feature to those not as familiar with Kotlin.