Stop scanning of beacons: stopRangingBeaconsInRegion() not working

1.4k views Asked by At

I'm using Android Beacon Library to scan for Eddystone Beacons for Android App. I start monitoring after beacon service is connected and then start ranging when a beacon is found.

I need to stop scanning for beacons (stop both monitoring and ranging) based on user's choice. I am using

beaconManager.stopMonitoringBeaconsInRegion(region);
beaconManager.stopRangingBeaconsInRegion(region);

to stop scanning inside my stopScan(). But the ranging doesn't stop, though monitoring stops. Is there something I'm doing wrong?

Here is the complete code:

    public class BlankFragment extends Fragment implements BeaconConsumer {


    public BlankFragment() {
        // Required empty public constructor
    }

    private static final int PERMISSION_REQUEST_FINE_LOCATION = 1;
    private Region region;
    private Map<String /* uid */, Beacon> deviceToBeaconMap = new HashMap<>();

    String LOG_TAG = BlankFragment.class.getSimpleName();
    SharedPreferences sp;
    View rootView;


    private BeaconManager beaconManager;
    boolean isScanning = false;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        sp = PreferenceManager.getDefaultSharedPreferences(getActivity());
        sp.registerOnSharedPreferenceChangeListener(listener);


        beaconManager = BeaconManager.getInstanceForApplication(getContext());

        beaconManager.getBeaconParsers().add(new BeaconParser().
                setBeaconLayout("s:0-1=feaa,m:2-2=00,p:3-3:-41,i:4-13,i:14-19"));

        beaconManager.bind(this);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        rootView = inflater.inflate(R.layout.fragment_near_me, container, false);
        return rootView;
    }

    @Override
    public void onStop() {
        super.onStop();

        if (isScanning)
            stopScan();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();

        deviceToBeaconMap.clear();
        beaconManager.unbind(this);
    }

    @Override
    public void onBeaconServiceConnect() {

        region = new Region("nearMeScanning", null, null, null);
        Log.i(LOG_TAG, "OnBeaconServiceConnect called");

        beaconManager.addMonitorNotifier(new MonitorNotifier() {
            @Override
            public void didEnterRegion(Region region) {
                try {
                    beaconManager.startRangingBeaconsInRegion(region);
                    Log.i(LOG_TAG, "In beacon region");
                } catch (RemoteException e) {
                    Log.e(LOG_TAG, "Remote Exception while starting Ranging", e);
                }
            }

            @Override
            public void didExitRegion(Region region) {
                try {
                    beaconManager.stopRangingBeaconsInRegion(region);
                    Log.i(LOG_TAG, "Exited beacon region");
                } catch (RemoteException e) {
                    Log.e(LOG_TAG, "Remote Exception while stoping Ranging", e);
                }
            }

            @Override
            public void didDetermineStateForRegion(int i, Region region) {
                Log.i(LOG_TAG, "State of region changed " + i);

            }
        });

        beaconManager.addRangeNotifier(new RangeNotifier() {
            @Override
            public void didRangeBeaconsInRegion(Collection<org.altbeacon.beacon.Beacon> beacons, Region region) {
                if (beacons.size() > 0) {
                    for (org.altbeacon.beacon.Beacon scannedBeacon : beacons) {

                        setOnLostRunnable();

                        String deviceUUID = scannedBeacon.getId1().toString().substring(2) + scannedBeacon.getId2().toString().substring(2);
                        String deviceAddress = scannedBeacon.getBluetoothAddress();
                        int rssi = scannedBeacon.getRssi();

                        Beacon beacon;

                        if (!deviceToBeaconMap.containsKey(deviceUUID)) {

                            beacon = new Beacon(deviceAddress, rssi, deviceUUID);
                            deviceToBeaconMap.put(deviceUUID, beacon);

                            if (BuildConfig.DEBUG)
                                Log.d(LOG_TAG, "New Beacon added " + deviceUUID + " DeviceAddress " + deviceAddress);


                        } else {
                            deviceToBeaconMap.get(deviceUUID).lastSeenTimestamp = System.currentTimeMillis();
                            deviceToBeaconMap.get(deviceUUID).rssi = rssi;
                        }

                    }
                }
            }
        });

        if (sp.getBoolean(Constants.SharedPreferences.IS_VISIBILITY_ON, false)) {
            refreshScan();
        } else {
            visbilityOffTasks();
        }
    }

    @Override
    public Context getApplicationContext() {
        return getActivity().getApplicationContext();
    }

    @Override
    public void unbindService(ServiceConnection serviceConnection) {
        getActivity().unbindService(serviceConnection);
    }

    @Override
    public boolean bindService(Intent intent, ServiceConnection serviceConnection, int i) {
        return getActivity().bindService(intent, serviceConnection, i);
    }

    private void refreshScan() {

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            if (getActivity().checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
                if (BuildConfig.DEBUG)
                    Log.d(LOG_TAG, "Will check for permissions");
                requestPermissions(new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
                        PERMISSION_REQUEST_FINE_LOCATION);
            } else {
                if (sp.getBoolean(Constants.SharedPreferences.IS_VISIBILITY_ON, false))
                    startScan();
            }
        } else {
            if (sp.getBoolean(Constants.SharedPreferences.IS_VISIBILITY_ON, false))
                startScan();
        }
    }

    private void startScan() {
        if (sp.getBoolean(Constants.SharedPreferences.IS_VISIBILITY_ON, false)) {
            Log.i(LOG_TAG, "Starting scanning");
            try {
                beaconManager.startMonitoringBeaconsInRegion(region);
                isScanning = true;
            } catch (RemoteException e) {
                Log.e(LOG_TAG, "Remote Exception while starting Ranging", e);
            }
        }
    }

    private void stopScan() {
        try {
            beaconManager.stopMonitoringBeaconsInRegion(region);
            beaconManager.stopRangingBeaconsInRegion(region);
            isScanning = false;
        } catch (RemoteException e) {
            Log.e(LOG_TAG, "Remote Exception while stoping Ranging", e);
        }
        Log.i(LOG_TAG, "Stopping scanning");
    }

    SharedPreferences.OnSharedPreferenceChangeListener listener = new SharedPreferences.OnSharedPreferenceChangeListener() {
        public void onSharedPreferenceChanged(SharedPreferences sp, String key) {
            if (key.equalsIgnoreCase(Constants.SharedPreferences.IS_VISIBILITY_ON) && getActivity() != null) {
                if (sp.getBoolean(Constants.SharedPreferences.IS_VISIBILITY_ON, false)) {

                    Log.i(LOG_TAG, "Visibility turned ON");
                    refreshScan();
                } else {
                    Log.i(LOG_TAG, "Visibility turned off");
                    visbilityOffTasks();
                }
            }
        }
    };

    private void visbilityOffTasks() {
        stopScan();
        deviceToBeaconMap.clear();
    }

    int onLostTimeoutMillis = 15000;

    private void setOnLostRunnable() {
        removeHandler.postDelayed(removeLostDevices, onLostTimeoutMillis);
    }

    private static final Handler removeHandler = new Handler(Looper.getMainLooper());
    Runnable removeLostDevices = new Runnable() {
        @Override
        public void run() {
            long time = System.currentTimeMillis();
            Iterator<Map.Entry<String, Beacon>> itr = deviceToBeaconMap.entrySet().iterator();
            while (itr.hasNext()) {
                Beacon beacon = itr.next().getValue();
                if (beacon != null && !beacon.deviceAddress.equalsIgnoreCase("default"))
                    if ((time - beacon.lastSeenTimestamp) > onLostTimeoutMillis) {
                        itr.remove();
                        if (BuildConfig.DEBUG)
                            Log.d(LOG_TAG, "Removing beacon " + beacon.deviceAddress + " Time is " + (time - beacon.lastSeenTimestamp));
                    }
            }
            removeHandler.postDelayed(this, onLostTimeoutMillis);
        }
    };


}

Here are the logs I observed for the scanning. Even after the scanning of beacons is stopped, they continue to appear.

    12-25 22:27:59.103 30207-30207/com.robocats.main I/BlankFragment: Visibility turned ON
12-25 22:27:59.104 30207-30207/com.robocats.main I/BlankFragment: Starting scanning
12-25 22:27:59.105 30207-30207/com.robocats.main I/BlankFragment: State of region changed 0
12-25 22:27:59.105 30207-30207/com.robocats.main I/BlankFragment: State of region changed 0
12-25 22:27:59.105 30207-30207/com.robocats.main I/BlankFragment: State of region changed 0
12-25 22:27:59.105 30207-30207/com.robocats.main I/BlankFragment: State of region changed 0
12-25 22:27:59.105 30207-30207/com.robocats.main I/BlankFragment: State of region changed 0
12-25 22:27:59.105 30207-30207/com.robocats.main I/BlankFragment: State of region changed 0
12-25 22:27:59.105 30207-30207/com.robocats.main I/BlankFragment: State of region changed 0
12-25 22:28:00.061 30207-2969/com.robocats.main I/BlankFragment: State of region changed 1
12-25 22:28:00.061 30207-2969/com.robocats.main I/BlankFragment: In beacon region
12-25 22:28:00.061 30207-2969/com.robocats.main I/BlankFragment: State of region changed 1
12-25 22:28:00.061 30207-2969/com.robocats.main I/BlankFragment: In beacon region
12-25 22:28:00.061 30207-2969/com.robocats.main I/BlankFragment: State of region changed 1
12-25 22:28:00.061 30207-2969/com.robocats.main I/BlankFragment: In beacon region
12-25 22:28:00.061 30207-2969/com.robocats.main I/BlankFragment: State of region changed 1
12-25 22:28:00.061 30207-2969/com.robocats.main I/BlankFragment: In beacon region
12-25 22:28:00.061 30207-2969/com.robocats.main I/BlankFragment: State of region changed 1
12-25 22:28:00.061 30207-2969/com.robocats.main I/BlankFragment: In beacon region
12-25 22:28:00.061 30207-2969/com.robocats.main I/BlankFragment: State of region changed 1
12-25 22:28:00.061 30207-2969/com.robocats.main I/BlankFragment: In beacon region
12-25 22:28:00.061 30207-2969/com.robocats.main I/BlankFragment: State of region changed 1
12-25 22:28:00.061 30207-2969/com.robocats.main I/BlankFragment: In beacon region
12-25 22:28:09.515 30207-30207/com.robocats.main D/BlankFragment: Removing beacon 0C:F3:EE:09:3A:77 Time is 15036
12-25 22:28:09.642 30207-30207/com.robocats.main D/BlankFragment: Removing beacon 0C:F3:EE:09:3A:77 Time is 15161
12-25 22:28:13.497 30207-3224/com.robocats.main D/BlankFragment: New Beacon added 6bd54ac521c218b8f418000000000073 DeviceAddress 0C:F3:EE:09:3A:77
12-25 22:28:13.510 30207-3224/com.robocats.main D/BlankFragment: New Beacon added 6bd54ac521c218b8f418000000000073 DeviceAddress 0C:F3:EE:09:3A:77
12-25 22:28:19.086 30207-3381/com.robocats.main D/BlankFragment: New Beacon added 2f234454f4911ba9ffa100000d5181da DeviceAddress 0C:F3:EE:09:3C:63
12-25 22:28:19.096 30207-3381/com.robocats.main D/BlankFragment: New Beacon added 2f234454f4911ba9ffa100000d5181da DeviceAddress 0C:F3:EE:09:3C:63
12-25 22:28:22.886 30207-30207/com.robocats.main I/BlankFragment: Visibility turned off
12-25 22:28:22.887 30207-30207/com.robocats.main I/BlankFragment: Stopping scanning
12-25 22:28:23.586 30207-3468/com.robocats.main D/BlankFragment: New Beacon added 2f234454f4911ba9ffa100000d5181da DeviceAddress 0C:F3:EE:09:3C:63
12-25 22:28:23.589 30207-3468/com.robocats.main D/BlankFragment: New Beacon added 4b3833f4b99a463283e84bfcc601a926 DeviceAddress 48:59:02:49:FA:3F
12-25 22:28:24.706 30207-3483/com.robocats.main D/BlankFragment: New Beacon added 6bd54ac521c218b8f418000000000073 DeviceAddress 0C:F3:EE:09:3A:77

For some reason, I see that the code inside onBeaconServiceConnect() is running twice. This might be the reason. Let me know why this might have been happening.

1

There are 1 answers

4
davidgyoung On BEST ANSWER

I believe the problem is caused by the Android lifecycle creating multiple copies of BlankFragment. Each time BlankFragment gets created, the onCreate method gets called and the code binds to the scanning service and starts ranging. Because multiple Fragments get created without onDestroy being called, you end up with multiple instances ranging at the same time. When stopScan is called, it only stops ranging for the most recently created Fragment instance.

One solution would be to stop ranging and monitoring and then unbind when the fragment goes out of view.