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

Shields loading optimization #6826

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

Conversation

tomaszrybakiewicz
Copy link
Contributor

@tomaszrybakiewicz tomaszrybakiewicz commented Jan 12, 2023

Closes NAVAND-483

Description

After reviewing RoadShiled loading logic, I've noticed that we were caching RoadShield image data in 3 different places.

  1. ShieldResultCache - RouteShield in LruCache
  2. ShieldByteArrayCache - ByteArray in LruCache
  3. Native TileStore - ByteArray on disk

The ShieldResultCache already caches image data together with RouteShield; therefore, ShieldByteArrayCache is redundant.

In this PR, I optimized RoadShield loading by:

  • deleting duplicate image cache
  • separating download from caching logic - introduced local ResourceLoader and CacheResourceLoader decorator
  • renaming ShieldResultCache -> RoadShieldLoader and loosely coupling it with sprites and image loaders
  • loosely coupling RoadShieldContentManagerImpl with the RoadShieldLoader class

(new setup)
shield-loader2

- deleted duplicate image cache - ShieldsCache and ShieldByteArrayCache were caching same data twice
- separated download from caching logic by introducing ResourceLoader and CacheResourceLoader local interfaces
- renamed ShieldResultCache -> RoadShieldLoader and loosely coupled it with sprites and image loaders
- updated RoadShieldContentManagerImpl - loosely coupled it with loader class
@tomaszrybakiewicz tomaszrybakiewicz added UI Work related to visual components, Android Auto, Camera, 3D, voice, etc. skip changelog Should not be added into version changelog. labels Jan 12, 2023
@github-actions
Copy link

Changelog

Features

  • Introduced ViewStyleCustomization.infoPanelGuidelineMaxPosPercent that allows customization of the NavigationView InfoPanel bottom guideline maximum position. Increased default value to 50%. #6792

Bug fixes and improvements

  • Fixed an issue with NavigationView that caused info panel to shrink in landscape mode with a full screen theme. #6780

  • ⚠️ Updated the NavigationView default navigation puck asset. #6678

    Previous puck can be restored by injecting LocationPuck2D with the bearingImage set to com.mapbox.navigation.ui.maps.R.drawable.mapbox_navigation_puck_icon drawable:

    navigationView.customizeViewStyles {
        locationPuckOptions = LocationPuckOptions.Builder(context)
            .defaultPuck(
                LocationPuck2D(
                    bearingImage = ContextCompat.getDrawable(
                        context,
                        com.mapbox.navigation.ui.maps.R.drawable.mapbox_navigation_puck_icon,
                    )
                )
            )
            .idlePuck(regularPuck(context))
            .build()
    }
  • Fixed an issue where the first voice instruction might have been played twice. #6766

  • Added guarantees that route progress with RouteProgress#currentState == OFF_ROUTE arrives earlier than NavigationRerouteController#reroute is called. #6764

  • Introduced NavigationViewListener.onSpeedInfoClicked that would be triggered when MapboxSpeedInfoView is clicked upon. #6770

  • Each newly instantiated MapboxRouteArrowView class will initialize the layers with the provided options on the first render call. Previously this would only be done if the layers hadn't already been initialized. #6466

  • Fixed a rare java.lang.NullPointerException: Attempt to read from field 'SpeechAnnouncement PlayCallback.announcement' on a null object reference crash in PlayCallback.getAnnouncement. #6760

  • Fixed standalone MapboxManeuverView appearance when the app also integrates Drop-In UI. #6774

Known issues ⚠️

Other changes

Android Auto Changelog

Features

Bug fixes and improvements

@github-actions github-actions bot requested a review from kmadsen January 12, 2023 16:12

private typealias LoaderCallback<R> = (Expected<String, R>) -> Unit

internal abstract class ResourceDownloader<I, R> : ResourceLoader<I, R> {
Copy link
Contributor

Choose a reason for hiding this comment

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

"I" stands for "input"?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes


import com.mapbox.bindgen.Expected

internal fun interface ResourceLoader<Argument, Resource> {
Copy link
Contributor

Choose a reason for hiding this comment

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

There is already a ResourceLoader in com.mapbox.navigation.ui.utils.internal.resource. Can we reuse that one? I find 2 classes with the same quite an abstract name confusing.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No. They are not the same. The utils ResourceLoader is a global facade over the native TileStore, and it acts more like Downloader than a local loader (maybe ResourceDownloader would be a better name for it).
This ResourceLoader is an internal interface. It offers an abstraction for a generic, async loader and helps break tight coupling between the RoadShieldContentManager and concrete loaders.
I am aware that the name clashes with the utils version. We can shorten it to just a Loader if it helps. Furthermore, I would like to introduce this abstraction here first and later, if we find it useful elsewhere, move it to the utils module as well.

Copy link
Contributor

Choose a reason for hiding this comment

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

They should have different names. Especially considering the fact they are interfaces. So you can't even just look at the class code and understand the difference.
Maybe the utils one should be TileStoreResourcesLoader or sth like that. Since it uses Common classes in the API anyway.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Since they are internal interfaces they don't have to have different names.
Anyway, I've renamed it to just Loader.

Copy link
Contributor

Choose a reason for hiding this comment

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

Since they are internal interfaces they don't have to have different names.

Technically of course they don't, but for better understanding they should. Especially considering the fact that utils ResourceLoader is accessible from other modules.

Anyway, I've renamed it to just Loader.

It's not really helpful. Now it looks like the ResourceLoader should implement Loader and Loader should be in utils module as well since it's general enough. I think in this case specifying what utils ResourceLoader does is a better way. TileStoreResourceLoader, for example.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No

Copy link
Contributor

Choose a reason for hiding this comment

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

What exactly are you negating here? And why?
What don't you like about my suggestion and what are the advantages of yours?


private typealias LoaderCallback<R> = (Expected<String, R>) -> Unit

internal abstract class ResourceDownloader<I, R> : ResourceLoader<I, R> {
Copy link
Contributor

Choose a reason for hiding this comment

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

Why don't you use TileStore cache? Aren't your needs met by TileStore?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

RoadShieldDownloader implicitly uses TIleStore via the utils ResourceLoader facade.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, but isn't the cache TileStore provides enough? I can assume it's not because it was not enough for voice instructions. Just want to make sure that writing our own cache above TileStore is a conscious choice.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Just want to make sure that writing our own cache above TileStore is a conscious choice.

What's that supposed to mean? First, of course, it is a conscious choice! Second, loading voice instructions don't require parsing large JSON strings with ~400 entries. Third, shields require loading two resources: a) a sprites list; b) images. Finally, the in-memory cache is necessary to avoid redundant I/O requests and computationally heavy result processing.

Copy link
Contributor

Choose a reason for hiding this comment

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

It means that we are aware that TileStore provides us with a caching mechanism but we write our own because it doesn't meet our needs.

…pdated return type to return Expected<Error, V> instead of Expected<String, V>
@codecov
Copy link

codecov bot commented Jan 17, 2023

Codecov Report

Merging #6826 (b8a6655) into main (61415ea) will decrease coverage by 0.04%.
The diff coverage is 85.29%.

Impacted file tree graph

@@             Coverage Diff              @@
##               main    #6826      +/-   ##
============================================
- Coverage     72.64%   72.61%   -0.04%     
- Complexity     5569     5600      +31     
============================================
  Files           780      783       +3     
  Lines         30104    30106       +2     
  Branches       3553     3555       +2     
============================================
- Hits          21869    21860       -9     
- Misses         6808     6815       +7     
- Partials       1427     1431       +4     
Impacted Files Coverage Δ
...ion/ui/shield/RoadShieldContentManagerContainer.kt 8.33% <0.00%> (-16.67%) ⬇️
...igation/ui/shield/internal/RoadShieldDownloader.kt 64.28% <66.66%> (ø)
...navigation/ui/shield/internal/loader/Downloader.kt 70.00% <70.00%> (ø)
...vigation/ui/shield/RoadShieldContentManagerImpl.kt 91.30% <90.74%> (-2.39%) ⬇️
.../shield/internal/loader/ShieldSpritesDownloader.kt 94.11% <94.11%> (ø)
...tion/ui/shield/internal/loader/RoadShieldLoader.kt 96.29% <96.29%> (ø)
...vigation/ui/shield/internal/loader/CachedLoader.kt 100.00% <100.00%> (ø)

@tomaszrybakiewicz
Copy link
Contributor Author

@abhishek1508 Can you review and merge this?

@abhishek1508
Copy link
Contributor

@abhishek1508 Can you review and merge this?

cc @LukasPaczos

@kmadsen kmadsen removed their request for review November 13, 2023 19:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
skip changelog Should not be added into version changelog. UI Work related to visual components, Android Auto, Camera, 3D, voice, etc.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants