Skip to content

Instantly share code, notes, and snippets.

@NinoDLC
Created May 20, 2021 14:06
Show Gist options
  • Save NinoDLC/8818ab9d14b4c13467052df9b107422e to your computer and use it in GitHub Desktop.
Save NinoDLC/8818ab9d14b4c13467052df9b107422e to your computer and use it in GitHub Desktop.
public class NearbyRestaurantsRepository {
private static final String TYPE = "restaurant";
// Round the GPS coordinate to avoid re-querying Google for a position that would be only a few meters away
private static final int GPS_SCALE = 2;
@NonNull
private final NearbyRestaurantsApi nearbyRestaurantsApi;
// We store all responses here, maybe sometimes we will avoid querying server if a similar query is asked
private final LruCache<NearbyRestaurantsQuery, NearbyRestaurantsResponse> cache = new LruCache<>(500);
public NearbyRestaurantsRepository(@NonNull NearbyRestaurantsApi nearbyRestaurantsApi) {
this.nearbyRestaurantsApi = nearbyRestaurantsApi;
}
@MainThread
@NonNull
public LiveData<NearbyRestaurantsResponse> getNearbyRestaurants(double latitude, double longitude, int radius) {
final MutableLiveData<NearbyRestaurantsResponse> nearbyRestaurantsResponseMutableLiveData = new MutableLiveData<>();
NearbyRestaurantsQuery query = generateQuery(latitude, longitude, radius);
// 1. We search in our cache a similar query
NearbyRestaurantsResponse existing = cache.get(query);
if (existing != null) {
// 2a. We found a similar query already existing, great ! No need to waste resources with an API call on Google's servers
nearbyRestaurantsResponseMutableLiveData.setValue(existing);
} else {
// 2b. Nothing similar in cache, query Google !
nearbyRestaurantsApi.getNearbyRestaurants(
latitude + "," + longitude,
radius,
TYPE,
null
).enqueue(new Callback<NearbyRestaurantsResponse>() {
@Override
public void onResponse(@NonNull Call<NearbyRestaurantsResponse> call, @NonNull Response<NearbyRestaurantsResponse> response) {
NearbyRestaurantsResponse body = response.body();
// 3. Save the result for next time if it's good...
if (response.isSuccessful() && body != null && body.isSuccessful()) {
cache.put(query, body);
}
nearbyRestaurantsResponseMutableLiveData.setValue(body);
}
@Override
public void onFailure(@NonNull Call<NearbyRestaurantsResponse> call, @NonNull Throwable t) {
t.printStackTrace();
}
});
}
return nearbyRestaurantsResponseMutableLiveData;
}
// We use a LruCache as a cache, and its keys are unique (which is provided by the Object.hashCode() and Object.equals(Object other)
// functions). That's why I overrode these functions in the NearbyRestaurantsQuery object.
// Thanks to the reduction of the scale (we lose "precision" of the GPS coordinates) we can avoid querying servers when location
// is just a few meters aways of the first one we did.
// This is a cheap (and fast) way to cache location-related queries, as we are not actually comparing true distance between points.
// A way to improve this would be to :
// 1/ Use a database (persistence across sessions). Check "GetRestaurantDetailsUseCase" for an example !
// 2/ Use a way to compare distance between the queried location and all the others. We would do the API call only if all persisted
// responses are above the "maximum distance" threshold or the response is too "old".
private NearbyRestaurantsQuery generateQuery(double latitude, double longitude, int radius) {
return new NearbyRestaurantsQuery(
BigDecimal.valueOf(latitude).setScale(GPS_SCALE, RoundingMode.HALF_UP),
BigDecimal.valueOf(longitude).setScale(GPS_SCALE, RoundingMode.HALF_UP),
radius
);
}
}
public class NearbyRestaurantsQuery {
@NonNull
private final BigDecimal lat;
@NonNull
private final BigDecimal lng;
private final int radius;
public NearbyRestaurantsQuery(@NonNull BigDecimal lat, @NonNull BigDecimal lng, int radius) {
this.lat = lat;
this.lng = lng;
this.radius = radius;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
NearbyRestaurantsQuery that = (NearbyRestaurantsQuery) o;
return radius == that.radius &&
lat.equals(that.lat) &&
lng.equals(that.lng);
}
@Override
public int hashCode() {
return Objects.hash(lat, lng, radius);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment