We are receiving some exceptions from a production environment, using Firebase Crashlytics. Unfortunately the issue is not locally reproducible and upon searching a bit here and there I couldn't find the same exception message, except some source code which does not really help.
Edit: If you wonder why I added Java as a tag, please look down below. You may answer the question without any Android knowledge.
The application uses fragments (ViewPager), MultiDex and lambda runnables, and it does not use view models or data binding. The exception also seem to be happening while the application is in foreground, if it helps.
The exceptions are happening from API 23 (Android 6.0) to API 29 (Android Q), we almost have at least one exception in each API level. No exceptions are recorded on lower APIs, though.
Exceptions are happening from various points, whether it's from Activity.onStart(), whether it's from ViewPager.setAdapter() or whether from Activity.onDestroy() and some various spots that I haven't checked yet. Although all of them contain some point to the program's code, I'm not sure if the program itself is the actual cause. There are some intersections on the stack traces, though.
Here is one of the exceptions. Apparently some parts are obfuscated due to proguard. (package name is hidden due to confidentiality):
Caused by java.lang.IllegalArgumentException: Local and anonymous classes can not be ViewModels
at androidx.lifecycle.ViewModelProvider.a + 40(ViewModelProvider.java:40)
at androidx.loader.app.LoaderManagerImpl$LoaderViewModel.dump + 361(LoaderManagerImpl.java:361)
at androidx.loader.app.LoaderManagerImpl.<init> + 5(LoaderManagerImpl.java:5)
at androidx.loader.app.LoaderManager.a + 9(LoaderManager.java:9)
at androidx.fragment.app.Fragment.performDestroyView + 29(Fragment.java:29)
at androidx.fragment.app.FragmentManagerImpl.a + 963(FragmentManagerImpl.java:963)
at androidx.fragment.app.FragmentManagerImpl.m + 99(FragmentManagerImpl.java:99)
at androidx.fragment.app.FragmentManagerImpl.dump + 489(FragmentManagerImpl.java:489)
at androidx.fragment.app.FragmentManagerImpl.completeShowHideFragment + 1165(FragmentManagerImpl.java:1165)
at androidx.fragment.app.FragmentManagerImpl.dispatchDestroy + 2644(FragmentManagerImpl.java:2644)
at androidx.fragment.app.FragmentController.c + 4(FragmentController.java:4)
at androidx.fragment.app.FragmentActivity.onDestroy + 5(FragmentActivity.java:5)
at androidx.appcompat.app.AppCompatActivity.onDestroy(AppCompatActivity.java)
at com.example.myapp.activities.MainActivity.onDestroy + 28(MainActivity.java:28)
at android.app.Activity.performDestroy + 7724(Activity.java:7724)
at android.app.Instrumentation.callActivityOnDestroy + 1310(Instrumentation.java:1310)
at android.app.ActivityThread.performDestroyActivity + 4723(ActivityThread.java:4723)
at android.app.ActivityThread.handleDestroyActivity + 4761(ActivityThread.java:4761)
at android.app.servertransaction.DestroyActivityItem.execute + 39(DestroyActivityItem.java:39)
at android.app.servertransaction.TransactionExecutor.executeLifecycleState + 145(TransactionExecutor.java:145)
at android.app.servertransaction.TransactionExecutor.execute + 70(TransactionExecutor.java:70)
The (MainActivity.java:28) line points to here:
public void onDestroy() {
super.onDestroy(); // the stack trace points here
}
It happens from the super call, which is something neither I can avoid calling, nor wrapping into try-catch. I did realize that all of the stack traces call something called LoaderManager (I was unable to find an Android Jetpack documentation) which internally calls ViewModelProvider. Apparently it uses the view model provider to log stuff, but on some occasions (the exception ratio is actually pretty low), it occurs and gets reported.
At the top of the stack trace, it mentions of ViewModelProvider. Though the stack trace is obfuscated, from Fragment.performDestroyView() I was able to track down the method that was tried to be called.
Out of curiosity I checked the source code of that method of the ViewModelProvider and found this:
/**
* Returns an existing ViewModel or creates a new one in the scope (usually, a fragment or
* an activity), associated with this {@code ViewModelProvider}.
* <p>
* The created ViewModel is associated with the given scope and will be retained
* as long as the scope is alive (e.g. if it is an activity, until it is
* finished or process is killed).
*
* @param modelClass The class of the ViewModel to create an instance of it if it is not
* present.
* @param <T> The type parameter for the ViewModel.
* @return A ViewModel that is an instance of the given type {@code T}.
*/
@NonNull
@MainThread
public <T extends ViewModel> T get(@NonNull Class<T> modelClass) {
String canonicalName = modelClass.getCanonicalName();
if (canonicalName == null) {
throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");
}
return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
}
Now, this line:
String canonicalName = modelClass.getCanonicalName();
I'm not sure in which circumstances a canonical name is null. But, the root cause is here. So, my main question is, why would a class' canonical name return null? And, is this something that I can resolve?
Any help is appreciated, thank you.