简体   繁体   中英

RxAndroid debounce google maps on camera change

I have an application which is using google maps and listening on camera change. My problem is that on each camera change I have to request my backend. What I want to do is just limiting the number of requests by using RxAndroid/Java to debounce.

My code looks like this:

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

                            }
                        });
            });

As you can see, I am forcing to use the MainThread for both (subscribeOn and observeOn) but I still get this error "Not on the main thread".

Here the stacktrace:

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)

Do you have any idea ?

Note: if I get out map.setOnCameraChangeListerner(...) from Observable.create(...) it works correctly.

Ok it seems that is (probably) due to the RxJava internal on how it is managed to pass objects from subscriber to observers. I fix my problem by simply getting LatLngBounds inside the subscriber and not the observer.

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

                        }
                    });
        });

Hope this will help :)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM