mapbox / mapbox-navigation-android

Mapbox Navigation SDK for Android
https://docs.mapbox.com/android/navigation/overview/
Other
621 stars 319 forks source link

Crash when using InstructionLoader #1834

Closed clj0020 closed 5 years ago

clj0020 commented 5 years ago

Android API: Navigation Mapbox Navigation SDK version: 0.33.1

Steps to trigger behavior

  1. Put textview and banner instruction into InstructionLoader constructor in onMilestoneEvent when milestone is a BannerInstructionMilestone.
  2. Call loadInstruction()
  3. Crash due to bannerShieldList not being initialized in the ImageCreator class, this is happening when there is shield images and when there aren't any.

Expected behavior

TextView updated with step instruction.

Actual behavior

Crash when ImageCreator calls hasImages and also when ImageCreator calls addShieldInfo().

Code

    override fun onMilestoneEvent(routeProgress: RouteProgress?, instruction: String?, milestone: Milestone?) {
        if (milestone is BannerInstructionMilestone) {
            val primaryInstruction = milestone.bannerInstructions.primary()
            primaryInstruction?.let {primary ->
                // Load instruction into Direction TextView.
                val loader = InstructionLoader(textDirection, primary)
                loader.loadInstruction()
            }
        }
    }

Stacktrace

When no Shields are present.

java.lang.NullPointerException: Attempt to invoke interface method 'boolean java.util.List.isEmpty()' on a null object reference
        at com.mapbox.services.android.navigation.ui.v5.instruction.ImageCreator.hasImages(ImageCreator.java:166)
        at com.mapbox.services.android.navigation.ui.v5.instruction.ImageCreator.loadImages(ImageCreator.java:116)
        at com.mapbox.services.android.navigation.ui.v5.instruction.ImageCreator.postProcess(ImageCreator.java:213)
        at com.mapbox.services.android.navigation.ui.v5.instruction.BannerComponentTree.loadInstruction(BannerComponentTree.java:70)
        at com.mapbox.services.android.navigation.ui.v5.instruction.InstructionLoader.loadInstruction(InstructionLoader.java:49)
        at com.routematch.drivebyrm.drivebyroutematch.ui.map.MapFragment.onMilestoneEvent(MapFragment.kt:296)
        at com.mapbox.services.android.navigation.v5.navigation.NavigationEventDispatcher.onMilestoneEvent(NavigationEventDispatcher.java:158)
        at com.mapbox.services.android.navigation.v5.navigation.RouteProcessorThreadListener.onMilestoneTrigger(RouteProcessorThreadListener.java:46)
        at com.mapbox.services.android.navigation.v5.navigation.RouteProcessorRunnable$1.run(RouteProcessorRunnable.java:137)
        at android.os.Handler.handleCallback(Handler.java:873)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at android.os.Looper.loop(Looper.java:193)
        at android.app.ActivityThread.main(ActivityThread.java:6718)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)

When Shields are present.

java.lang.NullPointerException: Attempt to invoke interface method 'boolean java.util.List.add(java.lang.Object)' on a null object reference
        at com.mapbox.services.android.navigation.ui.v5.instruction.ImageCreator.addShieldInfo(ImageCreator.java:58)
        at com.mapbox.services.android.navigation.ui.v5.instruction.ImageCreator.setupNode(ImageCreator.java:46)
        at com.mapbox.services.android.navigation.ui.v5.instruction.BannerComponentTree.parseBannerComponents(BannerComponentTree.java:41)
        at com.mapbox.services.android.navigation.ui.v5.instruction.BannerComponentTree.<init>(BannerComponentTree.java:23)
        at com.mapbox.services.android.navigation.ui.v5.instruction.InstructionLoader.<init>(InstructionLoader.java:35)
        at com.routematch.drivebyrm.drivebyroutematch.ui.map.MapFragment.onMilestoneEvent(MapFragment.kt:295)
        at com.mapbox.services.android.navigation.v5.navigation.NavigationEventDispatcher.onMilestoneEvent(NavigationEventDispatcher.java:158)
        at com.mapbox.services.android.navigation.v5.navigation.RouteProcessorThreadListener.onMilestoneTrigger(RouteProcessorThreadListener.java:46)
        at com.mapbox.services.android.navigation.v5.navigation.RouteProcessorRunnable$1.run(RouteProcessorRunnable.java:137)
        at android.os.Handler.handleCallback(Handler.java:873)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at android.os.Looper.loop(Looper.java:193)
        at android.app.ActivityThread.main(ActivityThread.java:6718)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
devotaaabel commented 5 years ago

Hi @clj0020 , please see https://github.com/mapbox/mapbox-navigation-android/blob/master/libandroid-navigation-ui/src/main/java/com/mapbox/services/android/navigation/ui/v5/instruction/ImageCreator.java#L82. This static method needs to be called before loading instructions like this: ImageCreator.getInstance().initialize(getContext());.

clj0020 commented 5 years ago

Please reopen. I am using the InstructionLoader according to docs here I shouldn't need to create an instance of ImageCreator. This is an issue with InstructionLoader.

devotaaabel commented 5 years ago

@clj0020 it looks like the docs don't mention that, I'll get those updated. You can see an example use of this in the InstructionView: https://github.com/mapbox/mapbox-navigation-android/blob/master/libandroid-navigation-ui/src/main/java/com/mapbox/services/android/navigation/ui/v5/instruction/InstructionView.java#L144 . The problem that the InstructionLoader loads highway shields by default, and if you haven't called ImageCreator.initialize, Picasso won't be loaded to show the highway signs, so this is a required part of the workflow. If you'd like to be able to use the InstructionLoader without loading highway signs, you can open a feature request for that. But currently, before InstructionLoader.loadInstruction is called, ImageCreator.initialize(context) must be called once (the shield images are pre-fetched). Please let me know if that clarifies the situation.

clj0020 commented 5 years ago

@devotaaabel Thank you that helped a lot. It was the docs that were confusing me. Maybe InstructionLoader should initialize ImageCreator in it's constructor so that it requires less lines to use.