android-maps-utils icon indicating copy to clipboard operation
android-maps-utils copied to clipboard

App freezes when loading KML layer

Open sharkboy777 opened this issue 4 years ago • 26 comments

I am trying to add KML layer on to the maps. It was working okay with just a ms of freeze when i try to load the small size KML file. But some files are large with size more than 1Mb - 10Mb.. in these all cases app is getting frozen for some time while the layer is adding to the map.

Below is the Asynctask class i am using to read the kml file from local storage path and adding it to maps.

I tried this in many ways using handlers, threads, asynctasks... as i cannot update the UI in background thread... this is causing my app to freeze for some time.

Eventhough it freezes I wouldn't mind but in some device while the app is frozen if the user interacts with the app then app is getting crashed. In some devices it is displaying "wait" dialog.

Please help me out.

` private static class AddKMLLayerToMaps extends AsyncTask<String, String, KmlLayer> {

    //added weakreference to avoid memory leaks
    private WeakReference<CoverageFragment> weakReferencedFragment;

    AddKMLLayerToMaps(CoverageFragment reference) {

        this.weakReferencedFragment = new WeakReference<>(reference);

        weakReferencedFragment.get().showLoading();
    }

    @Override
    protected KmlLayer doInBackground(String... strings) {

        try {

            TraceUtils.logE("Coverage kml Path", strings[0]);

            FileInputStream fileInputStream = new FileInputStream(strings[0]);

            CoverageFragment fragment = weakReferencedFragment.get();
            if (fragment == null || fragment.getActivity() == null || fragment.getActivity().isFinishing()) {
                return null;
            }

            KmlLayer kmlLayer = new KmlLayer(fragment.mMap, fileInputStream, getApplicationContext());

            fileInputStream.close();

            File file = new File(strings[0]);
            if (file.exists()) file.delete();

            return kmlLayer;

        } catch (Exception e) {

            weakReferencedFragment.get().hideLoading();

            TraceUtils.logException(e);

        }

        return null;
    }

    @Override
    protected void onPostExecute(KmlLayer kmlLayer) {

        super.onPostExecute(kmlLayer);

        if (kmlLayer != null) {

            //add the KML layer to map using the UI thread
            weakReferencedFragment.get().mActivity.runOnUiThread(() -> {
                try {

                    kmlLayer.addLayerToMap();

                } catch (Exception e) {

                    weakReferencedFragment.get().hideLoading();

                    TraceUtils.logException(e);

                }
            });

            //zoom to the center of KML layer containers
            new MoveMapToKMLLayer(weakReferencedFragment.get()).execute(kmlLayer);
        }
    }

}`

sharkboy777 avatar Nov 15 '19 07:11 sharkboy777

@sharkboy777 Could you please provide a sample KML file that you're having this problem with?

barbeau avatar Nov 21 '19 14:11 barbeau

@sharkboy777 Could you please provide a sample KML file that you're having this problem with? https://drive.google.com/file/d/1sbNgwgLqdmKbJR3nnOxpp4ZTSa7DWRFl/view?usp=sharing

Here is the drive link for kml file... please give me a solution

sharkboy777 avatar Nov 22 '19 11:11 sharkboy777

@sharkboy777 Could you please provide a sample KML file that you're having this problem with? https://drive.google.com/file/d/1sbNgwgLqdmKbJR3nnOxpp4ZTSa7DWRFl/view?usp=sharing

Here is the drive link for kml file... please give me a solution

any update on this??

sharkboy777 avatar Nov 28 '19 06:11 sharkboy777

Got anything on this issue?? Or atleast before adding the kml layer onto the maps, is it possible to get renderer object, to parse the KML data and get the polygons?

Why because, right now it seems to be not possible to get the renderer object before adding the KML layer onto maps.

sharkboy777 avatar Dec 16 '19 06:12 sharkboy777

@sharkboy777 I haven't had time to look at this

barbeau avatar Dec 16 '19 14:12 barbeau

I CPU profiled loading this KML and the bottleneck is calling GoogleMap.addPolygon(). It calls the method 9377 times, since there's so many polygons in the 20 KMLs embedded in this single KML. Google Maps on the web won't even load KMLs over 5MB, so I couldn't compare the performance there. Google Earth does do a good job loading and displaying this KML in a web browser fairly quickly though. Any performance optimization for loading this KML on Android would have to be done in the GoogleMap addPolygon() method, which isn't part of the android-maps-utils library.

I'd suggest splitting this KML into 20 smaller KMLs that can be loaded and displayed individually. Even if you display them all at once, you could at least add them to the map on separate main thread run loops to avoid freezing the UI for as long.

