Остановить сканирование маяков: stopRangingBeaconsInRegion() не работает

Я использую Android Beacon Library для поиска приложений Eddystone Beacons для Android. Я начинаю мониторинг после подключения службы маяка, а затем начинаю ранжирование, когда маяк найден.

Мне нужно остановить сканирование маяков (остановить как мониторинг, так и ранжирование) в зависимости от выбора пользователя. Я использую

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

чтобы остановить сканирование внутри моего stopScan(). Но ранжирование не прекращается, хотя мониторинг прекращается. Я что-то делаю не так?

Вот полный код:

    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);
        }
    };


}

Вот журналы, которые я наблюдал для сканирования. Даже после прекращения сканирования маяков они продолжают появляться.

    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

По какой-то причине я вижу, что код внутри onBeaconServiceConnect() выполняется дважды. Это может быть причиной. Дайте мне знать, почему это могло произойти.


person Avinash Gupta    schedule 25.12.2016    source источник
comment
Код выглядит нормально. Вы говорите, что продолжаете получать обратные вызовы, несмотря на прекращение ранжирования? Добавление строки журнала к обратному вызову ранжирования и прикрепление выдержки из LogCat может помочь выяснить, что происходит.   -  person davidgyoung    schedule 25.12.2016
comment
Да, я получаю обратные вызовы ранжирования даже после остановки ранжирования. Опубликовал LogCat, чтобы помочь вам лучше понять. Пожалуйста, взгляните.   -  person Avinash Gupta    schedule 25.12.2016


Ответы (1)


Я считаю, что проблема вызвана тем, что жизненный цикл Android создает несколько копий BlankFragment. Каждый раз, когда создается BlankFragment, вызывается метод onCreate, код привязывается к службе сканирования и начинает ранжирование. Поскольку несколько фрагментов создаются без вызова onDestroy, вы получаете несколько экземпляров одновременно. Когда вызывается stopScan, он останавливает диапазон только для последнего созданного экземпляра Fragment.

Одним из решений было бы прекратить ранжирование и мониторинг, а затем отменить привязку, когда фрагмент исчезнет из поля зрения.

person davidgyoung    schedule 25.12.2016
comment
Я думаю, вы правильно определили проблему. Я попытался привязаться к службам сканирования в onResume(), а затем остановить ранжирование, мониторинг и отвязку в onPause(), так что каждый раз, когда фрагмент выходит из поля зрения, он отвязывается. Но это не помогает. Вероятно, это связано с тем, что я использую FragmentPagerAdapter в действии для создания фрагментов, которые содержат ссылки на предыдущие копии. Каким-то образом он создает новые копии каждый раз, когда создается активность, и по-прежнему удерживает старые. Придется копать дальше, чтобы понять вопрос. Спасибо! - person Avinash Gupta; 25.12.2016
comment
Я копнул дальше и обнаружил, что проблема воспроизводится, когда я выхожу из приложения (если быть точным, действие, содержащее BlankFragment), а затем снова открываю его. Раньше я использовал BluetoothLeScanner для той же задачи и аналогичным образом управлял жизненным циклом, чтобы сканирование останавливалось, когда фрагмент больше не виден. Он работал нормально и не показывал проблем, которые отмечаются при использовании Android Beacon Library. Можете ли вы пролить свет на то, каковы различия в обоих способах сканирования? - person Avinash Gupta; 26.12.2016
comment
Я не уверен, почему у вас не было подобных проблем с BluetoothLeScanner. Чтобы сделать Fragment BeaconConsumer и обеспечить его работу, вам может потребоваться вручную сделать вызов, чтобы остановить сканирование каждого фрагмента, когда он выходит из поля зрения. Однако я думаю, что более простым способом сделать это было бы сделать Activity BeaconConsumer или даже POJO, ссылка на который принадлежит Activity. - person davidgyoung; 26.12.2016
comment
Я смог решить эту проблему, расширив службу BeaconConsumer. Тем не менее, я все еще не могу понять, какая разница в сканировании в обоих направлениях вызвала эту проблему. - person Avinash Gupta; 30.12.2016