diff options
| author | Bananeweizen <Bananeweizen@gmx.de> | 2013-03-29 19:48:29 +0100 |
|---|---|---|
| committer | Bananeweizen <Bananeweizen@gmx.de> | 2013-03-29 19:48:29 +0100 |
| commit | 163cd60d56714f853790a89918e0a2804d1e5b47 (patch) | |
| tree | cdfdab99421302748bb75ee839d6ed4fd160c6f5 /main/src/cgeo/geocaching/export | |
| parent | f28a7a362c5975f5a1d13388920ceeb522873f9f (diff) | |
| download | cgeo-163cd60d56714f853790a89918e0a2804d1e5b47.zip cgeo-163cd60d56714f853790a89918e0a2804d1e5b47.tar.gz cgeo-163cd60d56714f853790a89918e0a2804d1e5b47.tar.bz2 | |
refactoring: improve performance of GPX export
* load batches of caches instead of single caches
* do not load all caches to avoid huge memory usage
Diffstat (limited to 'main/src/cgeo/geocaching/export')
| -rw-r--r-- | main/src/cgeo/geocaching/export/GpxExport.java | 146 |
1 files changed, 88 insertions, 58 deletions
diff --git a/main/src/cgeo/geocaching/export/GpxExport.java b/main/src/cgeo/geocaching/export/GpxExport.java index 36c8ca7..19b8518 100644 --- a/main/src/cgeo/geocaching/export/GpxExport.java +++ b/main/src/cgeo/geocaching/export/GpxExport.java @@ -38,8 +38,11 @@ import java.io.IOException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; +import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Locale; +import java.util.Set; class GpxExport extends AbstractExport { private static final SimpleDateFormat dateFormatZ = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US); @@ -47,6 +50,11 @@ class GpxExport extends AbstractExport { public static final String PREFIX_GPX = "http://www.topografix.com/GPX/1/0"; public static final String PREFIX_GROUNDSPEAK = "http://www.groundspeak.com/cache/1/0"; + /** + * During the export, only this number of geocaches is fully loaded into memory. + */ + public static final int CACHES_PER_BATCH = 100; + protected GpxExport() { super(getString(R.string.export_gpx)); } @@ -98,8 +106,9 @@ class GpxExport extends AbstractExport { } private class ExportTask extends AsyncTaskWithProgress<Void, File> { - private final List<Geocache> caches; + private final Set<String> allGeocodes = new HashSet<String>(); private final Activity activity; + private int countExported = 0; /** * Instantiates and configures the task for exporting field notes. @@ -111,7 +120,11 @@ class GpxExport extends AbstractExport { */ public ExportTask(final List<Geocache> caches, final Activity activity) { super(activity, caches.size(), getProgressTitle(), cgeoapplication.getInstance().getResources().getQuantityString(R.plurals.cache_counts, caches.size(), caches.size())); - this.caches = caches; + + // get rid of the (half loaded) caches, we will reload them as full caches during the export + for (Geocache geocache : caches) { + allGeocodes.add(geocache.getGeocode()); + } this.activity = activity; } @@ -144,68 +157,24 @@ class GpxExport extends AbstractExport { PREFIX_GPX + " http://www.topografix.com/GPX/1/0/gpx.xsd " + PREFIX_GROUNDSPEAK + " http://www.groundspeak.com/cache/1/0/1/cache.xsd"); - for (int i = 0; i < caches.size(); i++) { - final Geocache cache = cgData.loadCache(caches.get(i).getGeocode(), LoadFlags.LOAD_ALL_DB_ONLY); + // Split the overall set of geocodes into small chunks. That is a compromise between memory efficiency (because + // we don't load all caches fully into memory) and speed (because we don't query each cache separately). - gpx.startTag(PREFIX_GPX, "wpt"); - gpx.attribute("", "lat", Double.toString(cache.getCoords().getLatitude())); - gpx.attribute("", "lon", Double.toString(cache.getCoords().getLongitude())); - - final Date hiddenDate = cache.getHiddenDate(); - if (hiddenDate != null) { - XmlUtils.simpleText(gpx, PREFIX_GPX, "time", dateFormatZ.format(hiddenDate)); + while (!allGeocodes.isEmpty()) { + int cachesInBatch = 0; + HashSet<String> geocodesOfBatch = new HashSet<String>(CACHES_PER_BATCH); + for (Iterator<String> iterator = allGeocodes.iterator(); iterator.hasNext() && cachesInBatch < CACHES_PER_BATCH;) { + final String geocode = iterator.next(); + geocodesOfBatch.add(geocode); + cachesInBatch++; } - - XmlUtils.multipleTexts(gpx, PREFIX_GPX, - "name", cache.getGeocode(), - "desc", cache.getName(), - "url", cache.getUrl(), - "urlname", cache.getName(), - "sym", cache.isFound() ? "Geocache Found" : "Geocache", - "type", "Geocache|" + cache.getType().pattern); - - gpx.startTag(PREFIX_GROUNDSPEAK, "cache"); - gpx.attribute("", "id", cache.getCacheId()); - gpx.attribute("", "available", !cache.isDisabled() ? "True" : "False"); - gpx.attribute("", "archives", cache.isArchived() ? "True" : "False"); - - XmlUtils.multipleTexts(gpx, PREFIX_GROUNDSPEAK, - "name", cache.getName(), - "placed_by", cache.getOwnerDisplayName(), - "owner", cache.getOwnerUserId(), - "type", cache.getType().pattern, - "container", cache.getSize().id, - "difficulty", Float.toString(cache.getDifficulty()), - "terrain", Float.toString(cache.getTerrain()), - "country", cache.getLocation(), - "state", "", - "encoded_hints", cache.getHint()); - - writeAttributes(gpx, cache); - - gpx.startTag(PREFIX_GROUNDSPEAK, "short_description"); - gpx.attribute("", "html", BaseUtils.containsHtml(cache.getShortDescription()) ? "True" : "False"); - gpx.text(cache.getShortDescription()); - gpx.endTag(PREFIX_GROUNDSPEAK, "short_description"); - - gpx.startTag(PREFIX_GROUNDSPEAK, "long_description"); - gpx.attribute("", "html", BaseUtils.containsHtml(cache.getDescription()) ? "True" : "False"); - gpx.text(cache.getDescription()); - gpx.endTag(PREFIX_GROUNDSPEAK, "long_description"); - - writeLogs(gpx, cache); - - gpx.endTag(PREFIX_GROUNDSPEAK, "cache"); - gpx.endTag(PREFIX_GPX, "wpt"); - - writeWaypoints(gpx, cache); - - publishProgress(i + 1); + allGeocodes.removeAll(geocodesOfBatch); + exportBatch(gpx, geocodesOfBatch); } gpx.endTag(PREFIX_GPX, "gpx"); gpx.endDocument(); - } catch (final IOException e) { + } catch (final Exception e) { Log.e("GpxExport.ExportTask export", e); if (writer != null) { @@ -226,6 +195,67 @@ class GpxExport extends AbstractExport { return exportFile; } + private void exportBatch(final XmlSerializer gpx, HashSet<String> geocodesOfBatch) throws IOException { + Set<Geocache> caches = cgData.loadCaches(geocodesOfBatch, LoadFlags.LOAD_ALL_DB_ONLY); + for (Geocache cache : caches) { + gpx.startTag(PREFIX_GPX, "wpt"); + gpx.attribute("", "lat", Double.toString(cache.getCoords().getLatitude())); + gpx.attribute("", "lon", Double.toString(cache.getCoords().getLongitude())); + + final Date hiddenDate = cache.getHiddenDate(); + if (hiddenDate != null) { + XmlUtils.simpleText(gpx, PREFIX_GPX, "time", dateFormatZ.format(hiddenDate)); + } + + XmlUtils.multipleTexts(gpx, PREFIX_GPX, + "name", cache.getGeocode(), + "desc", cache.getName(), + "url", cache.getUrl(), + "urlname", cache.getName(), + "sym", cache.isFound() ? "Geocache Found" : "Geocache", + "type", "Geocache|" + cache.getType().pattern); + + gpx.startTag(PREFIX_GROUNDSPEAK, "cache"); + gpx.attribute("", "id", cache.getCacheId()); + gpx.attribute("", "available", !cache.isDisabled() ? "True" : "False"); + gpx.attribute("", "archives", cache.isArchived() ? "True" : "False"); + + XmlUtils.multipleTexts(gpx, PREFIX_GROUNDSPEAK, + "name", cache.getName(), + "placed_by", cache.getOwnerDisplayName(), + "owner", cache.getOwnerUserId(), + "type", cache.getType().pattern, + "container", cache.getSize().id, + "difficulty", Float.toString(cache.getDifficulty()), + "terrain", Float.toString(cache.getTerrain()), + "country", cache.getLocation(), + "state", "", + "encoded_hints", cache.getHint()); + + writeAttributes(gpx, cache); + + gpx.startTag(PREFIX_GROUNDSPEAK, "short_description"); + gpx.attribute("", "html", BaseUtils.containsHtml(cache.getShortDescription()) ? "True" : "False"); + gpx.text(cache.getShortDescription()); + gpx.endTag(PREFIX_GROUNDSPEAK, "short_description"); + + gpx.startTag(PREFIX_GROUNDSPEAK, "long_description"); + gpx.attribute("", "html", BaseUtils.containsHtml(cache.getDescription()) ? "True" : "False"); + gpx.text(cache.getDescription()); + gpx.endTag(PREFIX_GROUNDSPEAK, "long_description"); + + writeLogs(gpx, cache); + + gpx.endTag(PREFIX_GROUNDSPEAK, "cache"); + gpx.endTag(PREFIX_GPX, "wpt"); + + writeWaypoints(gpx, cache); + + countExported++; + publishProgress(countExported); + } + } + private void writeWaypoints(final XmlSerializer gpx, final Geocache cache) throws IOException { List<Waypoint> waypoints = cache.getWaypoints(); List<Waypoint> ownWaypoints = new ArrayList<Waypoint>(waypoints.size()); |
