如何通过onDistanceChanged方法筛选出距离最近的Beacon?
Hey there! As someone who’s tinkered with the Nearby Messages API and beacons on Android, I can walk you through how to track the closest beacon easily. Let’s break this down step by step.
The Core Idea
You’ll need a way to store each beacon’s latest distance, then regularly check which one has the smallest (closest) value. Since distance updates come in through the onDistanceChanged callback, we’ll use that to keep our data fresh.
Step 1: Set Up a Thread-Safe Storage
First, add a member variable to hold each beacon’s current distance. We’ll use ConcurrentHashMap because the onDistanceChanged callback might run on a background thread, and this ensures safe access across threads:
// Maps each unique Eddystone UID to its latest distance reading private ConcurrentHashMap<EddystoneUid, Distance> beaconDistanceMap = new ConcurrentHashMap<>();
Step 2: Update Distance Data in the Callback
Modify your existing onDistanceChanged method to update the map every time a distance changes. After updating, you can immediately calculate the closest beacon:
public void onDistanceChanged(final Message mNearbyMessagesList, final Distance distance) { EddystoneUid eddystoneUid = EddystoneUid.from(mNearbyMessagesList); Log.i(TAG, "Distance changed, message: " + eddystoneUid + ", new distance: " + distance); // Update the map with the latest distance for this beacon beaconDistanceMap.put(eddystoneUid, distance); // Get the closest beacon right after updating EddystoneUid closestBeacon = getClosestBeacon(); if (closestBeacon != null) { Distance closestDistance = beaconDistanceMap.get(closestBeacon); Log.i(TAG, "Current closest beacon: " + closestBeacon + ", distance: " + closestDistance); } }
Step 3: Add a Method to Find the Closest Beacon
Now write a helper method to scan through the map and find the beacon with the smallest distance. Note that the Distance enum has priority levels (IMMEDIATE > NEAR > FAR > UNKNOWN)—we’ll account for that first, then compare exact meter values if priorities are the same:
private EddystoneUid getClosestBeacon() { if (beaconDistanceMap.isEmpty()) { return null; // No beacons detected yet } EddystoneUid closestUid = null; Distance closestDistance = null; for (Map.Entry<EddystoneUid, Distance> entry : beaconDistanceMap.entrySet()) { EddystoneUid currentUid = entry.getKey(); Distance currentDistance = entry.getValue(); // Initialize with the first beacon if we haven't set one yet if (closestDistance == null) { closestUid = currentUid; closestDistance = currentDistance; continue; } // Compare distance priorities first int currentPriority = getDistancePriority(currentDistance); int closestPriority = getDistancePriority(closestDistance); if (currentPriority > closestPriority) { // Current beacon is closer based on enum level closestUid = currentUid; closestDistance = currentDistance; } else if (currentPriority == closestPriority) { // If priorities match, compare exact meter values (if available) if (currentDistance.getMeters() != null && closestDistance.getMeters() != null) { if (currentDistance.getMeters() < closestDistance.getMeters()) { closestUid = currentUid; closestDistance = currentDistance; } } } } return closestUid; } // Assign priority values: higher = closer private int getDistancePriority(Distance distance) { switch (distance) { case IMMEDIATE: return 4; case NEAR: return 3; case FAR: return 2; case UNKNOWN: default: return 1; } }
Quick Tips for Polish
- Handle lost beacons: Don’t forget to remove beacons from the map when the
onLostcallback triggers, otherwise you’ll keep stale data. - UI thread safety: If you need to update the UI with the closest beacon, wrap that code in
runOnUiThread()or use aHandlerto avoid crashes. - Debounce updates: If distance changes are firing too frequently, add a small delay (like 500ms) before recalculating the closest beacon to avoid unnecessary processing.
内容的提问来源于stack exchange,提问作者Supreet Kaur

