RxAndroid отказывается от дребезга карт Google при смене камеры

У меня есть приложение, которое использует карты Google и прослушивает смену камеры. Моя проблема в том, что при каждой смене камеры я должен запрашивать свой бэкэнд. Что я хочу сделать, так это просто ограничить количество запросов, используя RxAndroid/Java для устранения отказов.

Мой код выглядит так:

Observable.create(new Observable.OnSubscribe<CameraPosition>() {
        @Override
        public void call(Subscriber<? super CameraPosition> subscriber) {
            if (!subscriber.isUnsubscribed()) {
                map.setOnCameraChangeListener(new GoogleMap.OnCameraChangeListener() {
                    @Override
                    public void onCameraChange(CameraPosition cameraPosition) {
                        subscriber.onNext(cameraPosition);
                    }
                });
            }
        }
    }).subscribeOn(AndroidSchedulers.mainThread())
            .observeOn(AndroidSchedulers.mainThread())
            .onErrorResumeNext(Observable.<CameraPosition>empty())
            .debounce(1, TimeUnit.SECONDS)
            .subscribe(cameraPosition -> {
                final LatLngBounds item = map.getProjection().getVisibleRegion().latLngBounds;

                homeActionBarActivity.getNMB().getRide().list(
                        item.southwest.latitude,
                        item.southwest.longitude,
                        item.northeast.latitude,
                        item.northeast.longitude)
                        .subscribeOn(Schedulers.newThread())
                        .observeOn(AndroidSchedulers.mainThread())
                        .onErrorResumeNext(Observable.<List<Ride>>empty())
                        .subscribe(new Action1<List<Ride>>() {
                            @Override
                            public void call(List<Ride> rides) {

                                clearRidePointsFromMap();

                                for (Ride ride : rides) {
                                    if (isAdded())
                                        addRideStartPointToMap(ride);
                                }

                            }
                        });
            });

Как видите, я заставляю использовать MainThread для обоих (подписка на и наблюдение за), но я все еще получаю эту ошибку «Не в основном потоке».

Здесь трассировка стека:

01-27 10:59:24.049    3049-3066/com.nousmotards.android E/AndroidRuntime﹕ FATAL EXCEPTION: RxComputationThreadPool-2
    Process: com.nousmotards.android, PID: 3049
    java.lang.IllegalStateException: Exception thrown on Scheduler.Worker thread. Add `onError` handling.
            at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:52)
            at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:422)
            at java.util.concurrent.FutureTask.run(FutureTask.java:237)
            at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:152)
            at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:265)
            at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
            at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
            at java.lang.Thread.run(Thread.java:864)
     Caused by: rx.exceptions.OnErrorNotImplementedException: Not on the main thread
            at rx.Observable$31.onError(Observable.java:7134)
            at rx.observers.SafeSubscriber._onError(SafeSubscriber.java:154)
            at rx.observers.SafeSubscriber.onError(SafeSubscriber.java:111)
            at rx.observers.SafeSubscriber.onNext(SafeSubscriber.java:137)
            at rx.observers.SerializedObserver.onNext(SerializedObserver.java:159)
            at rx.observers.SerializedSubscriber.onNext(SerializedSubscriber.java:81)
            at rx.internal.operators.OperatorDebounceWithTime$DebounceState.emit(OperatorDebounceWithTime.java:128)
            at rx.internal.operators.OperatorDebounceWithTime$1$1.call(OperatorDebounceWithTime.java:72)
            at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:47)
            at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:422)
            at java.util.concurrent.FutureTask.run(FutureTask.java:237)
            at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:152)
            at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:265)
            at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
            at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
            at java.lang.Thread.run(Thread.java:864)
     Caused by: java.lang.IllegalStateException: Not on the main thread
            at com.google.k.a.cl.b(Unknown Source)
            at com.google.maps.api.android.lib6.c.ca.a(Unknown Source)
            at com.google.maps.api.android.lib6.c.el.l(Unknown Source)
            at com.google.android.gms.maps.internal.l.onTransact(SourceFile:312)
            at android.os.Binder.transact(Binder.java:361)
            at com.google.android.gms.maps.internal.IGoogleMapDelegate$a$a.getProjection(Unknown Source)
            at com.google.android.gms.maps.GoogleMap.getProjection(Unknown Source)
            at com.nousmotards.android.fragments.home.MapFragment.lambda$onViewCreated$33(MapFragment.java:117)
            at com.nousmotards.android.fragments.home.MapFragment.access$lambda$0(MapFragment.java)
            at com.nousmotards.android.fragments.home.MapFragment$$Lambda$1.call(Unknown Source)
            at rx.Observable$31.onNext(Observable.java:7139)
            at rx.observers.SafeSubscriber.onNext(SafeSubscriber.java:130)
            at rx.observers.SerializedObserver.onNext(SerializedObserver.java:159)
            at rx.observers.SerializedSubscriber.onNext(SerializedSubscriber.java:81)
            at rx.internal.operators.OperatorDebounceWithTime$DebounceState.emit(OperatorDebounceWithTime.java:128)
            at rx.internal.operators.OperatorDebounceWithTime$1$1.call(OperatorDebounceWithTime.java:72)
            at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:47)
            at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:422)
            at java.util.concurrent.FutureTask.run(FutureTask.java:237)
            at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:152)
            at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:265)
            at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
            at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
            at java.lang.Thread.run(Thread.java:864)

Есть ли у вас какие-либо идеи ?

Примечание: если я получаю map.setOnCameraChangeListerner(...) из Observable.create(...), он работает правильно.


person eVoxmusic    schedule 27.01.2015    source источник


Ответы (1)


Хорошо, кажется, что это (вероятно) связано с внутренним механизмом RxJava, который управляет передачей объектов от подписчика к наблюдателям. Я исправляю свою проблему, просто получая LatLngBounds внутри подписчика, а не наблюдателя.

Observable.create(new Observable.OnSubscribe<LatLngBounds>() {
    @Override
    public void call(Subscriber<? super LatLngBounds> subscriber) {
        if (!subscriber.isUnsubscribed()) {
            map.setOnCameraChangeListener(cameraPosition ->
                    subscriber.onNext(map.getProjection().getVisibleRegion().latLngBounds));
        }
    }
}).subscribeOn(AndroidSchedulers.mainThread())
        .observeOn(AndroidSchedulers.mainThread())
        .onErrorResumeNext(Observable.<LatLngBounds>empty())
        .debounce(1, TimeUnit.SECONDS)
        .subscribe(item -> {
            homeActionBarActivity.getNMB().getRide().list(
                    item.southwest.latitude,
                    item.southwest.longitude,
                    item.northeast.latitude,
                    item.northeast.longitude)
                    .subscribeOn(Schedulers.newThread())
                    .observeOn(AndroidSchedulers.mainThread())
                    .onErrorResumeNext(Observable.<List<Ride>>empty())
                    .subscribe(new Action1<List<Ride>>() {
                        @Override
                        public void call(List<Ride> rides) {

                            clearRidePointsFromMap();

                            for (Ride ride : rides) {
                                if (isAdded())
                                    addRideStartPointToMap(ride);
                            }

                        }
                    });
        });

Надеюсь, это поможет :)

person eVoxmusic    schedule 27.01.2015
comment
Была такая же проблема, этот подход решил ее. Вы случайно не поняли, почему это не работает? Я не уверен, что выполнение общих действий с пользовательским интерфейсом внутри подписчика так же безопасно, как и я. - person Robin; 29.08.2016