#include "source.hpp" #include "../android_conversion.hpp" #include "../../attach_env.hpp" #include #include #include // Java -> C++ conversion #include #include // C++ -> Java conversion #include "../conversion/property_value.hpp" #include // Core Sources #include #include #include #include // Android Source peers #include "geojson_source.hpp" #include "image_source.hpp" #include "raster_source.hpp" #include "unknown_source.hpp" #include "vector_source.hpp" #include "custom_geometry_source.hpp" #include "raster_dem_source.hpp" namespace mbgl { namespace android { static std::unique_ptr createSourcePeer(jni::JNIEnv& env, mbgl::style::Source& coreSource, AndroidRendererFrontend& frontend) { if (coreSource.is()) { return std::make_unique(env, *coreSource.as(), frontend); } else if (coreSource.is()) { return std::make_unique(env, *coreSource.as(), frontend); } else if (coreSource.is()) { return std::make_unique(env, *coreSource.as(), frontend); } else if (coreSource.is()) { return std::make_unique(env, *coreSource.as(), frontend); } else { return std::make_unique(env, coreSource, frontend); } } const jni::Object& Source::peerForCoreSource(jni::JNIEnv& env, mbgl::style::Source& coreSource, AndroidRendererFrontend& frontend) { if (!coreSource.peer.has_value()) { coreSource.peer = createSourcePeer(env, coreSource, frontend); } return coreSource.peer.get>()->javaPeer; } Source::Source(jni::JNIEnv& env, mbgl::style::Source& coreSource, const jni::Object& obj, AndroidRendererFrontend& frontend) : source(coreSource) , javaPeer(jni::NewGlobal(env, obj)) , rendererFrontend(&frontend) { } Source::Source(jni::JNIEnv&, std::unique_ptr coreSource) : ownedSource(std::move(coreSource)) , source(*ownedSource) { } Source::~Source() { // Before being added to a map, the Java peer owns this C++ peer and cleans // up after itself correctly through the jni native peer bindings. // After being added to the map, the ownership is flipped and the C++ peer has a strong reference // to it's Java peer, preventing the Java peer from being GC'ed. // In this case, the core source initiates the destruction, which requires releasing the Java peer, // while also resetting it's nativePtr to 0 to prevent the subsequent GC of the Java peer from // re-entering this dtor. if (ownedSource.get() == nullptr && javaPeer.get() != nullptr) { // Manually clear the java peer android::UniqueEnv env = android::AttachEnv(); static auto& javaClass = jni::Class::Singleton(*env); static auto nativePtrField = javaClass.GetField(*env, "nativePtr"); javaPeer.Set(*env, nativePtrField, (jlong) 0); javaPeer.reset(); } } jni::Local Source::getId(jni::JNIEnv& env) { return jni::Make(env, source.getID()); } jni::Local Source::getAttribution(jni::JNIEnv& env) { auto attribution = source.getAttribution(); return attribution ? jni::Make(env, attribution.value()) : jni::Make(env,""); } void Source::addToMap(JNIEnv& env, const jni::Object& obj, mbgl::Map& map, AndroidRendererFrontend& frontend) { // Check to see if we own the source first if (!ownedSource) { throw std::runtime_error("Cannot add source twice"); } // Add source to map and release ownership map.getStyle().addSource(std::move(ownedSource)); // Add peer to core source source.peer = std::unique_ptr(this); // Add strong reference to java source javaPeer = jni::NewGlobal(env, obj); rendererFrontend = &frontend; } bool Source::removeFromMap(JNIEnv&, const jni::Object&, mbgl::Map& map) { // Cannot remove if not attached yet if (ownedSource) { throw std::runtime_error("Cannot remove detached source"); } // Remove the source from the map and take ownership ownedSource = map.getStyle().removeSource(source.getID()); // The source may not be removed if any layers still reference it return ownedSource != nullptr; } void Source::releaseJavaPeer() { // We can't release the peer if the source was not removed from the map if (!ownedSource) { return; } // Release the peer relationships. These will be re-established when the source is added to a map assert(ownedSource->peer.has_value()); ownedSource->peer.get>().release(); ownedSource->peer.reset(); // Release the strong reference to the java peer assert(javaPeer); javaPeer.release(); rendererFrontend = nullptr; } void Source::registerNative(jni::JNIEnv& env) { // Lookup the class static auto& javaClass = jni::Class::Singleton(env); #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod(name) // Register the peer jni::RegisterNativePeer(env, javaClass, "nativePtr", METHOD(&Source::getId, "nativeGetId"), METHOD(&Source::getAttribution, "nativeGetAttribution") ); // Register subclasses GeoJSONSource::registerNative(env); ImageSource::registerNative(env); RasterSource::registerNative(env); UnknownSource::registerNative(env); VectorSource::registerNative(env); CustomGeometrySource::registerNative(env); RasterDEMSource::registerNative(env); } } // namespace android } // namespace mbgl