Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 923b2602 authored by David Christie's avatar David Christie
Browse files

Make Location objects take less memory.

-Use bitmask for has*** methods.
-Use ThreadLocal for caching intermediate computations
 rather than preallocating memory in every Location

Change-Id: If2fa17bfd59511ec0b809f4b7d7cd8028360c340
parent ab253faa
Loading
Loading
Loading
Loading
+115 −100
Original line number Diff line number Diff line
@@ -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) {
        results.mDistance = distance;
        float initialBearing = (float) Math.atan2(cosU2 * sinLambda,
            cosU1 * sinU2 - sinU1 * cosU2 * cosLambda);
        initialBearing *= 180.0 / Math.PI;
            results[1] = initialBearing;
            if (results.length > 2) {
        results.mInitialBearing = initialBearing;
        float finalBearing = (float) Math.atan2(cosU1 * sinLambda,
                -sinU1 * cosU2 + cosU1 * sinU2 * cosLambda);
        finalBearing *= 180.0 / Math.PI;
                results[2] = finalBearing;
            }
        }
        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) {
        if (mLatitude != cache.mLat1 || mLongitude != cache.mLon1 ||
            dest.mLatitude != cache.mLat2 || dest.mLongitude != cache.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;
                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) {
        BearingDistanceCache cache = sBearingDistanceCache.get();
        // See if we already have the result
            if (mLatitude != mLat1 || mLongitude != mLon1 ||
                            dest.mLatitude != mLat2 || dest.mLongitude != mLon2) {
        if (mLatitude != cache.mLat1 || mLongitude != cache.mLon1 ||
                        dest.mLatitude != cache.mLat2 || dest.mLongitude != cache.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;
                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;
    }
}
+35 −0
Original line number Diff line number Diff line
@@ -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());
    }
}