jeffdgr8 avatar Jan 26 '20 11:01 jeffdgr8

I CPU profiled loading this KML and the bottleneck is calling GoogleMap.addPolygon(). It calls the method 9377 times, since there's so many polygons in the 20 KMLs embedded in this single KML. Google Maps on the web won't even load KMLs over 5MB, so I couldn't compare the performance there. Google Earth does do a good job loading and displaying this KML in a web browser fairly quickly though. Any performance optimization for loading this KML on Android would have to be done in the GoogleMap addPolygon() method, which isn't part of the android-maps-utils library.

I'd suggest splitting this KML into 20 smaller KMLs that can be loaded and displayed individually. Even if you display them all at once, you could at least add them to the map on separate main thread run loops to avoid freezing the UI for as long.

afdfd

I CPU profiled loading this KML and the bottleneck is calling GoogleMap.addPolygon(). It calls the method 9377 times, since there's so many polygons in the 20 KMLs embedded in this single KML. Google Maps on the web won't even load KMLs over 5MB, so I couldn't compare the performance there. Google Earth does do a good job loading and displaying this KML in a web browser fairly quickly though. Any performance optimization for loading this KML on Android would have to be done in the GoogleMap addPolygon() method, which isn't part of the android-maps-utils library.

I'd suggest splitting this KML into 20 smaller KMLs that can be loaded and displayed individually. Even if you display them all at once, you could at least add them to the map on separate main thread run loops to avoid freezing the UI for as long.

  • The problem here is that, I can't even access the KML Layer object without rendering it on the map first(which already takes an ample amount of time). If I could do that then it will be easier for me to retrieve the polygons from each kml file and draw them on map without rendering the entire kml file on maps

  • There is no way for me to get 20 kml files instead of single kml file from the server. I will receive these single KML files(with more kmls in it) like above

  • Is there any way to get the KMLLayer or KMLRenderer object without actually rendering it on the maps first?

sharkboy777 avatar Jan 27 '20 06:01 sharkboy777

KmlLayer is created before calling addLayerToMap(), which adds the polygons to the map. Constructing KmlLayer is also fairly long-running, as it takes a good amount of I/O and processing to parse such a large KML. Constructing the KmlLayer can be done on a background thread though, avoiding blocking the UI. But addLayerToMap() has to be called on the main thread, which is why it causes freezing to add such a large KML to the map.

If you're unable to edit the KML and break it up into multiple smaller files, I'd suggest still making sure you at least are constructing the KmlLayer on a background thread. addLayerToMap() has to be done on the main thread though.

For reference, I updated the KmlDemoActivity to construct the KmlLayer in the background in https://github.com/googlemaps/android-maps-utils/pull/607.

jeffdgr8 avatar Jan 28 '20 16:01 jeffdgr8

CPU profiling identifies GoogleMap.addPolygon() as the bottleneck adding KmlLayer to the map. Within this method, the bottleneck is ArrayList.sort(), called in a couple places by Collections.sort().

The long-running KmlLayer.<init> can all be done on a background thread. But the KmlLayer.addToMap() has to be done on the main thread.

Screenshot 2020-01-27 at 5 54 00 PM

@jpoehnelt @arriolac hard to know exactly what could be optimized in the closed-source GoogleMap code, but the GoogleMap.addPolygon() method could likely be optimized significantly by utilizing a collection type that provides more efficient sorting or if somehow that sorting could be limited, avoided, or done off of the main thread.

jeffdgr8 avatar Jan 28 '20 17:01 jeffdgr8

@jeffdgr8 Have you tried profiling the library with the Android Maps v3 beta SDK? https://github.com/googlemaps/android-maps-utils/commits/android-sdk-v3-beta

We have a similar bottleneck for clustering when adding markers with Maps API v2, and slow performance adding markers was called out as fixed in the Android v3 beta SDK - see https://github.com/googlemaps/android-maps-utils/issues/164#issuecomment-549857232. Not sure if this fix also addresses slow performance adding polygons, but 🤞 if it was just a simple Collection sorting issue.

barbeau avatar Jan 28 '20 18:01 barbeau

@barbeau I actually did pull the maps v3 branch to test and ran into this crash again on my Pixel Slate. I'll try profiling again on my phone. I also wanted to merge master into the android-sdk-v3-beta branch, which has conflicts.

I was happy to see that performance issue fixed in v3, as I was tracking it closely. There hasn't been much movement on the v3 beta since it was first announced last spring, not even an iterative beta release with that crash fixed.

jeffdgr8 avatar Jan 28 '20 21:01 jeffdgr8

