forked from upstream-mirrors/OpenTracks
REAL performance improvement in importer - using a bulk insertion
This commit is contained in:
@@ -167,24 +167,25 @@ public class MyTracksProvider extends ContentProvider {
|
||||
|
||||
@Override
|
||||
public int delete(Uri url, String where, String[] selectionArgs) {
|
||||
if (urlMatcher.match(url) == TRACKPOINTS) {
|
||||
Log.w(MyTracksProvider.TAG, "provider trackpoints delete!");
|
||||
int count = db.delete(TRACKPOINTS_TABLE, where, selectionArgs);
|
||||
getContext().getContentResolver().notifyChange(url, null, true);
|
||||
return count;
|
||||
} else if (urlMatcher.match(url) == TRACKS) {
|
||||
Log.w(MyTracksProvider.TAG, "provider track delete!");
|
||||
int count = db.delete(TRACKS_TABLE, where, selectionArgs);
|
||||
getContext().getContentResolver().notifyChange(url, null, true);
|
||||
return count;
|
||||
} else if (urlMatcher.match(url) == WAYPOINTS) {
|
||||
Log.w(MyTracksProvider.TAG, "provider waypoint delete!");
|
||||
int count = db.delete(WAYPOINTS_TABLE, where, selectionArgs);
|
||||
getContext().getContentResolver().notifyChange(url, null, true);
|
||||
return count;
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unknown URL " + url);
|
||||
String table;
|
||||
switch (urlMatcher.match(url)) {
|
||||
case TRACKPOINTS:
|
||||
table = TRACKPOINTS_TABLE;
|
||||
break;
|
||||
case TRACKS:
|
||||
table = TRACKS_TABLE;
|
||||
break;
|
||||
case WAYPOINTS:
|
||||
table = WAYPOINTS_TABLE;
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown URL " + url);
|
||||
}
|
||||
|
||||
Log.w(MyTracksProvider.TAG, "provider delete in " + table + "!");
|
||||
int count = db.delete(table, where, selectionArgs);
|
||||
getContext().getContentResolver().notifyChange(url, null, true);
|
||||
return count;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -216,19 +217,49 @@ public class MyTracksProvider extends ContentProvider {
|
||||
} else {
|
||||
values = new ContentValues();
|
||||
}
|
||||
if (urlMatcher.match(url) == TRACKPOINTS) {
|
||||
return insertTrackPoint(url, values);
|
||||
} else if (urlMatcher.match(url) == TRACKS) {
|
||||
return insertTrack(url, values);
|
||||
} else if (urlMatcher.match(url) == WAYPOINTS) {
|
||||
return insertWaypoint(url, values);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unknown URL " + url);
|
||||
|
||||
int urlMatchType = urlMatcher.match(url);
|
||||
return insertType(url, urlMatchType, values);
|
||||
}
|
||||
|
||||
private Uri insertType(Uri url, int urlMatchType, ContentValues values) {
|
||||
switch (urlMatchType) {
|
||||
case TRACKPOINTS:
|
||||
return insertTrackPoint(url, values);
|
||||
case TRACKS:
|
||||
return insertTrack(url, values);
|
||||
case WAYPOINTS:
|
||||
return insertWaypoint(url, values);
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown URL " + url);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int bulkInsert(Uri url, ContentValues[] valuesBulk) {
|
||||
Log.d(MyTracksProvider.TAG, "MyTracksProvider.bulkInsert");
|
||||
int numInserted = 0;
|
||||
try {
|
||||
// Use a transaction in order to make the insertions run as a single batch
|
||||
db.beginTransaction();
|
||||
|
||||
int urlMatch = urlMatcher.match(url);
|
||||
for (numInserted = 0; numInserted < valuesBulk.length; numInserted++) {
|
||||
ContentValues values = valuesBulk[numInserted];
|
||||
if (values == null) { values = new ContentValues(); }
|
||||
|
||||
insertType(url, urlMatch, values);
|
||||
}
|
||||
|
||||
db.setTransactionSuccessful();
|
||||
} finally {
|
||||
db.endTransaction();
|
||||
}
|
||||
|
||||
return numInserted;
|
||||
}
|
||||
|
||||
private Uri insertTrackPoint(Uri url, ContentValues values) {
|
||||
Log.d(MyTracksProvider.TAG, "MyTracksProvider.insertTrackPoint");
|
||||
boolean hasLat = values.containsKey(TrackPointsColumns.LATITUDE);
|
||||
boolean hasLong = values.containsKey(TrackPointsColumns.LONGITUDE);
|
||||
boolean hasTime = values.containsKey(TrackPointsColumns.TIME);
|
||||
|
||||
@@ -232,6 +232,17 @@ public interface MyTracksProviderUtils {
|
||||
*/
|
||||
Uri insertTrackPoint(Location location, long trackId);
|
||||
|
||||
/**
|
||||
* Inserts multiple track points in a single operation.
|
||||
*
|
||||
* @param locations an array of locations to insert
|
||||
* @param length the number of locations (from the beginning of the array)
|
||||
* to actually insert, or -1 for all of them
|
||||
* @param trackId the ID of the track to insert the points into
|
||||
* @return the number of points inserted
|
||||
*/
|
||||
int bulkInsertTrackPoints(Location[] locations, int length, long trackId);
|
||||
|
||||
/**
|
||||
* Inserts a waypoint in the provider.
|
||||
*
|
||||
@@ -262,7 +273,7 @@ public interface MyTracksProviderUtils {
|
||||
* @param cursor a cursor pointing at a db or provider with locations
|
||||
* @return a new location object
|
||||
*/
|
||||
public Location createLocation(Cursor cursor);
|
||||
Location createLocation(Cursor cursor);
|
||||
|
||||
/**
|
||||
* Creates a waypoint object from a given cursor.
|
||||
@@ -270,7 +281,7 @@ public interface MyTracksProviderUtils {
|
||||
* @param cursor a cursor pointing at a db or provider with waypoints.
|
||||
* @return a new waypoint object
|
||||
*/
|
||||
public Waypoint createWaypoint(Cursor cursor);
|
||||
Waypoint createWaypoint(Cursor cursor);
|
||||
|
||||
/**
|
||||
* A factory which can produce instances of {@link MyTracksProviderUtils},
|
||||
|
||||
+14
-2
@@ -840,8 +840,6 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
|
||||
Cursor cursor = getLocationsCursor(track.getId(),
|
||||
startingPoint,
|
||||
buffer.getSize(), false);
|
||||
final int idColumnIdx =
|
||||
cursor.getColumnIndexOrThrow(TrackPointsColumns._ID);
|
||||
if (cursor == null) {
|
||||
Log.w(MyTracksProvider.TAG, "Cannot get a locations cursor!");
|
||||
buffer.setInvalid();
|
||||
@@ -860,6 +858,8 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
|
||||
return;
|
||||
}
|
||||
|
||||
final int idColumnIdx =
|
||||
cursor.getColumnIndexOrThrow(TrackPointsColumns._ID);
|
||||
do {
|
||||
Location location = createLocation(cursor);
|
||||
if (location == null) {
|
||||
@@ -900,6 +900,18 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
|
||||
createContentValues(location, trackId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int bulkInsertTrackPoints(Location[] locations, int length, long trackId) {
|
||||
if (length == -1) { length = locations.length; }
|
||||
|
||||
ContentValues[] values = new ContentValues[length];
|
||||
for (int i = 0; i < length; i++) {
|
||||
values[i] = createContentValues(locations[i], trackId);
|
||||
}
|
||||
|
||||
return context.getContentResolver().bulkInsert(TrackPointsColumns.CONTENT_URI, values);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Uri insertWaypoint(Waypoint waypoint) {
|
||||
Log.d(MyTracksProvider.TAG, "MyTracksProviderUtilsImpl.insertWaypoint");
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package com.google.android.apps.mytracks.io;
|
||||
|
||||
import com.google.android.apps.mytracks.MyTracksConstants;
|
||||
import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
|
||||
import com.google.android.apps.mytracks.content.Track;
|
||||
import com.google.android.apps.mytracks.stats.TripStatisticsBuilder;
|
||||
@@ -23,6 +24,7 @@ import com.google.android.apps.mytracks.util.MyTracksUtils;
|
||||
import android.location.Location;
|
||||
import android.location.LocationManager;
|
||||
import android.net.Uri;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
@@ -52,7 +54,7 @@ import org.xml.sax.helpers.DefaultHandler;
|
||||
*/
|
||||
public class GpxImporter extends DefaultHandler {
|
||||
|
||||
/**
|
||||
/*
|
||||
* Different date formats used in GPX files
|
||||
*/
|
||||
static final SimpleDateFormat DATE_FORMAT1 = new SimpleDateFormat(
|
||||
@@ -63,7 +65,7 @@ public class GpxImporter extends DefaultHandler {
|
||||
"yyyy-MM-dd'T'HH:mm:ss.SSSZ");
|
||||
static final SimpleTimeZone UTC_TIMEZONE = new SimpleTimeZone(0, "UTC");
|
||||
|
||||
/**
|
||||
/*
|
||||
* GPX-XML tag names and attributes.
|
||||
*/
|
||||
private static final String TAG_TRACK = "trk";
|
||||
@@ -76,6 +78,14 @@ public class GpxImporter extends DefaultHandler {
|
||||
private static final String ATT_LAT = "lat";
|
||||
private static final String ATT_LON = "lon";
|
||||
|
||||
/**
|
||||
* The maximum number of locations to buffer for bulk-insertion into the database.
|
||||
*/
|
||||
private static final int MAX_BUFFERED_LOCATIONS = 512;
|
||||
|
||||
/**
|
||||
* Utilities for accessing the contnet provider.
|
||||
*/
|
||||
private final MyTracksProviderUtils providerUtils;
|
||||
|
||||
/**
|
||||
@@ -98,12 +108,6 @@ public class GpxImporter extends DefaultHandler {
|
||||
* Previous location, required for calculations.
|
||||
*/
|
||||
private Location lastLocation;
|
||||
|
||||
/**
|
||||
* URI of the last point inserted into the database.
|
||||
* We parse point IDs out of this when necessary.
|
||||
*/
|
||||
private Uri lastPointIdUri;
|
||||
|
||||
/**
|
||||
* Currently reading track.
|
||||
@@ -115,6 +119,16 @@ public class GpxImporter extends DefaultHandler {
|
||||
*/
|
||||
private TripStatisticsBuilder statsBuilder;
|
||||
|
||||
/**
|
||||
* Buffer of locations to be bulk-inserted into the database.
|
||||
*/
|
||||
private Location[] bufferedPointInserts = new Location[MAX_BUFFERED_LOCATIONS];
|
||||
|
||||
/**
|
||||
* Number of locations buffered to be inserted into the database.
|
||||
*/
|
||||
private int numBufferedPointInserts = 0;
|
||||
|
||||
/**
|
||||
* Number of locations already processed.
|
||||
*/
|
||||
@@ -163,7 +177,13 @@ public class GpxImporter extends DefaultHandler {
|
||||
long[] trackIds = null;
|
||||
|
||||
try {
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
parser.parse(is, handler);
|
||||
|
||||
long end = System.currentTimeMillis();
|
||||
Log.d(MyTracksConstants.TAG, "Total import time: " + (end - start) + "ms");
|
||||
|
||||
trackIds = handler.getImportedTrackIds();
|
||||
} catch (SAXException e) {
|
||||
throw e;
|
||||
@@ -182,7 +202,7 @@ public class GpxImporter extends DefaultHandler {
|
||||
this.providerUtils = providerUtils;
|
||||
tracksWritten = new ArrayList<Long>();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void characters(char[] ch, int start, int length) throws SAXException {
|
||||
String newContent = new String(ch, start, length);
|
||||
@@ -333,7 +353,7 @@ public class GpxImporter extends DefaultHandler {
|
||||
statsBuilder.addLocation(location, location.getTime());
|
||||
|
||||
// insert in db
|
||||
lastPointIdUri = providerUtils.insertTrackPoint(location, track.getId());
|
||||
insertTrackPoint(location);
|
||||
|
||||
// first track point?
|
||||
if (lastLocation == null) {
|
||||
@@ -349,7 +369,23 @@ public class GpxImporter extends DefaultHandler {
|
||||
throw new SAXException(msg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected void insertTrackPoint(Location loc) {
|
||||
bufferedPointInserts[numBufferedPointInserts] = loc;
|
||||
numBufferedPointInserts++;
|
||||
|
||||
if (numBufferedPointInserts >= MAX_BUFFERED_LOCATIONS) {
|
||||
flushPointInserts();
|
||||
}
|
||||
}
|
||||
|
||||
private void flushPointInserts() {
|
||||
if (numBufferedPointInserts <= 0) { return; }
|
||||
|
||||
providerUtils.bulkInsertTrackPoints(bufferedPointInserts, numBufferedPointInserts, track.getId());
|
||||
numBufferedPointInserts = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Track segment finished.
|
||||
*/
|
||||
@@ -363,6 +399,8 @@ public class GpxImporter extends DefaultHandler {
|
||||
*/
|
||||
private void onTrackElementEnd() {
|
||||
if (lastLocation != null) {
|
||||
flushPointInserts();
|
||||
|
||||
// Calculate statistics for the imported track and update
|
||||
statsBuilder.pauseAt(lastLocation.getTime());
|
||||
track.setStopId(getLastPointId());
|
||||
@@ -493,7 +531,9 @@ public class GpxImporter extends DefaultHandler {
|
||||
* Returns the ID of the last point inserted into the database.
|
||||
*/
|
||||
private long getLastPointId() {
|
||||
return Long.parseLong(lastPointIdUri.getLastPathSegment());
|
||||
flushPointInserts();
|
||||
|
||||
return providerUtils.getLastLocationId(track.getId());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user