google-site-verification: google61fe8ba583a51912.html
AltBeacon Library in Java: Complete Guide for Beacon Detection

Introduction to AltBeacon

AltBeacon is an open-source Android library that provides a standardized beacon format and robust detection capabilities for Bluetooth Low Energy (BLE) beacons. It's the successor to the original Android Beacon Library and offers improved performance, battery efficiency, and reliability.

Key Features

  • Open Standard: Implements the open AltBeacon specification
  • Background Scanning: Efficient scanning even when app is in background
  • Battery Efficient: Optimized scanning cycles to preserve battery
  • Multiple Beacon Formats: Supports iBeacon, Eddystone, and custom formats
  • Region Monitoring: Define geographic regions based on beacon presence
  • Ranging: Measure distance to nearby beacons

Implementation Guide

Dependencies

Add to your app/build.gradle:

dependencies { implementation 'org.altbeacon:android-beacon-library:2.19.5' implementation 'androidx.appcompat:appcompat:1.6.1' implementation 'androidx.core:core:1.12.0' }

AndroidManifest.xml Permissions

<uses-permission android:name="android.permission.BLUETOOTH" /> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" android:maxSdkVersion="30" /> <uses-permission android:name="android.permission.BLUETOOTH_SCAN" android:usesPermissionFlags="neverForLocation" tools:targetApi="31" /> <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" /> <!-- For Android 12+ --> <uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />

Base Application Class

