Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support shared observables instead of enforcing same single listener requirement as underlying googleMap instance #116

Open
kmayoral opened this issue Mar 26, 2024 · 2 comments
Labels
triage me I really want to be triaged. type: feature request ‘Nice-to-have’ improvement, new feature or different behavior or design.

Comments

@kmayoral
Copy link

Thank you for this library!

A humble request to this update would be to provide a method to share a single observable instance across multiple callers for every map event type. This could be done by providing a wrapper around the googleMap instance that tracks the inner observable instance state or a static weakreference keyed-by-googlemap-instance type of cache that tracks any previously created observable instances to share out.

In this way, the caller can have multiple streams referencing the same underlying observables without worrying about that streams which were subscribed to earlier no longer receiving emissions.

@kmayoral kmayoral added triage me I really want to be triaged. type: feature request ‘Nice-to-have’ improvement, new feature or different behavior or design. labels Mar 26, 2024
@wangela
Copy link
Member

wangela commented Mar 26, 2024

If you would like to upvote the priority of this issue, please comment below or react on the original post above with 👍 so we can see what is popular when we triage.

@kmayoral Thank you for opening this issue. 🙏
Please check out these other resources that might help you get to a resolution in the meantime:

This is an automated message, feel free to ignore.

@kmayoral
Copy link
Author

Here could be an example implementation (untested):

package com.google.maps.android.rx

import com.google.android.gms.maps.GoogleMap
import com.google.maps.android.rx.shared.MainThreadObservable
import io.reactivex.rxjava3.core.Observable
import java.util.WeakHashMap

/**
 * Utility class used to create shared observable instances that will be returned to callers for
 * as long as the stream is active and the associated google map is still loaded in memory.
 *
 * @param initialCapacity should be set to a value greater than the max number of GoogleMap
 * instances supported by the application. Defaults to 16 which should be more than enough for most
 * use cases.
 */
public class SharedObservableCache<T : MainThreadObservable<R>, R : Any>(
    initialCapacity: Int = 16,
) {
    private val streamCache = WeakHashMap<GoogleMap, Observable<R>>(initialCapacity)

    /**
     * Returns an existing observable instance stored in the observable cache, if it exists or
     * creates a new instance set to be removed from cache whenever the created observable is
     * disposed, and then makes it shareable before placing it into the cache to be referenced by
     * future callers.
     *
     * @param map The GoogleMap instance that we want to request event updates from
     * @param creator The function to invoke to create a new event listening observable if none is
     * present in the cache
     * @return The shareable version of the created or cache referenced observable that is safe to
     * reference across multiple callers
     */
    public fun <T : Observable<R>> getOrCreate(map: GoogleMap, creator: (GoogleMap) -> T): Observable<R> {
        return streamCache[map]
            ?: creator.invoke(map)
                .doOnDispose { synchronized(streamCache) { streamCache.remove(map) } }
                .share()
                .also {
                    synchronized(streamCache) {
                        streamCache[map] = it
                    }
                } as Observable<R>
    }
}

And an example of how it could be referenced in an existing implementation such as within GoogleMapCameraIdleObservable.kt:


// This is a static cache
private val sharedStreamCache by lazy {
    SharedObservableCache<GoogleMapCameraIdleObservable, Unit>()
}

/**
 * Creates an [Observable] that emits whenever the camera on this [GoogleMap] instance goes idle.
 *
 * The created [Observable] uses [GoogleMap.setOnCameraIdleListener] to listen to camera idle
 * events. Since only one listener at a time is allowed, only one Observable at a time can be used.
 */
public fun GoogleMap.cameraIdleEvents(): Observable<Unit> =
    GoogleMapCameraIdleObservable(this)

/**
 * Retrieves a shared [Observable] that emits whenever the camera on this [GoogleMap] instance
 * goes idle.
 *
 * The returned [Observable] will be an instance of [GoogleMapCameraIdleObservable] that has had
 * {@link io.reactivex.rxjava3.core.Observable#share()} called on it in order to allow multiple
 * concurrent rx streams to reference the same camera idle events.
 */
public fun GoogleMap.cameraIdleEventsShared(): Observable<Unit> =
    sharedStreamCache.getOrCreate(this, GoogleMap::cameraIdleEvents)

private class GoogleMapCameraIdleObservable(
    private val googleMap: GoogleMap
) : MainThreadObservable<Unit>() {
....

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
triage me I really want to be triaged. type: feature request ‘Nice-to-have’ improvement, new feature or different behavior or design.
Projects
None yet
Development

No branches or pull requests

2 participants