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

fix: Return early in rememberComposeBitmapDescriptor for invalid view size #533

Open
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

ln-12
Copy link

@ln-12 ln-12 commented Mar 15, 2024

Fixes #518 🦕

  • Make sure to open a GitHub issue as a bug/feature request before writing your code! That way we can discuss the change, evaluate designs, and agree on the general idea
  • Ensure the tests and linter pass
  • Code coverage does not decrease (if any source code was changed)
  • Appropriate docs were updated (if necessary)

By returning early in the case of invalid view width/height, we can avoid the IllegalArgumentException described in issue #518.

@ln-12 ln-12 changed the title Return early in rememberComposeBitmapDescriptor for invalid view size fix: Return early in rememberComposeBitmapDescriptor for invalid view size Mar 15, 2024
Copy link
Contributor

@bubenheimer bubenheimer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The end result of this change is that the map will render the default marker instead of crashing on an empty composable marker. This would seem to hide the problem in a workaround, rather than flag it. Would throwing a more meaningful exception help with troubleshooting here?

Just my 2 cents here: creating an empty ComposeView suggests some kind of problem in user code. I imagine user code may be able to avoid creating the marker entirely by testing whether necessary preconditions are met before attempting to create the marker.

@@ -51,6 +51,11 @@ private fun renderComposableToBitmapDescriptor(
View.MeasureSpec.makeMeasureSpec(parent.height, View.MeasureSpec.AT_MOST),
)

if(composeView.measuredWidth == 0 || composeView.measuredHeight == 0) {
parent.removeView(composeView)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can avoid the extra removeView() by adding try-finally in the right place here.

Copy link
Author

@ln-12 ln-12 Mar 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the feedback! Yeah, unfortunately I am not sure why this happens in the first place. Maybe some race condition while navigating? I can't dig into it as this happens super rarely and I was not able to reproduce it while having the debugger attached.

I don't think that a different error would help here as we already know that the problem is an incorrect view size. The question is just why we get to this state. As you can see from the example code in the issue, the original reporter only uses the GoogleMaps composable and adds some markers (the same as I do in my code). I don't see what we as users of this library could do differently as the code works >99% of the time. Or could we add any meaningful information to a new exception which would help finding the root cause?

For now, not providing a custom icon at all in this edge case was the best solution for me as it at least does not cause crashes in production.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if you used Modifier.requiredSizeIn() on your marker composable content with a minimum of 1.dp or so, as an alternative workaround? I'd be curious if you still crash then.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ln-12 Actually, I suspect that setting a minimum size on the composable, like I suggested above, would not make a difference.

Here is the problem that I think may be at the root of this issue (have not tested it):

View.MeasureSpec.makeMeasureSpec(parent.width, View.MeasureSpec.AT_MOST),
View.MeasureSpec.makeMeasureSpec(parent.height, View.MeasureSpec.AT_MOST),

A parent view dimension of 0 would cause this crash, no matter how large the Composable wants to be.

I'd think the parent view here would commonly be the Activity's top ComposeView, or supposedly something else in case of navigation, etc. So the maximum marker icon size would commonly be constrained by the size of the screen, which looks weird to me, as that could be quite large.

A better approach, short of workarounds, may be to use View.MeasureSpec.UNSPECIFIED here; restricting marker size by screen size just does not look that useful, it's way too big, creating giant bitmaps. However, I imagine this change would be breaking, as there is no max size anymore. Yet a marker composable relying on something like fillMaxSize is not doing it right, it needs to use something more specific. There may be other concerns with UNSPECIFIED, however, not sure.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right. I can force the crash by setting the size of the fragment which contains the compose view to 0 (but I am still not sure when this happens while navigating): https://github.com/ln-12/GoogleMapsCrashReproducer/blob/main/app/src/main/res/layout/activity_main.xml#L11-L12

With that, I also tested View.MeasureSpec.UNSPECIFIED and for me it did not crash anymore. I can change my PR to use this spec, but just like you, I am not sure about other side effects when doing so.

So what would you suggest going forward?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If no one objects, I suggest going with the following (see above comment):

  1. Set layoutParams (WRAP_CONTENT) on ComposeView, because docs say you have to.
  2. Use View.MeasureSpec.UNSPECIFIED, because this makes the most sense to me. This is a breaking change, as it makes parent size 0. Before, parent size was typically the screen size, which seems an unlikely intentional input for determining marker size and can be obtained in other ways. This change needs to be documented.

Copy link
Author

@ln-12 ln-12 May 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed the PR accordingly. After some testing, I could not get another crash with these changes, so it seems to work.

Should this be documented inline with the code or only as a release note in the changelog?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be documented inline with the code or only as a release note in the changelog?

The requirement for the Composable to have a non-null size in both dimensions, and the absence of a parent size need to be documented in public API KDoc documentation on MarkerComposable(). Also in release notes, I believe this would be a task for project maintainers, e.g. @kikoso or @dkhawk.

It could also be flagged prominently in code; personally I'd check for this case after measure() and throw an exception of some sort if measure returns 0 for one or both of the dimensions. This way the problem is identified in a well-defined manner if it happens with the ability to give specific instructions on what to do (set a size in the Composable).

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What @bubenheimer describer: the expectation is to have the documentation incode from the PR. The release notes can be manually updated to reflect any other relevant changed.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BTW, I meant "absence of a parent size", not "absence of a parent".

@el-qq
Copy link
Contributor

el-qq commented May 16, 2024

@ln-12 hi. Thank you for this contribution

Please add semantic prefixes to commits

@ln-12 ln-12 force-pushed the 518_illegal_argument_exception branch from c679916 to 62c313d Compare May 17, 2024 09:26
@ln-12
Copy link
Author

ln-12 commented May 17, 2024

I added the commit prefixes and applied all requested changes. If you are not yet happy with the wording, please let me know.


if (composeView.measuredWidth == 0 || composeView.measuredHeight == 0) {
throw IllegalStateException("The ComposeView was measured to have a width or height of " +
"zero. Make sure the parent and content have a non-zero size.")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to your testing, "parent" size should no longer matter, right?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, right. I changed that.

@ln-12 ln-12 force-pushed the 518_illegal_argument_exception branch from 6a838ba to 81423e7 Compare May 17, 2024 16:22
@ln-12
Copy link
Author

ln-12 commented May 17, 2024

I was using the wrong mail for my last commit (as I was on a different machine), so the CLA check failed. Fixed that as well. Sorry for the inconvenience!

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

Successfully merging this pull request may close these issues.

MarkerComposable throw Fatal Exception: java.lang.IllegalArgumentException: width and height must be > 0
6 participants