I profiled with the maps v3 beta (https://github.com/googlemaps/android-maps-utils/pull/608) and actually couldn't get the KML to display on the map at all. The app hung for a few minutes with the main thread blocked and eventually crashed.

2020-01-28 15:45:03.596 13775-13816/com.google.maps.android.utils.demo E/System: java.lang.OutOfMemoryError: OutOfMemoryError thrown while trying to throw an exception; no stack trace available
2020-01-28 15:44:31.912 589-589/? A/libc: crash_dump helper failed to exec
2020-01-28 15:45:04.379 13775-13882/? E/AndroidRuntime: Error reporting crash
    java.lang.OutOfMemoryError: OutOfMemoryError thrown while trying to throw OutOfMemoryError; no stack trace available

Other smaller KML files worked as expected.

jeffdgr8 avatar Jan 28 '20 22:01 jeffdgr8

@jeffdgr8 Thanks for checking that out. So is that actually a regression from Maps SDK v2? Would Maps SDK v2 eventually load if you gave it enough time?

barbeau avatar Jan 28 '20 22:01 barbeau

@barbeau yes, with the v2 SDK it takes 10-15 seconds to add the polygons and display on the map. So this is a regression in v3. I created a bug report.

jeffdgr8 avatar Jan 28 '20 23:01 jeffdgr8

KmlLayer is created before calling addLayerToMap(), which adds the polygons to the map. Constructing KmlLayer is also fairly long-running, as it takes a good amount of I/O and processing to parse such a large KML. Constructing the KmlLayer can be done on a background thread though, avoiding blocking the UI. But addLayerToMap() has to be called on the main thread, which is why it causes freezing to add such a large KML to the map.

If you're unable to edit the KML and break it up into multiple smaller files, I'd suggest still making sure you at least are constructing the KmlLayer on a background thread. addLayerToMap() has to be done on the main thread though.

For reference, I updated the KmlDemoActivity to construct the KmlLayer in the background in #607.

yes.. for now i am getting the kmllayer object on a background thread and then adding the layertomap using main ui thread.

There is another issue in this same kml file, which is it doesn't load the styles but just loads the plain black polygon lines without any fill colors. Raised this issue in the below ticket. https://github.com/googlemaps/android-maps-utils/issues/583

Because of the above issue, I had to get all polygon objects from the rendered kmllayer and draw them on maps again using polygon styles to get the output. I don't know exactly if it is the issue with map-utils library or the kml file itself

sharkboy777 avatar Jan 30 '20 18:01 sharkboy777

@barbeau yes, with the v2 SDK it takes 10-15 seconds to add the polygons and display on the map. So this is a regression in v3. I created a bug report.

@jeffdgr8 , @barbeau Thank you guys for helping out with the issue and reporting it to google. Hope they fix it soon😊😊

sharkboy777 avatar Jan 30 '20 18:01 sharkboy777

This issue has been automatically marked as stale because it has not had recent activity. Please comment here if it is still valid so that we can reprioritize. Thank you!

stale[bot] avatar May 29 '20 21:05 stale[bot]

Not stale.

arriolac avatar Jul 15 '20 19:07 arriolac

This issue has been automatically marked as stale because it has not had recent activity. Please comment here if it is still valid so that we can reprioritize. Thank you!

stale[bot] avatar Nov 13 '20 01:11 stale[bot]

Not stale.

barbeau avatar Nov 13 '20 14:11 barbeau

This issue has been automatically marked as stale because it has not had recent activity. Please comment here if it is still valid so that we can reprioritize. Thank you!

stale[bot] avatar Mar 19 '21 23:03 stale[bot]

Not stale

jeffdgr8 avatar Mar 20 '21 17:03 jeffdgr8

Still useful

obete avatar May 15 '21 07:05 obete

This issue has been automatically marked as stale because it has not had recent activity. Please comment here if it is still valid so that we can reprioritize. Thank you!

stale[bot] avatar Sep 19 '21 19:09 stale[bot]

Not stale

jeffdgr8 avatar Sep 19 '21 20:09 jeffdgr8

This issue has been automatically marked as stale because it has not had recent activity. Please comment here if it is still valid so that we can reprioritize. Thank you!

stale[bot] avatar Apr 16 '22 03:04 stale[bot]

Closing this. Please reopen if you believe it should be addressed. Thank you for your contribution.

stale[bot] avatar Nov 02 '22 01:11 stale[bot]

Not Stale, Still m facing this issue with latest sdk in 2024

mohsin-ka avatar Mar 12 '24 07:03 mohsin-ka