[android] Convert GeoJSON features to tiles in background
Composing tiles from the GeoJSON features is an expensive operation that might block UI thread on updating the `GeoJsonSource` with the new data. This change moves tile composing to the background thread and thus unblocks the UI thread.
This commit is contained in:
@@ -60,6 +60,7 @@ public:
|
||||
void setGeoJSONData(std::shared_ptr<GeoJSONData>);
|
||||
|
||||
optional<std::string> getURL() const;
|
||||
const GeoJSONOptions& getOptions() const;
|
||||
|
||||
class Impl;
|
||||
const Impl& impl() const;
|
||||
|
||||
@@ -44,18 +44,16 @@ namespace android {
|
||||
}
|
||||
|
||||
GeoJSONSource::GeoJSONSource(jni::JNIEnv& env, const jni::String& sourceId, const jni::Object<>& options)
|
||||
: Source(env, std::make_unique<mbgl::style::GeoJSONSource>(
|
||||
jni::Make<std::string>(env, sourceId),
|
||||
convertGeoJSONOptions(env, options)))
|
||||
, converter(std::make_unique<Actor<FeatureConverter>>(Scheduler::GetBackground())) {
|
||||
}
|
||||
: Source(env,
|
||||
std::make_unique<mbgl::style::GeoJSONSource>(jni::Make<std::string>(env, sourceId),
|
||||
convertGeoJSONOptions(env, options))),
|
||||
converter(std::make_unique<Actor<FeatureConverter>>(Scheduler::GetBackground(),
|
||||
source.as<style::GeoJSONSource>()->getOptions())) {}
|
||||
|
||||
GeoJSONSource::GeoJSONSource(jni::JNIEnv& env,
|
||||
mbgl::style::Source& coreSource,
|
||||
AndroidRendererFrontend& frontend)
|
||||
: Source(env, coreSource, createJavaPeer(env), frontend)
|
||||
, converter(std::make_unique<Actor<FeatureConverter>>(Scheduler::GetBackground())) {
|
||||
}
|
||||
GeoJSONSource::GeoJSONSource(jni::JNIEnv& env, mbgl::style::Source& coreSource, AndroidRendererFrontend& frontend)
|
||||
: Source(env, coreSource, createJavaPeer(env), frontend),
|
||||
converter(std::make_unique<Actor<FeatureConverter>>(Scheduler::GetBackground(),
|
||||
source.as<style::GeoJSONSource>()->getOptions())) {}
|
||||
|
||||
GeoJSONSource::~GeoJSONSource() = default;
|
||||
|
||||
@@ -63,7 +61,7 @@ namespace android {
|
||||
|
||||
std::shared_ptr<std::string> json = std::make_shared<std::string>(jni::Make<std::string>(env, jString));
|
||||
|
||||
Update::Converter converterFn = [this, json](ActorRef<Callback> _callback) {
|
||||
Update::Converter converterFn = [this, json](ActorRef<GeoJSONDataCallback> _callback) {
|
||||
converter->self().invoke(&FeatureConverter::convertJson, json, _callback);
|
||||
};
|
||||
|
||||
@@ -84,11 +82,11 @@ namespace android {
|
||||
|
||||
void GeoJSONSource::setURL(jni::JNIEnv& env, const jni::String& url) {
|
||||
// Update the core source
|
||||
source.as<mbgl::style::GeoJSONSource>()->GeoJSONSource::setURL(jni::Make<std::string>(env, url));
|
||||
source.as<style::GeoJSONSource>()->setURL(jni::Make<std::string>(env, url));
|
||||
}
|
||||
|
||||
jni::Local<jni::String> GeoJSONSource::getURL(jni::JNIEnv& env) {
|
||||
optional<std::string> url = source.as<mbgl::style::GeoJSONSource>()->GeoJSONSource::getURL();
|
||||
optional<std::string> url = source.as<style::GeoJSONSource>()->getURL();
|
||||
return url ? jni::Make<jni::String>(env, *url) : jni::Local<jni::String>();
|
||||
}
|
||||
|
||||
@@ -166,7 +164,7 @@ namespace android {
|
||||
auto global = jni::NewGlobal<jni::EnvAttachingDeleter>(env, jObject);
|
||||
auto object = std::make_shared<decltype(global)>(std::move(global));
|
||||
|
||||
Update::Converter converterFn = [this, object](ActorRef<Callback> _callback) {
|
||||
Update::Converter converterFn = [this, object](ActorRef<GeoJSONDataCallback> _callback) {
|
||||
converter->self().invoke(&FeatureConverter::convertObject<JNIType>, object, _callback);
|
||||
};
|
||||
|
||||
@@ -175,25 +173,23 @@ namespace android {
|
||||
|
||||
void GeoJSONSource::setAsync(Update::Converter converterFn) {
|
||||
awaitingUpdate = std::make_unique<Update>(
|
||||
std::move(converterFn),
|
||||
std::make_unique<Actor<Callback>>(
|
||||
*Scheduler::GetCurrent(),
|
||||
[this](GeoJSON geoJSON) {
|
||||
// conversion from Java features to core ones finished
|
||||
android::UniqueEnv _env = android::AttachEnv();
|
||||
std::move(converterFn),
|
||||
std::make_unique<Actor<GeoJSONDataCallback>>(
|
||||
*Scheduler::GetCurrent(), [this](std::shared_ptr<style::GeoJSONData> geoJSONData) {
|
||||
// conversion from Java features to core ones finished
|
||||
android::UniqueEnv _env = android::AttachEnv();
|
||||
|
||||
// Update the core source
|
||||
source.as<mbgl::style::GeoJSONSource>()->GeoJSONSource::setGeoJSON(geoJSON);
|
||||
// Update the core source
|
||||
source.as<mbgl::style::GeoJSONSource>()->setGeoJSONData(std::move(geoJSONData));
|
||||
|
||||
// if there is an awaiting update, execute it, otherwise, release resources
|
||||
if (awaitingUpdate) {
|
||||
update = std::move(awaitingUpdate);
|
||||
update->converterFn(update->callback->self());
|
||||
} else {
|
||||
update.reset();
|
||||
}
|
||||
})
|
||||
);
|
||||
// if there is an awaiting update, execute it, otherwise, release resources
|
||||
if (awaitingUpdate) {
|
||||
update = std::move(awaitingUpdate);
|
||||
update->converterFn(update->callback->self());
|
||||
} else {
|
||||
update.reset();
|
||||
}
|
||||
}));
|
||||
|
||||
// If another update is running, wait
|
||||
if (update) {
|
||||
@@ -230,12 +226,10 @@ namespace android {
|
||||
);
|
||||
}
|
||||
|
||||
void FeatureConverter::convertJson(std::shared_ptr<std::string> json,
|
||||
ActorRef<Callback> callback) {
|
||||
void FeatureConverter::convertJson(std::shared_ptr<std::string> json, ActorRef<GeoJSONDataCallback> callback) {
|
||||
using namespace mbgl::style::conversion;
|
||||
|
||||
android::UniqueEnv _env = android::AttachEnv();
|
||||
|
||||
// Convert the jni object
|
||||
Error error;
|
||||
optional<GeoJSON> converted = parseGeoJSON(*json, error);
|
||||
@@ -243,23 +237,23 @@ namespace android {
|
||||
mbgl::Log::Error(mbgl::Event::JNI, "Error setting geo json: " + error.message);
|
||||
return;
|
||||
}
|
||||
|
||||
callback.invoke(&Callback::operator(), *converted);
|
||||
callback.invoke(&GeoJSONDataCallback::operator(), style::GeoJSONData::create(*converted, options));
|
||||
}
|
||||
|
||||
template <class JNIType>
|
||||
void FeatureConverter::convertObject(std::shared_ptr<jni::Global<jni::Object<JNIType>, jni::EnvAttachingDeleter>> jObject, ActorRef<Callback> callback) {
|
||||
void FeatureConverter::convertObject(
|
||||
std::shared_ptr<jni::Global<jni::Object<JNIType>, jni::EnvAttachingDeleter>> jObject,
|
||||
ActorRef<GeoJSONDataCallback> callback) {
|
||||
using namespace mbgl::android::geojson;
|
||||
|
||||
android::UniqueEnv _env = android::AttachEnv();
|
||||
// Convert the jni object
|
||||
auto geometry = JNIType::convert(*_env, *jObject);
|
||||
callback.invoke(&Callback::operator(), GeoJSON(geometry));
|
||||
callback.invoke(&GeoJSONDataCallback::operator(), style::GeoJSONData::create(geometry, options));
|
||||
}
|
||||
|
||||
Update::Update(Converter _converterFn, std::unique_ptr<Actor<Callback>> _callback)
|
||||
: converterFn(std::move(_converterFn))
|
||||
, callback(std::move(_callback)) {}
|
||||
Update::Update(Converter _converterFn, std::unique_ptr<Actor<GeoJSONDataCallback>> _callback)
|
||||
: converterFn(std::move(_converterFn)), callback(std::move(_callback)) {}
|
||||
|
||||
} // namespace android
|
||||
} // namespace mbgl
|
||||
|
||||
@@ -11,22 +11,28 @@
|
||||
namespace mbgl {
|
||||
namespace android {
|
||||
|
||||
using Callback = std::function<void (GeoJSON)>;
|
||||
using GeoJSONDataCallback = std::function<void(std::shared_ptr<style::GeoJSONData>)>;
|
||||
|
||||
struct FeatureConverter {
|
||||
void convertJson(std::shared_ptr<std::string>, ActorRef<Callback>);
|
||||
class FeatureConverter {
|
||||
public:
|
||||
explicit FeatureConverter(style::GeoJSONOptions options_) : options(std::move(options_)) {}
|
||||
void convertJson(std::shared_ptr<std::string>, ActorRef<GeoJSONDataCallback>);
|
||||
|
||||
template <class JNIType>
|
||||
void convertObject(std::shared_ptr<jni::Global<jni::Object<JNIType>, jni::EnvAttachingDeleter>>, ActorRef<Callback>);
|
||||
void convertObject(std::shared_ptr<jni::Global<jni::Object<JNIType>, jni::EnvAttachingDeleter>>,
|
||||
ActorRef<GeoJSONDataCallback>);
|
||||
|
||||
private:
|
||||
style::GeoJSONOptions options;
|
||||
};
|
||||
|
||||
struct Update {
|
||||
using Converter = std::function<void (ActorRef<Callback>)>;
|
||||
using Converter = std::function<void(ActorRef<GeoJSONDataCallback>)>;
|
||||
Converter converterFn;
|
||||
|
||||
std::unique_ptr<Actor<Callback>> callback;
|
||||
std::unique_ptr<Actor<GeoJSONDataCallback>> callback;
|
||||
|
||||
Update(Converter, std::unique_ptr<Actor<Callback>>);
|
||||
Update(Converter, std::unique_ptr<Actor<GeoJSONDataCallback>>);
|
||||
};
|
||||
|
||||
class GeoJSONSource : public Source {
|
||||
|
||||
@@ -45,6 +45,10 @@ optional<std::string> GeoJSONSource::getURL() const {
|
||||
return url;
|
||||
}
|
||||
|
||||
const GeoJSONOptions& GeoJSONSource::getOptions() const {
|
||||
return impl().getOptions();
|
||||
}
|
||||
|
||||
void GeoJSONSource::loadDescription(FileSource& fileSource) {
|
||||
if (!url) {
|
||||
loaded = true;
|
||||
|
||||
Reference in New Issue
Block a user