public void writeGPX(@NonNull final List<String> allGeocodesIn, final Writer writer, final ProgressListener progressListener) throws IOException {
        // create a copy of the geocode list, as we need to modify it, but it might be immutable
        final List<String> allGeocodes = new ArrayList<>(allGeocodesIn);

        this.progressListener = progressListener;
        gpx.setOutput(writer);
        gpx.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);

        gpx.startDocument(StandardCharsets.UTF_8.name(), true);
        gpx.setPrefix(PREFIX_GPX, NS_GPX);
        gpx.setPrefix(PREFIX_XSI, NS_XSI);
        gpx.setPrefix(PREFIX_GROUNDSPEAK, NS_GROUNDSPEAK);
        gpx.setPrefix(PREFIX_GSAK, NS_GSAK);
        gpx.setPrefix(PREFIX_CGEO, NS_CGEO);

        gpx.startTag(NS_GPX, "gpx");
        gpx.attribute("", "version", "1.0");
        gpx.attribute("", "creator", "c:geo - http://www.cgeo.org/");
        gpx.attribute(NS_XSI, "schemaLocation", NS_GPX + " " + GPX_SCHEMA + " " + NS_GROUNDSPEAK + " " + GROUNDSPEAK_SCHEMA + " " + NS_GSAK + " " + GSAK_SCHEMA);

        // 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).
        while (!allGeocodes.isEmpty()) {
            final List<String> batch = allGeocodes.subList(0, Math.min(CACHES_PER_BATCH, allGeocodes.size()));
            exportBatch(gpx, batch);
            batch.clear();
        }

        gpx.endTag(NS_GPX, "gpx");
        gpx.endDocument();
    }
counter for exported caches, used for progress reporting
main/src/main/java/cgeo/geocaching/export/GpxSerializer.java