Open edenman opened 5 years ago
(i ended up just doing it manually but still think it'd be cool to make this generic)
This sounds like a nice idea for an improvement, thank you.
How did you tackle this manually? and if you had to make it generic how would you go about it?
The first idea springs to my mind is to make the tool print a kind of tree, that shows each "sub-Bundle
" of the saved instance state (I'm assuming android:viewHierarchyState
is indeed a Bundle
here?) indented underneath their top-level key and total size. Simple values would look like they do now.
You know, now I come to think of it (this is going back a while so I could be wrong) I think the big bit of state I was looking for that prompted me to make this tool was actually nested inside a fragment's arguments and I did some manual extra work to identify the root cause then as well. I guess I did something like:
// Manually added in onCreate of offending fragment:
TooLargeTool.logBundleBreakdown(TAG, getArguments());
Obviously I was sick of the whole thing by then and didn't follow up with a proper generic solution! But maybe we can now?
Here's what I ended up doing:
private var savedState: Bundle? = null
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
map.onSaveInstanceState(outState)
savedState = outState
}
override fun onStop() {
super.onStop()
val foo = savedState
if (foo != null) {
for (key in foo.keySet()) {
if (key == "android:viewHierarchyState") {
val vhBundle = foo.getBundle("android:viewHierarchyState").require("No bundle")
val viewsSparseArray = vhBundle.getSparseParcelableArray<Parcelable>("android:views")
.require("No bundle for android:views")
viewsSparseArray.forEach { viewKey, viewState ->
val idName = safeNameForID(resources, viewKey)
Timber.d("view name($idName) took size ${sizeOfParcel(viewState)}")
}
}
}
savedState = null
}
}
fun sizeOfParcel(foo: Parcelable): Int {
val parcel = Parcel.obtain()
parcel.writeParcelable(foo, 0)
val dataSize = parcel.dataSize()
parcel.recycle()
return dataSize
}
I think it would make sense to just auto-expand keys above a certain size, but I'd have to do some tinkering to figure out how to detect the type of thing (android:viewHierarchyState
is a Bundle, but then android:views
is a SparseArray
).
You can use BaseBundle.get(key: String): Object
and then use is
/instanceof
to find out what the value is.
We have TooLargeTool.valueSizes(bundle: Bundle): Map<String, Int>
which breaks down a Bundle
and reports the size of each key, I guess we'll need a similar function to operate on a SparseArray
too?
As a preliminary step I might introduce a type to better represent these tree structures because Map<String, Int>
ain't gonna cut it.
This library is great!
I'd like to figure out what part of the viewHierarchyState is taking up so much room. Any thoughts on how to best allow drilling-down in a situation like this? I could specify keys that I want to be drilled-down into when I call
TooLargeTool.startLogging(this)
? Or we could automatically drill down into the keys above a certain size? Happy to put together a PR if you give me some guidance on what approach(es) you'd be ok with.