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:
-
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) -
initialize
photoFile
andphotoUri
inside ofonCreate()
- 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 theCrimeRepository
andCrime
classes and our now forcedCrimeFragment
addition. -
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.