Crash when selecting "Home" tab
Zhuinden opened this issue · comments
11-30 04:54:27.700 4977-4977/com.codingwithmitch.openapi E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.codingwithmitch.openapi, PID: 4977
java.lang.NullPointerException: Attempt to invoke virtual method 'com.codingwithmitch.openapi.ui.Data com.codingwithmitch.openapi.ui.DataState.getData()' on a null object reference
at com.codingwithmitch.openapi.ui.main.blog.ViewBlogFragment$subscribeObservers$1.onChanged(ViewBlogFragment.kt:79)
at com.codingwithmitch.openapi.ui.main.blog.ViewBlogFragment$subscribeObservers$1.onChanged(ViewBlogFragment.kt:21)
at androidx.lifecycle.LiveData.considerNotify(LiveData.java:131)
at androidx.lifecycle.LiveData.dispatchingValue(LiveData.java:149)
at androidx.lifecycle.LiveData.setValue(LiveData.java:307)
at androidx.lifecycle.MutableLiveData.setValue(MutableLiveData.java:50)
at androidx.lifecycle.Transformations$2$1.onChanged(Transformations.java:155)
at androidx.lifecycle.MediatorLiveData$Source.onChanged(MediatorLiveData.java:152)
at androidx.lifecycle.LiveData.considerNotify(LiveData.java:131)
at androidx.lifecycle.LiveData.dispatchingValue(LiveData.java:149)
at androidx.lifecycle.LiveData.setValue(LiveData.java:307)
at androidx.lifecycle.LiveData$1.run(LiveData.java:91)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5417)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
Actually, that first crash is trickier than I thought. I can't get a consistent repro of it. 🤔
Ah, you need to actually select a Blog by clicking it first.
Then it will happen consistently. ❤️
11-30 05:40:32.359 5634-5634/com.codingwithmitch.openapi E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.codingwithmitch.openapi, PID: 5634
java.lang.NullPointerException: Attempt to invoke virtual method 'com.codingwithmitch.openapi.ui.Data com.codingwithmitch.openapi.ui.DataState.getData()' on a null object reference
at com.codingwithmitch.openapi.ui.main.blog.ViewBlogFragment$subscribeObservers$1.onChanged(ViewBlogFragment.kt:79)
at com.codingwithmitch.openapi.ui.main.blog.ViewBlogFragment$subscribeObservers$1.onChanged(ViewBlogFragment.kt:21)
at androidx.lifecycle.LiveData.considerNotify(LiveData.java:131)
at androidx.lifecycle.LiveData.dispatchingValue(LiveData.java:149)
at androidx.lifecycle.LiveData.setValue(LiveData.java:307)
at androidx.lifecycle.MutableLiveData.setValue(MutableLiveData.java:50)
at androidx.lifecycle.Transformations$2$1.onChanged(Transformations.java:155)
Can you help me reproduce? mitch@tabian.ca
If you don't mind. :)
1.) login
2.) on the initial tab, select a blog post
3.) select another tab
4.) put the application in background with HOME
5.) press "Terminate Application" in the Logcat tab in Android Studio
6.) restart the app from launcher
7.) try to go to the blog tab again
The 4-5-6th steps simulate when Android kills a backgrounded app then the user comes back to it from either the recent tasks or the launcher intent.
Thank you I'll take a look.
Yikes this is a big problem
Process ID before manually killing application.
Issue:
New process after relaunch results in all data lost in viewmodel. Fragment backstack is maintained.
Resolution:
There's two options that I see:
-
Save the state of viewmodel and restore.
- See: Saved State for ViewModel
- This seems very complex when using with dagger, but is likely the best option.
- More research needed...
- See: Saved State for ViewModel
-
Reset the app after process death
- This is more of a bandaid than a solution.
- This would be much easier. The downside is the user would not be able to continue doing whatever they were doing before the process died. Theoretically this shouldn't happen often. Probably only low memory conditions.
- How?
- Save PID to SharedPreferences in launcher activity (AuthActivity)
- Check PID in MainActivity. If does not match, restart application.
Thank you @Zhuinden for pointing this out. I had no idea how to test process death.
Theoretically this shouldn't happen often.
Mate, I use a Pixel XL with 4 GB RAM, all I need to do to trigger this is take a photo. Sometimes that's enough, sometimes I also share it to Google Maps which is definitely enough.
Can't even imagine what it's like on a lower end device. 🤔
Please tell me if I'm missing something.
I ran on a OnePlus One , which has 3 GB of ram and was released in 2014.
- Launched Open Api app
- Clicked a blog
- Selected another tab
- Sent to background by pressing HOME button on device - Opened Camera App
- Took several pictures
- Viewed several pictures
- Opened Google Maps:
- Uploaded a photo to a park I go to frequently
- Searched for places
- Got directions
- Scrolled around aimlessly
- Opened Instagram:
- Scrolled around
- Watched stories
- Published a story of my own by taking a picture
- Also I would note that I received about 100 push notifications when I opened it
- Opened facebook:
- Scrolled around
- Opened YouTube:
- Played some videos (making sure in 1080p)
- Updated the following apps (since I haven't ran this device in a while)
- Instagram
- Facebook
- Google maps
- YouTube
While doing all this I kept checking the process using ADB and it was never killed. I obviously also opened the Open-Api app from the recently used app list after doing all this, and it was functioning fine. I thought maybe I need to "relaunch" the app to cause the issue but that didn't either.
Am I doing something wrong?
Issue is partially fixed.
What is resolved:
- AuthToken is restored in SessionManager after process death:
- Saved using onSaveInstanceState in MainActivity
- Restored with restoreSession in MainActivity
- ViewStates are restored in BaseFragments on process death:
- BaseAuthFragment restores AuthViewState
- BaseAccountFragment restores AccountViewState
- BaseBlogFragment restores BlogViewState
- !!!!NOTE: Saving the search query results (the list of blogs) is not recommended. This is too much data for
onSaveInstanceState
. Instead, the search query itself (String value), should be saved usingonSaveInstanceState
and the query should be made again once the app is restored. Make sure the RecyclerView scrolls to the same position they left it. See reference.
- !!!!NOTE: Saving the search query results (the list of blogs) is not recommended. This is too much data for
- BaseCreateBlogFragment restores CreateBlogViewState
Issues that still remain:
Interestingly the issue still remains if you trigger a configuration change immediately upon relaunching the app after killing the process.
Still working on this...
onViewStateRestored
only happens if onViewCreated
happened, which is skipped if the View is not actually used due to immediately cancelling out on creation / rotation.
You can workaround this by restoring state in Fragment.onCreate
instead of onViewStateRestored
.
Solved. See commit.
Merging to master.
TODO (future improvement):
Saving the search query results (the list of blogs) is not recommended. This is too much data for onSaveInstanceState. Instead, the search query itself (String value), should be saved using onSaveInstanceState and the query should be made again once the app is restored. Make sure the RecyclerView scrolls to the same position they left it. See reference.
Update:
- Restored List by saving query to outState and making query again.
- Fixed BottomNav backstack issue:
Will merge to master after I film their respective videos.