import android.app.Application; import org.altbeacon.beacon.BeaconManager; import org.altbeacon.beacon.BeaconParser; import org.altbeacon.beacon.MonitorNotifier; import org.altbeacon.beacon.Region; public class BeaconApplication extends Application implements MonitorNotifier { private static final String TAG = "BeaconApplication"; private BeaconManager beaconManager; @Override public void onCreate() { super.onCreate(); setupBeaconManager(); } private void setupBeaconManager() { beaconManager = BeaconManager.getInstanceForApplication(this); // Configure beacon parsers for different formats beaconManager.getBeaconParsers().add(new BeaconParser() .setBeaconLayout("m:2-3=beac,i:4-19,i:20-21,i:22-23,p:24-24,d:25-25")); // Add iBeacon parser beaconManager.getBeaconParsers().add(new BeaconParser() .setBeaconLayout("m:2-3=0215,i:4-19,i:20-21,i:22-23,p:24-24")); // Add Eddystone-UID parser beaconManager.getBeaconParsers().add(new BeaconParser() .setBeaconLayout("s:0-1=feaa,m:2-2=00,p:3-3:-41,i:4-13,i:14-19")); // Set scan periods (customize for your use case) beaconManager.setForegroundScanPeriod(1100L); beaconManager.setForegroundBetweenScanPeriod(0L); beaconManager.setBackgroundScanPeriod(1100L); beaconManager.setBackgroundBetweenScanPeriod(30000L); // Bind the beacon service beaconManager.bind(this); } @Override public void didEnterRegion(Region region) { // Beacon detected in region android.util.Log.d(TAG, "Entered region: " + region.getUniqueId()); } @Override public void didExitRegion(Region region) { // Exited beacon region android.util.Log.d(TAG, "Exited region: " + region.getUniqueId()); } @Override public void didDetermineStateForRegion(int state, Region region) { // Region state changed String stateString = (state == MonitorNotifier.INSIDE) ? "INSIDE" : "OUTSIDE"; android.util.Log.d(TAG, "Region state: " + stateString + " for " + region.getUniqueId()); } }

Main Activity with Beacon Detection

import android.Manifest; import android.content.pm.PackageManager; import android.os.Build; import android.os.Bundle; import android.widget.ArrayAdapter; import android.widget.ListView; import android.widget.Toast; import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; import org.altbeacon.beacon.Beacon; import org.altbeacon.beacon.BeaconConsumer; import org.altbeacon.beacon.BeaconManager; import org.altbeacon.beacon.BeaconParser; import org.altbeacon.beacon.RangeNotifier; import org.altbeacon.beacon.Region; import java.util.ArrayList; import java.util.Collection; import java.util.List; public class MainActivity extends AppCompatActivity implements BeaconConsumer, RangeNotifier { private static final String TAG = "MainActivity"; private static final int PERMISSION_REQUEST_CODE = 1001; private static final String BEACON_REGION_ID = "my-beacon-region"; private BeaconManager beaconManager; private ListView beaconListView; private ArrayAdapter<String> beaconAdapter; private List<String> beaconList; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initializeUI(); checkPermissions(); } private void initializeUI() { beaconListView = findViewById(R.id.beaconListView); beaconList = new ArrayList<>(); beaconAdapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, beaconList); beaconListView.setAdapter(beaconAdapter); } private void checkPermissions() { List<String> requiredPermissions = new ArrayList<>(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { // Android 12+ requires BLUETOOTH_SCAN, BLUETOOTH_CONNECT if (ContextCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED) { requiredPermissions.add(Manifest.permission.BLUETOOTH_SCAN); } if (ContextCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) { requiredPermissions.add(Manifest.permission.BLUETOOTH_CONNECT); } } else { // Older versions need ACCESS_FINE_LOCATION if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) { requiredPermissions.add(Manifest.permission.ACCESS_FINE_LOCATION); } } if (!requiredPermissions.isEmpty()) { ActivityCompat.requestPermissions(this, requiredPermissions.toArray(new String[0]), PERMISSION_REQUEST_CODE); } else { initializeBeaconManager(); } } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == PERMISSION_REQUEST_CODE) { boolean allGranted = true; for (int result : grantResults) { if (result != PackageManager.PERMISSION_GRANTED) { allGranted = false; break; } } if (allGranted) { initializeBeaconManager(); } else { Toast.makeText(this, "Permissions required for beacon detection", Toast.LENGTH_LONG).show(); } } } private void initializeBeaconManager() { beaconManager = BeaconManager.getInstanceForApplication(this); // Configure for optimal performance beaconManager.setForegroundScanPeriod(1100L); beaconManager.setForegroundBetweenScanPeriod(0L); // Connect to beacon service beaconManager.bind(this); } @Override public void onBeaconServiceConnect() { // Set up region monitoring Region region = new Region(BEACON_REGION_ID, null, null, null); try { // Start ranging beacons beaconManager.startRangingBeacons(region); beaconManager.addRangeNotifier(this); // Start monitoring for region entry/exit beaconManager.startMonitoring(region); } catch (Exception e) { android.util.Log.e(TAG, "Failed to start beacon monitoring", e); } } @Override public void didRangeBeaconsInRegion(Collection<Beacon> beacons, Region region) { runOnUiThread(() -> updateBeaconList(beacons)); } private void updateBeaconList(Collection<Beacon> beacons) { beaconList.clear(); for (Beacon beacon : beacons) { String beaconInfo = String.format( "UUID: %s\nMajor: %d Minor: %d\nDistance: %.2fm RSSI: %d\nTxPower: %d", beacon.getId1().toString(), beacon.getId2().toInt(), beacon.getId3().toInt(), beacon.getDistance(), beacon.getRssi(), beacon.getTxPower() ); beaconList.add(beaconInfo); } beaconAdapter.notifyDataSetChanged(); // Log detection for debugging if (!beacons.isEmpty()) { android.util.Log.d(TAG, "Detected " + beacons.size() + " beacons"); } } @Override protected void onDestroy() { super.onDestroy(); if (beaconManager != null) { beaconManager.unbind(this); } } }

Advanced Beacon Monitoring Service

import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; import android.app.Service; import android.content.Intent; import android.os.Binder; import android.os.Build; import android.os.IBinder; import org.altbeacon.beacon.Beacon; import org.altbeacon.beacon.BeaconConsumer; import org.altbeacon.beacon.BeaconManager; import org.altbeacon.beacon.BeaconParser; import org.altbeacon.beacon.MonitorNotifier; import org.altbeacon.beacon.RangeNotifier; import org.altbeacon.beacon.Region; import java.util.Collection; import java.util.HashSet; import java.util.Set; public class BeaconMonitoringService extends Service implements BeaconConsumer, MonitorNotifier, RangeNotifier { private static final String TAG = "BeaconMonitoringService"; private static final String CHANNEL_ID = "beacon_monitoring_channel"; private static final int NOTIFICATION_ID = 1; private BeaconManager beaconManager; private final IBinder binder = new LocalBinder(); private Set<BeaconDetectionListener> listeners = new HashSet<>(); public interface BeaconDetectionListener { void onBeaconDetected(Beacon beacon); void onBeaconsInRange(Collection<Beacon> beacons); void onRegionEntered(Region region); void onRegionExited(Region region); } public class LocalBinder extends Binder { BeaconMonitoringService getService() { return BeaconMonitoringService.this; } } @Override public void onCreate() { super.onCreate(); createNotificationChannel(); startForeground(NOTIFICATION_ID, createNotification()); initializeBeaconManager(); } private void initializeBeaconManager() { beaconManager = BeaconManager.getInstanceForApplication(this); // Add beacon parsers beaconManager.getBeaconParsers().add(new BeaconParser() .setBeaconLayout("m:2-3=beac,i:4-19,i:20-21,i:22-23,p:24-24,d:25-25")); // Background configuration beaconManager.setBackgroundScanPeriod(1100L); beaconManager.setBackgroundBetweenScanPeriod(30000L); beaconManager.setEnableScheduledScanJobs(false); beaconManager.setBackgroundMode(true); beaconManager.bind(this); } @Override public void onBeaconServiceConnect() { beaconManager.addMonitorNotifier(this); beaconManager.addRangeNotifier(this); // Monitor all beacons Region monitoringRegion = new Region("background-region", null, null, null); try { beaconManager.startMonitoring(monitoringRegion); beaconManager.startRangingBeacons(monitoringRegion); } catch (Exception e) { android.util.Log.e(TAG, "Cannot start monitoring", e); } } @Override public void didEnterRegion(Region region) { android.util.Log.d(TAG, "Entered region: " + region.getUniqueId()); notifyRegionEntered(region); // Show notification showRegionNotification("Entered Beacon Region", "You have entered a beacon monitoring area"); } @Override public void didExitRegion(Region region) { android.util.Log.d(TAG, "Exited region: " + region.getUniqueId()); notifyRegionExited(region); showRegionNotification("Exited Beacon Region", "You have left a beacon monitoring area"); } @Override public void didDetermineStateForRegion(int state, Region region) { String stateStr = (state == INSIDE) ? "INSIDE" : "OUTSIDE"; android.util.Log.d(TAG, "Region state: " + stateStr + " for " + region.getUniqueId()); } @Override public void didRangeBeaconsInRegion(Collection<Beacon> beacons, Region region) { for (Beacon beacon : beacons) { android.util.Log.d(TAG, "Ranged beacon: " + beacon.toString()); notifyBeaconDetected(beacon); } notifyBeaconsInRange(beacons); } // Listener management public void addBeaconListener(BeaconDetectionListener listener) { listeners.add(listener); } public void removeBeaconListener(BeaconDetectionListener listener) { listeners.remove(listener); } private void notifyBeaconDetected(Beacon beacon) { for (BeaconDetectionListener listener : listeners) { listener.onBeaconDetected(beacon); } } private void notifyBeaconsInRange(Collection<Beacon> beacons) { for (BeaconDetectionListener listener : listeners) { listener.onBeaconsInRange(beacons); } } private void notifyRegionEntered(Region region) { for (BeaconDetectionListener listener : listeners) { listener.onRegionEntered(region); } } private void notifyRegionExited(Region region) { for (BeaconDetectionListener listener : listeners) { listener.onRegionExited(region); } } private void createNotificationChannel() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { NotificationChannel channel = new NotificationChannel( CHANNEL_ID, "Beacon Monitoring", NotificationManager.IMPORTANCE_LOW ); channel.setDescription("Background beacon monitoring service"); NotificationManager manager = getSystemService(NotificationManager.class); manager.createNotificationChannel(channel); } } private Notification createNotification() { Notification.Builder builder; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { builder = new Notification.Builder(this, CHANNEL_ID); } else { builder = new Notification.Builder(this); } builder.setContentTitle("Beacon Monitor") .setContentText("Monitoring for nearby beacons") .setSmallIcon(android.R.drawable.ic_dialog_info) .setPriority(Notification.PRIORITY_LOW); return builder.build(); } private void showRegionNotification(String title, String content) { Notification.Builder builder; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { builder = new Notification.Builder(this, CHANNEL_ID); } else { builder = new Notification.Builder(this); } Notification notification = builder .setContentTitle(title) .setContentText(content) .setSmallIcon(android.R.drawable.ic_dialog_info) .build(); NotificationManager manager = getSystemService(NotificationManager.class); manager.notify(NOTIFICATION_ID + 1, notification); } @Override public IBinder onBind(Intent intent) { return binder; } @Override public void onDestroy() { super.onDestroy(); if (beaconManager != null) { beaconManager.unbind(this); } } }

Beacon Proximity Manager

import org.altbeacon.beacon.Beacon; import java.util.HashMap; import java.util.Map; public class BeaconProximityManager { public enum Proximity { IMMEDIATE, NEAR, FAR, UNKNOWN } private static final double IMMEDIATE_THRESHOLD = 0.5; // meters private static final double NEAR_THRESHOLD = 3.0; // meters private Map<String, Beacon> trackedBeacons = new HashMap<>(); private Map<String, Long> lastSeenTimestamps = new HashMap<>(); private static final long TIMEOUT_MS = 30000; // 30 seconds public Proximity calculateProximity(Beacon beacon) { double distance = beacon.getDistance(); if (distance < 0) { return Proximity.UNKNOWN; } else if (distance <= IMMEDIATE_THRESHOLD) { return Proximity.IMMEDIATE; } else if (distance <= NEAR_THRESHOLD) { return Proximity.NEAR; } else { return Proximity.FAR; } } public void updateBeacon(Beacon beacon) { String beaconId = getBeaconId(beacon); trackedBeacons.put(beaconId, beacon); lastSeenTimestamps.put(beaconId, System.currentTimeMillis()); } public void cleanupOldBeacons() { long currentTime = System.currentTimeMillis(); lastSeenTimestamps.entrySet().removeIf(entry -> (currentTime - entry.getValue()) > TIMEOUT_MS); trackedBeacons.keySet().removeIf(beaconId -> !lastSeenTimestamps.containsKey(beaconId)); } public Map<String, Beacon> getTrackedBeacons() { return new HashMap<>(trackedBeacons); } private String getBeaconId(Beacon beacon) { return String.format("%s-%s-%s", beacon.getId1(), beacon.getId2(), beacon.getId3()); } public double calculateAverageDistance(Beacon beacon, int samples) { // Simple moving average for distance smoothing // In production, implement proper filtering return beacon.getDistance(); } }

Layout Files

activity_main.xml

<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:padding="16dp"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Detected Beacons" android:textSize="18sp" android:textStyle="bold" android:gravity="center" android:layout_marginBottom="16dp" /> <ListView android:id="@+id/beaconListView" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" /> <Button android:id="@+id/startMonitoringButton" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Start Monitoring" android:layout_marginTop="8dp" /> <Button android:id="@+id/stopMonitoringButton" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Stop Monitoring" android:layout_marginTop="8dp" /> </LinearLayout>

Best Practices

1. Battery Optimization

// Use appropriate scan intervals beaconManager.setForegroundScanPeriod(1100L); // Scan for 1.1 seconds beaconManager.setForegroundBetweenScanPeriod(5000L); // Wait 5 seconds between scans // For background, use longer intervals beaconManager.setBackgroundScanPeriod(1100L); beaconManager.setBackgroundBetweenScanPeriod(30000L); // Wait 30 seconds

2. Permission Handling for Different Android Versions

private boolean hasRequiredPermissions() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { return hasPermission(Manifest.permission.BLUETOOTH_SCAN) && hasPermission(Manifest.permission.BLUETOOTH_CONNECT); } else { return hasPermission(Manifest.permission.ACCESS_FINE_LOCATION); } }

3. Error Handling and Recovery

private void handleBeaconServiceDisconnect() { try { beaconManager.unbind(this); // Rebind after short delay new android.os.Handler().postDelayed(() -> beaconManager.bind(this), 1000); } catch (Exception e) { android.util.Log.e(TAG, "Error reconnecting to beacon service", e); } }

Testing and Debugging

Enable Debug Logging

// In your Application class import org.altbeacon.beacon.logging.LogManager; import org.altbeacon.beacon.logging.Loggers; public void enableBeaconDebugLogging() { LogManager.setLogger(Loggers.verboseLogger()); LogManager.setVerboseLoggingEnabled(true); }

Testing with Mock Beacons

// Create mock beacons for testing Beacon mockBeacon = new Beacon.Builder() .setId1("2f234454-cf6d-4a0f-adf2-f4911ba9ffa6") .setId2("1") .setId3("2") .setRssi(-55) .setTxPower(-59) .build();

Conclusion

The AltBeacon library provides a robust foundation for beacon detection in Android applications. This implementation covers:

  • Basic beacon detection with proper permission handling
  • Background monitoring for persistent beacon tracking
  • Proximity calculations for distance estimation
  • Best practices for battery efficiency and performance
  • Error handling and recovery mechanisms

Remember to test thoroughly on different Android versions and device models, as Bluetooth behavior can vary significantly across hardware. Always prioritize user privacy and battery life in your implementation.

Java Observability, Logging Intelligence & AI-Driven Monitoring (APM, Tracing, Logs & Anomaly Detection)

https://macronepal.com/blog/beyond-metrics-observing-serverless-and-traditional-java-applications-with-thundra-apm/
Explains using Thundra APM to observe both serverless and traditional Java applications by combining tracing, metrics, and logs into a unified observability platform for faster debugging and performance insights.

https://macronepal.com/blog/dynatrace-oneagent-in-java-2/
Explains Dynatrace OneAgent for Java, which automatically instruments JVM applications to capture metrics, traces, and logs, enabling full-stack monitoring and root-cause analysis with minimal configuration.

https://macronepal.com/blog/lightstep-java-sdk-distributed-tracing-and-observability-implementation/
Explains Lightstep Java SDK for distributed tracing, helping developers track requests across microservices and identify latency issues using OpenTelemetry-based observability.

https://macronepal.com/blog/honeycomb-io-beeline-for-java-complete-guide-2/
Explains Honeycomb Beeline for Java, which provides high-cardinality observability and deep query capabilities to understand complex system behavior and debug distributed systems efficiently.

https://macronepal.com/blog/lumigo-for-serverless-in-java-complete-distributed-tracing-guide-2/
Explains Lumigo for Java serverless applications, offering automatic distributed tracing, log correlation, and error tracking to simplify debugging in cloud-native environments. (Lumigo Docs)

https://macronepal.com/blog/from-noise-to-signals-implementing-log-anomaly-detection-in-java-applications/
Explains how to detect anomalies in Java logs using behavioral patterns and machine learning techniques to separate meaningful incidents from noisy log data and improve incident response.

https://macronepal.com/blog/ai-powered-log-analysis-in-java-from-reactive-debugging-to-proactive-insights/
Explains AI-driven log analysis for Java applications, shifting from manual debugging to predictive insights that identify issues early and improve system reliability using intelligent log processing.

https://macronepal.com/blog/titliel-java-logging-best-practices/
Explains best practices for Java logging, focusing on structured logs, proper log levels, performance optimization, and ensuring logs are useful for debugging and observability systems.

https://macronepal.com/blog/seeking-a-loguru-for-java-the-quest-for-elegant-and-simple-logging/
Explains the search for simpler, more elegant logging frameworks in Java, comparing modern logging approaches that aim to reduce complexity while improving readability and developer experience.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper