Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Drill into viewHierarchyState #14

Open
edenman opened this issue Oct 11, 2018 · 4 comments
Open

Drill into viewHierarchyState #14

edenman opened this issue Oct 11, 2018 · 4 comments

Comments

@edenman
Copy link

edenman commented Oct 11, 2018

This library is great!

10-11 11:53:13.179 D/TooLargeTool(21791): MainActivity.onSaveInstanceState wrote: Bundle@161550193 contains 7 keys and measures 821.2 KB when serialized as a Parcel
10-11 11:53:13.179 D/TooLargeTool(21791): * android:sessionId = 0.1 KB
10-11 11:53:13.179 D/TooLargeTool(21791): * com.google.app_measurement.screen_service = 0.2 KB
10-11 11:53:13.179 D/TooLargeTool(21791): * map_state = 0.2 KB
10-11 11:53:13.179 D/TooLargeTool(21791): * android:fragments = 0.8 KB
10-11 11:53:13.179 D/TooLargeTool(21791): * @android:autofillResetNeeded = 0.1 KB
10-11 11:53:13.179 D/TooLargeTool(21791): * android:lastAutofillId = 0.1 KB
10-11 11:53:13.179 D/TooLargeTool(21791): * android:viewHierarchyState = 819.7 KB

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.

@edenman
Copy link
Author

edenman commented Oct 12, 2018

(i ended up just doing it manually but still think it'd be cool to make this generic)

@maxspencer
Copy link
Contributor

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?

@edenman
Copy link
Author

edenman commented Oct 16, 2018

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).

@maxspencer
Copy link
Contributor

maxspencer commented Oct 17, 2018

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants