Merge "Make Location objects take less memory."

This commit is contained in:
David Christie
2015-10-28 01:27:37 +00:00
committed by Android (Google) Code Review
2 changed files with 150 additions and 100 deletions

View File

@@ -78,32 +78,51 @@ public class Location implements Parcelable {
*/
public static final String EXTRA_NO_GPS_LOCATION = "noGPSLocation";
/**
* Bit mask for mFieldsMask indicating the presence of mAltitude.
*/
private static final byte HAS_ALTITUDE_MASK = 1;
/**
* Bit mask for mFieldsMask indicating the presence of mSpeed.
*/
private static final byte HAS_SPEED_MASK = 2;
/**
* Bit mask for mFieldsMask indicating the presence of mBearing.
*/
private static final byte HAS_BEARING_MASK = 4;
/**
* Bit mask for mFieldsMask indicating the presence of mAccuracy.
*/
private static final byte HAS_ACCURACY_MASK = 8;
/**
* Bit mask for mFieldsMask indicating location is from a mock provider.
*/
private static final byte HAS_MOCK_PROVIDER_MASK = 16;
// Cached data to make bearing/distance computations more efficient for the case
// where distanceTo and bearingTo are called in sequence. Assume this typically happens
// on the same thread for caching purposes.
private static ThreadLocal<BearingDistanceCache> sBearingDistanceCache
= new ThreadLocal<BearingDistanceCache>() {
@Override
protected BearingDistanceCache initialValue() {
return new BearingDistanceCache();
}
};
private String mProvider;
private long mTime = 0;
private long mElapsedRealtimeNanos = 0;
private double mLatitude = 0.0;
private double mLongitude = 0.0;
private boolean mHasAltitude = false;
private double mAltitude = 0.0f;
private boolean mHasSpeed = false;
private float mSpeed = 0.0f;
private boolean mHasBearing = false;
private float mBearing = 0.0f;
private boolean mHasAccuracy = false;
private float mAccuracy = 0.0f;
private Bundle mExtras = null;
private boolean mIsFromMockProvider = false;
// Cache the inputs and outputs of computeDistanceAndBearing
// so calls to distanceTo() and bearingTo() can share work
private double mLat1 = 0.0;
private double mLon1 = 0.0;
private double mLat2 = 0.0;
private double mLon2 = 0.0;
private float mDistance = 0.0f;
private float mInitialBearing = 0.0f;
// Scratchpad
private final float[] mResults = new float[2];
// A bitmask of fields present in this object (see HAS_* constants defined above).
private byte mFieldsMask = 0;
/**
* Construct a new Location with a named provider.
@@ -131,18 +150,14 @@ public class Location implements Parcelable {
mProvider = l.mProvider;
mTime = l.mTime;
mElapsedRealtimeNanos = l.mElapsedRealtimeNanos;
mFieldsMask = l.mFieldsMask;
mLatitude = l.mLatitude;
mLongitude = l.mLongitude;
mHasAltitude = l.mHasAltitude;
mAltitude = l.mAltitude;
mHasSpeed = l.mHasSpeed;
mSpeed = l.mSpeed;
mHasBearing = l.mHasBearing;
mBearing = l.mBearing;
mHasAccuracy = l.mHasAccuracy;
mAccuracy = l.mAccuracy;
mExtras = (l.mExtras == null) ? null : new Bundle(l.mExtras);
mIsFromMockProvider = l.mIsFromMockProvider;
}
/**
@@ -152,18 +167,14 @@ public class Location implements Parcelable {
mProvider = null;
mTime = 0;
mElapsedRealtimeNanos = 0;
mFieldsMask = 0;
mLatitude = 0;
mLongitude = 0;
mHasAltitude = false;
mAltitude = 0;
mHasSpeed = false;
mSpeed = 0;
mHasBearing = false;
mBearing = 0;
mHasAccuracy = false;
mAccuracy = 0;
mExtras = null;
mIsFromMockProvider = false;
}
/**
@@ -297,7 +308,7 @@ public class Location implements Parcelable {
}
private static void computeDistanceAndBearing(double lat1, double lon1,
double lat2, double lon2, float[] results) {
double lat2, double lon2, BearingDistanceCache results) {
// Based on http://www.ngs.noaa.gov/PUBS_LIB/inverse.pdf
// using the "Inverse Formula" (section 4)
@@ -382,19 +393,19 @@ public class Location implements Parcelable {
}
float distance = (float) (b * A * (sigma - deltaSigma));
results[0] = distance;
if (results.length > 1) {
float initialBearing = (float) Math.atan2(cosU2 * sinLambda,
cosU1 * sinU2 - sinU1 * cosU2 * cosLambda);
initialBearing *= 180.0 / Math.PI;
results[1] = initialBearing;
if (results.length > 2) {
float finalBearing = (float) Math.atan2(cosU1 * sinLambda,
-sinU1 * cosU2 + cosU1 * sinU2 * cosLambda);
finalBearing *= 180.0 / Math.PI;
results[2] = finalBearing;
}
}
results.mDistance = distance;
float initialBearing = (float) Math.atan2(cosU2 * sinLambda,
cosU1 * sinU2 - sinU1 * cosU2 * cosLambda);
initialBearing *= 180.0 / Math.PI;
results.mInitialBearing = initialBearing;
float finalBearing = (float) Math.atan2(cosU1 * sinLambda,
-sinU1 * cosU2 + cosU1 * sinU2 * cosLambda);
finalBearing *= 180.0 / Math.PI;
results.mFinalBearing = finalBearing;
results.mLat1 = lat1;
results.mLat2 = lat2;
results.mLon1 = lon1;
results.mLon2 = lon2;
}
/**
@@ -420,8 +431,16 @@ public class Location implements Parcelable {
if (results == null || results.length < 1) {
throw new IllegalArgumentException("results is null or has length < 1");
}
BearingDistanceCache cache = sBearingDistanceCache.get();
computeDistanceAndBearing(startLatitude, startLongitude,
endLatitude, endLongitude, results);
endLatitude, endLongitude, cache);
results[0] = cache.mDistance;
if (results.length > 1) {
results[1] = cache.mInitialBearing;
if (results.length > 2) {
results[2] = cache.mFinalBearing;
}
}
}
/**
@@ -433,21 +452,14 @@ public class Location implements Parcelable {
* @return the approximate distance in meters
*/
public float distanceTo(Location dest) {
BearingDistanceCache cache = sBearingDistanceCache.get();
// See if we already have the result
synchronized (mResults) {
if (mLatitude != mLat1 || mLongitude != mLon1 ||
dest.mLatitude != mLat2 || dest.mLongitude != mLon2) {
computeDistanceAndBearing(mLatitude, mLongitude,
dest.mLatitude, dest.mLongitude, mResults);
mLat1 = mLatitude;
mLon1 = mLongitude;
mLat2 = dest.mLatitude;
mLon2 = dest.mLongitude;
mDistance = mResults[0];
mInitialBearing = mResults[1];
}
return mDistance;
if (mLatitude != cache.mLat1 || mLongitude != cache.mLon1 ||
dest.mLatitude != cache.mLat2 || dest.mLongitude != cache.mLon2) {
computeDistanceAndBearing(mLatitude, mLongitude,
dest.mLatitude, dest.mLongitude, cache);
}
return cache.mDistance;
}
/**
@@ -461,21 +473,14 @@ public class Location implements Parcelable {
* @return the initial bearing in degrees
*/
public float bearingTo(Location dest) {
synchronized (mResults) {
// See if we already have the result
if (mLatitude != mLat1 || mLongitude != mLon1 ||
dest.mLatitude != mLat2 || dest.mLongitude != mLon2) {
computeDistanceAndBearing(mLatitude, mLongitude,
dest.mLatitude, dest.mLongitude, mResults);
mLat1 = mLatitude;
mLon1 = mLongitude;
mLat2 = dest.mLatitude;
mLon2 = dest.mLongitude;
mDistance = mResults[0];
mInitialBearing = mResults[1];
}
return mInitialBearing;
BearingDistanceCache cache = sBearingDistanceCache.get();
// See if we already have the result
if (mLatitude != cache.mLat1 || mLongitude != cache.mLon1 ||
dest.mLatitude != cache.mLat2 || dest.mLongitude != cache.mLon2) {
computeDistanceAndBearing(mLatitude, mLongitude,
dest.mLatitude, dest.mLongitude, cache);
}
return cache.mInitialBearing;
}
/**
@@ -591,7 +596,7 @@ public class Location implements Parcelable {
* True if this location has an altitude.
*/
public boolean hasAltitude() {
return mHasAltitude;
return (mFieldsMask & HAS_ALTITUDE_MASK) != 0;
}
/**
@@ -611,7 +616,7 @@ public class Location implements Parcelable {
*/
public void setAltitude(double altitude) {
mAltitude = altitude;
mHasAltitude = true;
mFieldsMask |= HAS_ALTITUDE_MASK;
}
/**
@@ -622,14 +627,14 @@ public class Location implements Parcelable {
*/
public void removeAltitude() {
mAltitude = 0.0f;
mHasAltitude = false;
mFieldsMask &= ~HAS_ALTITUDE_MASK;
}
/**
* True if this location has a speed.
*/
public boolean hasSpeed() {
return mHasSpeed;
return (mFieldsMask & HAS_SPEED_MASK) != 0;
}
/**
@@ -648,7 +653,7 @@ public class Location implements Parcelable {
*/
public void setSpeed(float speed) {
mSpeed = speed;
mHasSpeed = true;
mFieldsMask |= HAS_SPEED_MASK;
}
/**
@@ -659,14 +664,14 @@ public class Location implements Parcelable {
*/
public void removeSpeed() {
mSpeed = 0.0f;
mHasSpeed = false;
mFieldsMask &= ~HAS_SPEED_MASK;
}
/**
* True if this location has a bearing.
*/
public boolean hasBearing() {
return mHasBearing;
return (mFieldsMask & HAS_BEARING_MASK) != 0;
}
/**
@@ -698,7 +703,7 @@ public class Location implements Parcelable {
bearing -= 360.0f;
}
mBearing = bearing;
mHasBearing = true;
mFieldsMask |= HAS_BEARING_MASK;
}
/**
@@ -709,7 +714,7 @@ public class Location implements Parcelable {
*/
public void removeBearing() {
mBearing = 0.0f;
mHasBearing = false;
mFieldsMask &= ~HAS_BEARING_MASK;
}
/**
@@ -719,7 +724,7 @@ public class Location implements Parcelable {
* accuracy.
*/
public boolean hasAccuracy() {
return mHasAccuracy;
return (mFieldsMask & HAS_ACCURACY_MASK) != 0;
}
/**
@@ -757,7 +762,7 @@ public class Location implements Parcelable {
*/
public void setAccuracy(float accuracy) {
mAccuracy = accuracy;
mHasAccuracy = true;
mFieldsMask |= HAS_ACCURACY_MASK;
}
/**
@@ -768,7 +773,7 @@ public class Location implements Parcelable {
*/
public void removeAccuracy() {
mAccuracy = 0.0f;
mHasAccuracy = false;
mFieldsMask &= ~HAS_ACCURACY_MASK;
}
/**
@@ -786,7 +791,7 @@ public class Location implements Parcelable {
@SystemApi
public boolean isComplete() {
if (mProvider == null) return false;
if (!mHasAccuracy) return false;
if (!hasAccuracy()) return false;
if (mTime == 0) return false;
if (mElapsedRealtimeNanos == 0) return false;
return true;
@@ -804,8 +809,8 @@ public class Location implements Parcelable {
@SystemApi
public void makeComplete() {
if (mProvider == null) mProvider = "?";
if (!mHasAccuracy) {
mHasAccuracy = true;
if (!hasAccuracy()) {
mFieldsMask |= HAS_ACCURACY_MASK;
mAccuracy = 100.0f;
}
if (mTime == 0) mTime = System.currentTimeMillis();
@@ -844,7 +849,7 @@ public class Location implements Parcelable {
s.append("Location[");
s.append(mProvider);
s.append(String.format(" %.6f,%.6f", mLatitude, mLongitude));
if (mHasAccuracy) s.append(String.format(" acc=%.0f", mAccuracy));
if (hasAccuracy()) s.append(String.format(" acc=%.0f", mAccuracy));
else s.append(" acc=???");
if (mTime == 0) {
s.append(" t=?!?");
@@ -855,10 +860,10 @@ public class Location implements Parcelable {
s.append(" et=");
TimeUtils.formatDuration(mElapsedRealtimeNanos / 1000000L, s);
}
if (mHasAltitude) s.append(" alt=").append(mAltitude);
if (mHasSpeed) s.append(" vel=").append(mSpeed);
if (mHasBearing) s.append(" bear=").append(mBearing);
if (mIsFromMockProvider) s.append(" mock");
if (hasAltitude()) s.append(" alt=").append(mAltitude);
if (hasSpeed()) s.append(" vel=").append(mSpeed);
if (hasBearing()) s.append(" bear=").append(mBearing);
if (isFromMockProvider()) s.append(" mock");
if (mExtras != null) {
s.append(" {").append(mExtras).append('}');
@@ -879,18 +884,14 @@ public class Location implements Parcelable {
Location l = new Location(provider);
l.mTime = in.readLong();
l.mElapsedRealtimeNanos = in.readLong();
l.mFieldsMask = in.readByte();
l.mLatitude = in.readDouble();
l.mLongitude = in.readDouble();
l.mHasAltitude = in.readInt() != 0;
l.mAltitude = in.readDouble();
l.mHasSpeed = in.readInt() != 0;
l.mSpeed = in.readFloat();
l.mHasBearing = in.readInt() != 0;
l.mBearing = in.readFloat();
l.mHasAccuracy = in.readInt() != 0;
l.mAccuracy = in.readFloat();
l.mExtras = in.readBundle();
l.mIsFromMockProvider = in.readInt() != 0;
return l;
}
@@ -910,18 +911,14 @@ public class Location implements Parcelable {
parcel.writeString(mProvider);
parcel.writeLong(mTime);
parcel.writeLong(mElapsedRealtimeNanos);
parcel.writeByte(mFieldsMask);
parcel.writeDouble(mLatitude);
parcel.writeDouble(mLongitude);
parcel.writeInt(mHasAltitude ? 1 : 0);
parcel.writeDouble(mAltitude);
parcel.writeInt(mHasSpeed ? 1 : 0);
parcel.writeFloat(mSpeed);
parcel.writeInt(mHasBearing ? 1 : 0);
parcel.writeFloat(mBearing);
parcel.writeInt(mHasAccuracy ? 1 : 0);
parcel.writeFloat(mAccuracy);
parcel.writeBundle(mExtras);
parcel.writeInt(mIsFromMockProvider? 1 : 0);
}
/**
@@ -946,7 +943,7 @@ public class Location implements Parcelable {
* Attaches an extra {@link Location} to this Location.
*
* @param key the key associated with the Location extra
* @param location the Location to attach
* @param value the Location to attach
* @hide
*/
public void setExtraLocation(String key, Location value) {
@@ -962,7 +959,7 @@ public class Location implements Parcelable {
* @return true if this Location came from a mock provider, false otherwise
*/
public boolean isFromMockProvider() {
return mIsFromMockProvider;
return (mFieldsMask & HAS_MOCK_PROVIDER_MASK) != 0;
}
/**
@@ -973,6 +970,24 @@ public class Location implements Parcelable {
*/
@SystemApi
public void setIsFromMockProvider(boolean isFromMockProvider) {
mIsFromMockProvider = isFromMockProvider;
if (isFromMockProvider) {
mFieldsMask |= HAS_MOCK_PROVIDER_MASK;
} else {
mFieldsMask &= ~HAS_MOCK_PROVIDER_MASK;
}
}
/**
* Caches data used to compute distance and bearing (so successive calls to {@link #distanceTo}
* and {@link #bearingTo} don't duplicate work.
*/
private static class BearingDistanceCache {
private double mLat1 = 0.0;
private double mLon1 = 0.0;
private double mLat2 = 0.0;
private double mLon2 = 0.0;
private float mDistance = 0.0f;
private float mInitialBearing = 0.0f;
private float mFinalBearing = 0.0f;
}
}

View File

@@ -16,6 +16,7 @@
package android.location;
import android.os.Parcel;
import android.test.suitebuilder.annotation.SmallTest;
import junit.framework.TestCase;
@@ -225,6 +226,40 @@ public class LocationTest extends TestCase {
assertEquals(message, loc.getBearing(), 0, 0);
}
public void testParcel() {
final double expectedLat = 33;
final double expectedLon = -122;
final float expectedAccuracy = 15;
final float expectedSpeed = 5;
Location loc = new Location("test");
loc.setLatitude(expectedLat);
loc.setLongitude(expectedLon);
loc.setAccuracy(expectedAccuracy);
loc.setSpeed(expectedSpeed);
// Serialize location object into bytes via parcelable capability
Parcel parcel = Parcel.obtain();
loc.writeToParcel(parcel, 0);
byte[] rawBytes = parcel.marshall();
parcel.recycle();
// Turn the bytes back into a location object
parcel = Parcel.obtain();
parcel.unmarshall(rawBytes, 0, rawBytes.length);
parcel.setDataPosition(0);
Location deserialized = Location.CREATOR.createFromParcel(parcel);
parcel.recycle();
assertEquals(expectedLat, deserialized.getLatitude());
assertEquals(expectedLon, deserialized.getLongitude());
assertEquals(expectedAccuracy, deserialized.getAccuracy());
assertTrue(deserialized.hasAccuracy());
assertEquals(expectedSpeed, deserialized.getSpeed());
assertTrue(deserialized.hasSpeed());
assertFalse(deserialized.hasBearing());
assertFalse(deserialized.hasAltitude());
assertFalse(deserialized.isFromMockProvider());
}
}