Merge "Make Location objects take less memory."
This commit is contained in:
committed by
Android (Google) Code Review
commit
e978607ac3
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user