Fixed
Status Update
Comments
ho...@gmail.com <ho...@gmail.com> #2
Looking into this. It's not clear from looking at the code how this can happen. There are two IDs used as tags:
<item type="id" name="metricsStateHolder"/>
<item type="id" name="metricsDelegator"/>
These are assigned unique values at build time. As long as they are only ever used to assign objects of the correct type with those tags, there shouldn't be the kind of classCast exception seen above, and the assignments look correct, eg:
metricsStateHolder = Holder()
rootView.setTag(R.id.metricsStateHolder, metricsStateHolder)
and
delegator = DelegatingFrameMetricsListener(delegates)
decorView.setTag(R.id.metricsDelegator, delegator)
Logging shows ids are indeed unique and off by one, eg:
2131230959, 2131230958
Still investigating
<item type="id" name="metricsStateHolder"/>
<item type="id" name="metricsDelegator"/>
These are assigned unique values at build time. As long as they are only ever used to assign objects of the correct type with those tags, there shouldn't be the kind of classCast exception seen above, and the assignments look correct, eg:
metricsStateHolder = Holder()
rootView.setTag(R.id.metricsStateHolder, metricsStateHolder)
and
delegator = DelegatingFrameMetricsListener(delegates)
decorView.setTag(R.id.metricsDelegator, delegator)
Logging shows ids are indeed unique and off by one, eg:
2131230959, 2131230958
Still investigating
ma...@gmail.com <ma...@gmail.com> #3
Looks like a race condition. The two keys can be used from different threads: the main thread and the FrameMetricsAggregator thread. SparseArray (used by View tags) is not thread safe, so it is theoretically possible for some of the same items in the array to be used by different threads. The API doesn't make that obvious, since different tags should be sorted with different keys, but since it's not thread safe, the same item may be manipulated by two threads in parallel, thus resulting in ill-defined results like what we're seeing here. I'm not able to repro the issue directly in JankStats, but I am able to create such a situation using SparseArray directly, so it seems like this is probably the root cause.
We synchronize access to some structures in JankStats already for other potential race conditions; we should be able to similarly protect access to the tags array for use between threads.
We synchronize access to some structures in JankStats already for other potential race conditions; we should be able to similarly protect access to the tags array for use between threads.
ap...@google.com <ap...@google.com> #4
The main culprit (called out in that stack trace) was the call to get the state holder from the FrameMetricsAggregator thread. This is only done to remove SingleFrameStates after processing a frame, but there's a simpler way to do that during frame processing code directly, without needing to access the tags from a non-UI thread.
There are various other calls to getTag/setTag that have been modified to ensure they are called from the UI thread, so things should be more solid (in an upcoming fix).
There are various other calls to getTag/setTag that have been modified to ensure they are called from the UI thread, so things should be more solid (in an upcoming fix).
jb...@google.com <jb...@google.com> #5
Happened to me as well.
According to datadog a user clicked a deeplink that navigates into our app.
java.lang.ClassCastException: com.xxx.ui.main.MainActivity cannot be cast to androidx.metrics.performance.PerformanceMetricsState$Holder
at androidx.metrics.performance.PerformanceMetricsState$Companion.getHolderForHierarchy(Unknown Source:27)
at androidx.metrics.performance.a.onFrameMetricsAvailable(Unknown Source:162)
at android.view.FrameMetricsObserver.onFrameMetricsAvailable(FrameMetricsObserver.java:59)
at android.graphics.HardwareRendererObserver.lambda$notifyDataAvailable$0$HardwareRendererObserver(HardwareRendererObserver.java:93)
at android.graphics.HardwareRendererObserver$$ExternalSyntheticLambda0.run(Unknown Source:2)
at android.os.Handler.handleCallback(Handler.java:980)
at android.os.Handler.dispatchMessage(Handler.java:104)
at android.os.Looper.loopOnce(Looper.java:238)
at android.os.Looper.loop(Looper.java:357)
at android.os.HandlerThread.run(HandlerThread.java:85)
According to datadog a user clicked a deeplink that navigates into our app.
java.lang.ClassCastException: com.xxx.ui.main.MainActivity cannot be cast to androidx.metrics.performance.PerformanceMetricsState$Holder
at androidx.metrics.performance.PerformanceMetricsState$Companion.getHolderForHierarchy(Unknown Source:27)
at androidx.metrics.performance.a.onFrameMetricsAvailable(Unknown Source:162)
at android.view.FrameMetricsObserver.onFrameMetricsAvailable(FrameMetricsObserver.java:59)
at android.graphics.HardwareRendererObserver.lambda$notifyDataAvailable$0$HardwareRendererObserver(HardwareRendererObserver.java:93)
at android.graphics.HardwareRendererObserver$$ExternalSyntheticLambda0.run(Unknown Source:2)
at android.os.Handler.handleCallback(Handler.java:980)
at android.os.Handler.dispatchMessage(Handler.java:104)
at android.os.Looper.loopOnce(Looper.java:238)
at android.os.Looper.loop(Looper.java:357)
at android.os.HandlerThread.run(HandlerThread.java:85)
Description
Component used: Navigation Version used: 2.9.0-alpha05 Devices/Android versions reproduced on: 33
I have two nested navHostController mainNavController dashboardNavController
in dashboardNavHost when i switch between tabs with this code :
dashboardNavController.navigate(rout) { popUpTo(dashboardNavController.graph.startDestinationId) { saveState = true } launchSingleTop = true restoreState = true }
and then with mainNavController go to other composable screen, when popup back, i get the following error:
java.lang.IllegalStateException: The saved state value associated with the key 'android-support-nav:controller:backStackIds' is either null or not of the expected type. This might happen if the value was saved with a different type or if the saved state has been modified unexpectedly. at androidx.navigation.NavController.restoreState(NavController.android.kt:3697) at androidx.navigation.compose.NavHostControllerKt$NavControllerSaver$2.invoke(NavHostController.kt:80) at androidx.navigation.compose.NavHostControllerKt$NavControllerSaver$2.invoke(NavHostController.kt:78) at androidx.compose.runtime.saveable.SaverKt$Saver$1.restore(Saver.kt:65) at androidx.compose.runtime.saveable.RememberSaveableKt.rememberSaveable(RememberSaveable.kt:86) at androidx.navigation.compose.NavHostControllerKt.rememberNavController(NavHostController.kt:59)