Mapbox源码分析之样式加载

简介:

通过源码,我们来一步步分析Mapbox地图引擎如何进行不同样式的数据加载的,这里是基于5.3.0的版本.

通过官网案例,我们知道在SDK中配置了不同的style样式给我们使用,通过配置不同style,便可加载不同的类型地图,那么我们便可以从setStyleUrl()这个方法着手.这里我们从MapView的setStyleUrl()方法看起

1
2
3
4
5
6
7
8
9
10
public void setStyleUrl(@NonNull String url) {
if (destroyed) {
return;
}
if (!isMapInitialized()) {
mapboxMapOptions.styleUrl(url);
return;
}
nativeMapView.setStyleUrl(url);
}

这里我们看到,他将相关配置直接往下传到底层了,从nativeMapView这个名字可以看出这是一个和原生相关的地图类,那么我们发现即使不传任何style,地图也会初始化一个默认样式的地图,那么这个默认地图是在哪配置的呢,我们使用AndroidStudio的Find Usages功能,我们看到MapboxMap这个类中的onStart()方法也使用了这个方法

1
2
3
4
5
6
7
8
void onStart() {
nativeMapView.update();
trackingSettings.onStart();
if (TextUtils.isEmpty(nativeMapView.getStyleUrl())) {
// if user hasn't loaded a Style yet
nativeMapView.setStyleUrl(Style.MAPBOX_STREETS);
}
}

我们继续Find Usages这个方法,看看在哪里调用的,我们发现在MapView的initialiseDrawingSurface()方法里初始化画布的同时,也初始化了地图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
mapRenderer = new GLSurfaceViewMapRenderer(getContext(), glSurfaceView) {
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
MapView.this.post(new Runnable() {
@Override
public void run() {
// Initialise only once
if (mapboxMap == null) {
initialiseMap();
mapboxMap.onStart();
}
}
});
super.onSurfaceCreated(gl, config);
}
};

到这里,加载默认样式已经很清晰了,我们继续看nativeMapView.setStyleUrl()方法

1
2
3
4
5
6
7
8
public void setStyleUrl(String url) {
if (isDestroyedOn("setStyleUrl")) {
return;
}
nativeSetStyleUrl(url);
}
private native void nativeSetStyleUrl(String url);

这里我们看到这里将配置简单的往底层传,便没做什么了,这里我们全局搜索一下nativeSetStyleUrl字符串,看到和NativeMapView对应的底层原生文件native_map_view.cpp中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void NativeMapView::registerNative(jni::JNIEnv& env) {
// Lookup the class
NativeMapView::javaClass = *jni::Class<NativeMapView>::Find(env).NewGlobalRef(env).release();
#define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name)
// Register the peer
jni::RegisterNativePeer<NativeMapView>(env, NativeMapView::javaClass, "nativePtr",
std::make_unique<NativeMapView, JNIEnv&, jni::Object<NativeMapView>, jni::Object<FileSource>, jni::Object<MapRenderer>, jni::jfloat>,
"nativeInitialize",
"nativeDestroy",
//省略
METHOD(&NativeMapView::setStyleUrl, "nativeSetStyleUrl"),
 //省略
);
}

这里我们看到这里将setStyleUrl()方法和nativeSetStyleUrl()方法进行绑定,我们看下底层NativeMapView的setStyleUrl()方法

1
2
3
void NativeMapView::setStyleUrl(jni::JNIEnv& env, jni::String url) {
map->getStyle().loadURL(jni::Make<std::string>(env, url));
}

这里我们看到它调用了MapView的getStyle()方法,拿到Map::Impl的Style对象,然后调用Style对象的loadURL()方法

1
2
3
style::Style& Map::getStyle() {
return *impl->style;
}

这里我们看下Style对象的loadURL()方法

1
2
3
void Style::loadURL(const std::string& url) {
impl->loadURL(url);
}

它又转给它自己的实现类的方法了,这里我们看下Style::Impl的loadURL()方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
void Style::Impl::loadURL(const std::string& url_) {
lastError = nullptr;
observer->onStyleLoading();
loaded = false;
url = url_;
styleRequest = fileSource.request(Resource::style(url), [this](Response res) {
// Once we get a fresh style, or the style is mutated, stop revalidating.
if (res.isFresh() || mutated) {
styleRequest.reset();
}
// Don't allow a loaded, mutated style to be overwritten with a new version.
if (mutated && loaded) {
return;
}
if (res.error) {
const std::string message = "loading style failed: " + res.error->message;
Log::Error(Event::Setup, message.c_str());
observer->onStyleError(std::make_exception_ptr(util::StyleLoadException(message)));
observer->onResourceError(std::make_exception_ptr(std::runtime_error(res.error->message)));
} else if (res.notModified || res.noContent) {
return;
} else {
parse(*res.data);
}
});
}

这里我们看到它首先通知观察者样式正在加载中,然后调用fileSource.request()方法,这里我们看下参数,一个通过Resource的style方法将url分类然后封装成Resource对象,另一个则是回调函数,用于处理获取的响应数据Response

1
2
3
4
5
6
Resource Resource::style(const std::string& url) {
return Resource {
Resource::Kind::Style,
url
};
}

这里我们往下看,看看如果处理请求的数据的,我们看到回调函数中有这么一行代码,parse(*res.data);从名字上看,这应该是解析数据的函数,我们看下具体实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
void Style::Impl::parse(const std::string& json_) {
Parser parser;
if (auto error = parser.parse(json_)) {
std::string message = "Failed to parse style: " + util::toString(error);
Log::Error(Event::ParseStyle, message.c_str());
observer->onStyleError(std::make_exception_ptr(util::StyleParseException(message)));
observer->onResourceError(error);
return;
}
mutated = false;
loaded = false;
json = json_;
sources.clear();
layers.clear();
images.clear();
transitionOptions = {};
transitionOptions.duration = util::DEFAULT_TRANSITION_DURATION;
for (auto& source : parser.sources) {
addSource(std::move(source));
}
for (auto& layer : parser.layers) {
addLayer(std::move(layer));
}
name = parser.name;
defaultCamera.center = parser.latLng;
defaultCamera.zoom = parser.zoom;
defaultCamera.angle = parser.bearing;
defaultCamera.pitch = parser.pitch;
setLight(std::make_unique<Light>(parser.light));
spriteLoaded = false;
spriteLoader->load(parser.spriteURL, scheduler, fileSource);
glyphURL = parser.glyphURL;
loaded = true;
observer->onStyleLoaded();
}

这里我们看到通过Parser.parse()方法将解析的json字符串进行解析,然后进行一些数据的配置,包括数据源,图层,精灵,字体,默认缩放等级和经纬度,方向等等,最后调用onStyleLoaded()方法通知观察者样式加载结束.