毕业设计笔记--Ubuntu 14.0.4下Mapbox源码分析1

12 篇文章 0 订阅

MapView是整个MapBox的核心,有必要先把MapView研究一下。

  • MapView的initialize方法其中调用了setWillNotDraw(false):自定义View中如果重写了onDraw()即自定义了绘制,那么就应该在构造函数中调用view的setWillNotDraw(false),设置该flag标志。其实默认该标志就是false。
  • MapView的getMapAsync(final OnMapReadyCallback callback) 方法,如果不调用这个方法那么默认世界地图会显示出来,调用该方法,回调实现为空,默认世界地图也会显示出来,如果参数传一个null,默认世界地图也会显示出来,也就是说getMapAsync方法与原始地图数据的加载没有关系,此方法主要是当地图数据准备好后或者说是MapboxMap准备好后,回调添加的接口。
  • 经过几天的断断续续的研读源码,终于发现,MapView默认加载的地图的设置是这一句话:
    private String styleUrl = Style.MAPBOX_STREETS;
    请求网络是HTTPRequest类(Java C++代码中也有相应的类),下面分析一下,整个加载流程:
    1. Mapbox请求网络用的是OKHttp, HTTPRequest类实现了okhttp3.Callback接口,然后实现了该接口的两个方法
      void onFailure(Call call, IOException e);
      其中调用了private native void nativeOnFailure(int type, String message)这个C++实现的方法;

      void onResponse(Call call, Response response) throws IOException;
      其中调用了private native void nativeOnResponse(int code, String etag, String modified, String cacheControl, String expires, String retryAfter, String xRateLimitReset, byte[] body) 这个C++实现的方法。

      需要注意的是这个类的构造方法是私有的,(并且此构造函数中是进行了网络请求的,下面会贴代码)不可能通过new来构造这个类,那么八成是通过反射来构造这个类。下面看C++的代码:
      http_file_source.cpp这个类有这么一行代码:
      static constexpr auto Name() { return “com/mapbox/mapboxsdk/http/HTTPRequest”; }; 可以猜测,这极有可能是通过这个类名来反射的。


      注意,使用JNI目前有两种方式,一种是遵守JNI规范的函数命名规范(应用层多采用,这层也是大多数人一开始学习JNI接触到的),一种是采用函数注册的方式(应用框架层多采用)。Mapbox目前我发现的都是采用第二种函数注册的方式。


      继续,前面说到HTTPRequest极有可能是通过反射来实例化的,
      在http_file_source.cpp这个类中有一个类HTTPRequest(注意此类继承自AsyncRequest) 还有一个函数void RegisterNativeHTTPRequest(jni::JNIEnv& env) 现在只需要知道,这个函数是用来关联Java层与C/C++层函数的,在main.cpp中extern “C” JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved)方法调用的,当在系统中调用System.loadLibrary函数时,该函数会找到对应的动态库,然后首先试图找到”JNI_OnLoad”函数,如果该函数存在,则调用它。JNI_OnLoad可以和JNIEnv的registerNatives函数结合起来,实现动态的函数替换。这不是本文的重点,本文不在分析。
void RegisterNativeHTTPRequest(jni::JNIEnv& env) {
 1.   HTTPRequest::javaClass = *jni::Class<HTTPRequest>::Find(env).NewGlobalRef(env).release();//release()方法是智能指针的控制权的释放,有兴趣的同学可以看一下C++的智能指针。

 2.   #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name)

 3.   jni::RegisterNativePeer<HTTPRequest>(env, HTTPRequest::javaClass, "mNativePtr",
        METHOD(&HTTPRequest::onFailure, "nativeOnFailure"),
        METHOD(&HTTPRequest::onResponse, "nativeOnResponse"));
}

上面是RegisterNativeHTTPRequest方法,第1部分代码其实就是C++代码根据类名找到Java层对应的类(反射),这一行代码依次调用顺序为:

class.hpp中
static Class Find(JNIEnv& env)
{
return Class(FindClass(env, TagType::Name()));
}

 //TagType::Name() 其实调用的就是上面说到的http_file_source.cpp中的方法static constexpr auto Name() { return "com/mapbox/mapboxsdk/http/HTTPRequest"; };最终结果HTTPRequest::javaClass指向了Java类HTTPRequest

functions.hpp中
inline jclass& FindClass(JNIEnv& env, const char* name)
{
 return *CheckJavaException(env, Wrap<jclass*>(env.FindClass(name)));
}

template < class T >
UniqueGlobalRef<T> NewGlobalRef(JNIEnv& env, T* t)
{
jobject* obj = Wrap<jobject*>(env.NewGlobalRef(Unwrap(t)));
 CheckJavaException(env);
  if (t && !obj)
    throw std::bad_alloc();
  return UniqueGlobalRef<T>(reinterpret_cast<T*>(obj), GlobalRefDeleter(env));
}

owenrship.hpp中
template < class T >
using UniqueGlobalRef = std::unique_ptr< T, GlobalRefDeleter >;
//注意,Mapbox的C++层代码中使用了大量的智能指针,像上面一样,如果阅读有困难,可能先了解一下C++智能指针的相关语法知识。

第2部分的代码就是一个define宏定义,
第3部分的代码也很简单,就是将java层HTTPRequest.java中的native方法nativeOnFailure、nativeOnResponse对应至C++中的HTTPRequest::onFailure、HTTPRequest::onResponse方法,变量mNativePtr代表的是Java层中HTTPRequest对应C++层HTTPRequest对象的地址,下面是方法体:

native_method.hpp中:
template < class Peer, class TagType, class... Methods >
 void RegisterNativePeer(JNIEnv& env, const Class<TagType>& clazz, const char* fieldName, Methods&&... methods)
{
   static Field<TagType, jni::jlong> field { env, clazz, fieldName };
   RegisterNatives(env, clazz, methods.template operator()<Peer>(field)...);
}

functions.hpp中
template < class... Methods >
inline void RegisterNatives(JNIEnv& env, jclass& clazz, const Methods&... methods)
{
     ::JNINativeMethod unwrapped[sizeof...(methods)] = { Unwrap(methods)... };
    CheckJavaExceptionThenErrorCode(env,
env.RegisterNatives(Unwrap(clazz), unwrapped, sizeof...(methods)));
}

分析到这里,大家应该能明白,当调用System.loadLibrary函数时,java层与C++层各种方法字段就关联起来了。

下面来看http_file_source.cpp中的HTTPRequest类的构造函数:

HTTPRequest::HTTPRequest(jni::JNIEnv& env, const Resource& resource_, FileSource::Callback callback_)
    : resource(resource_),
      callback(callback_) {
    std::string etagStr;
    std::string modifiedStr;

    if (resource.priorEtag) {
        etagStr = *resource.priorEtag;
    } else if (resource.priorModified) {
        modifiedStr = util::rfc1123(*resource.priorModified);
    }

    jni::UniqueLocalFrame frame = jni::PushLocalFrame(env, 10);

    static auto constructor =
        javaClass.GetConstructor<jni::jlong, jni::String, jni::String, jni::String>(env);//取得Java类HTTPRequest的构造函数

    javaRequest = javaClass.New(env, constructor,reinterpret_cast<jlong>(this),
jni::Make<jni::String>(env, resource.url),
jni::Make<jni::String>(env, etagStr),
jni::Make<jni::String>(env,modifiedStr)).NewGlobalRef(env);//实例化Java层中的HTTRequest对象。
}

至此java层HTTRequest对象已经构造成功,验证了前面说的通过反射来构造这个类的假设。注意,java层HTTPRequest类的构造函数中作了网络请求,看代码:

java层HTTPRequest类中:

private HTTPRequest(long nativePtr, String resourceUrl, String etag, String modified) {
        mNativePtr = nativePtr;

        try {
            // Don't try a request if we aren't connected
            if (!MapboxAccountManager.getInstance().isConnected()) {
                throw new NoRouteToHostException("No Internet connection available.");
            }

            HttpUrl httpUrl = HttpUrl.parse(resourceUrl);
            System.out.println("----------ssa----------"+httpUrl+"-------------");
            final String host = httpUrl.host().toLowerCase(MapboxConstants.MAPBOX_LOCALE);
            if (host.equals("mapbox.com") || host.endsWith(".mapbox.com") || host.equals("mapbox.cn") || host.endsWith(".mapbox.cn")) {
                if (httpUrl.querySize() == 0) {
                    resourceUrl = resourceUrl + "?";
                } else {
                    resourceUrl = resourceUrl + "&";
                }
                resourceUrl = resourceUrl + "events=true";
            }
            System.out.println("---------ssa-----------"+resourceUrl+"-------------");
            Request.Builder builder = new Request.Builder()
                    .url(resourceUrl)
                    .tag(resourceUrl.toLowerCase(MapboxConstants.MAPBOX_LOCALE))
                    .addHeader("User-Agent", getUserAgent());
            if (etag.length() > 0) {
                builder = builder.addHeader("If-None-Match", etag);
            } else if (modified.length() > 0) {
                builder = builder.addHeader("If-Modified-Since", modified);
            }
            mRequest = builder.build();
            mCall = mClient.newCall(mRequest);
            mCall.enqueue(this);
        } catch (Exception e) {
            onFailure(e);
        }
 }
  • 上面主要是介绍了一些细节点,但是倒底是什么时候创建了C++层的HTTPRequest对象呢?现在按顺序分析一下整个请求网络,拉取矢量数据的过程,希望能从中找到答案:
    1. 上面说到MapView默认加载的地图的设置是这一句话:
      private String styleUrl = Style.MAPBOX_STREETS;
      下面看一下MapView.java中设置styleUrl的代码:
public void setStyleUrl(@NonNull String url) {
       if (destroyed) {
            return;
       }

// stopgap for https://github.com/mapbox/mapbox-gl-native/issues/6242
       if (TextUtils.isEmpty(nativeMapView.getAccessToken())) {
            setAccessToken(MapboxAccountManager.getInstance().getAccessToken());
        }

        styleUrl = url;
        nativeMapView.setStyleUrl(url);//调用了NativeMapView.java中的setSytleUrl方法。
        styleWasSet = true;
}

NativeMapView.java中:
public void setStyleUrl(String url) {
     nativeSetStyleUrl(nativeMapViewPtr, url);
}
private native void nativeSetStyleUrl(long nativeMapViewPtr, String url);
//又到了一个native方法。。。
  1. 追踪nativeSetStyleUrl方法
jni.cpp中

void nativeSetStyleUrl(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jni::jstring* url) {
    assert(nativeMapViewPtr != 0);
    NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
    nativeMapView->getMap().setStyleURL(std_string_from_jstring(env, url));//nativeMapView->getMap()得到的是Map,此处调用的是map.cpp中的setStyleURL方法。其中native_map_view.cpp中有相关代码,见下面:
}
native_map_view.cpp相关代码:
在NativeMapView的构造函数中有以下代码
fileSource = std::make_unique<mbgl::DefaultFileSource>(
        mbgl::android::cachePath + "/mbgl-offline.db",
        mbgl::android::apkPath);

map = std::make_unique<mbgl::Map>(
        *this, mbgl::Size{ static_cast<uint32_t>(width), static_cast<uint32_t>(height) },
        pixelRatio, *fileSource, threadPool, MapMode::Continuous);
//上面的代码相当于初始化了map与fileSource(为mbgl::DefaultFileSource)

map.cpp中:

void Map::setStyleURL(const std::string& url) {
    if (impl->styleURL == url) {
        return;
    }

    impl->loading = true;

    impl->backend.notifyMapChange(MapChangeWillStartLoadingMap);

    impl->styleRequest = nullptr;
    impl->styleURL = url;
    impl->styleJSON.clear();
    impl->styleMutated = false;

    impl->style = std::make_unique<Style>(impl->fileSource, impl->pixelRatio);//make_unique也是智能指针中的内容

//下面的impl->fileSource即是上面提到的mbgl::DefaultFileSource,在default_file_source.cpp中
    impl->styleRequest = impl->fileSource.request(Resource::style(impl->styleURL), [this](Response res) {
        // Once we get a fresh style, or the style is mutated, stop revalidating.
        if (res.isFresh() || impl->styleMutated) {
            impl->styleRequest.reset();
        }

        // Don't allow a loaded, mutated style to be overwritten with a new version.
        if (impl->styleMutated && impl->style->loaded) {
            return;
        }

        if (res.error) {
            if (res.error->reason == Response::Error::Reason::NotFound &&
                util::mapbox::isMapboxURL(impl->styleURL)) {
                Log::Error(Event::Setup, "style %s could not be found or is an incompatible legacy map or style", impl->styleURL.c_str());
            } else {
                Log::Error(Event::Setup, "loading style failed: %s", res.error->message.c_str());
            }
            impl->onStyleError();
            impl->onResourceError(std::make_exception_ptr(std::runtime_error(res.error->message)));
        } else if (res.notModified || res.noContent) {
            return;
        } else {
            impl->loadStyleJSON(*res.data);
        }
    });//上面很明显做了一个网络请求
}

default_file_source.cpp中request方法

std::unique_ptr<AsyncRequest> DefaultFileSource::request(const Resource& resource, Callback callback) {
    class DefaultFileRequest : public AsyncRequest {
    public:
        DefaultFileRequest(Resource resource_, FileSource::Callback callback_, util::Thread<DefaultFileSource::Impl>& thread_)
            : thread(thread_),
              workRequest(thread.invokeWithCallback(&DefaultFileSource::Impl::request, this, resource_, callback_)) {
        }

        ~DefaultFileRequest() override {
            thread.invoke(&DefaultFileSource::Impl::cancel, this);
        }

        util::Thread<DefaultFileSource::Impl>& thread;
        std::unique_ptr<AsyncRequest> workRequest;
    };

    if (isAssetURL(resource.url)) {
        return assetFileSource->request(resource, callback);
    } else if (LocalFileSource::acceptsURL(resource.url)) {
        return localFileSource->request(resource, callback);
    } else {
        return std::make_unique<DefaultFileRequest>(resource, callback, *thread);
    }
}
  1. 上面已经说到了impl->fileSource即是mbgl::DefaultFileSource,所以现在我们需要看DefaultFileSource的request方法:
default_file_source.cpp中:

std::unique_ptr<AsyncRequest> DefaultFileSource::request(const Resource& resource, Callback callback) {
    class DefaultFileRequest : public AsyncRequest { public: DefaultFileRequest(Resource resource_, FileSource::Callback callback_, util::Thread<DefaultFileSource::Impl>& thread_) : thread(thread_), workRequest(thread.invokeWithCallback(&DefaultFileSource::Impl::request, this, resource_, callback_)) { }

        ~DefaultFileRequest() override {
            thread.invoke(&DefaultFileSource::Impl::cancel, this);
        }

        util::Thread<DefaultFileSource::Impl>& thread;
        std::unique_ptr<AsyncRequest> workRequest;
    };

    if (isAssetURL(resource.url)) {
        return assetFileSource->request(resource, callback); } else if (LocalFileSource::acceptsURL(resource.url)) { return localFileSource->request(resource, callback);
    } else {
        Log::Info(Event::General, "----sunshanai---DefaultFileRequest::resource.url: %s----", resource.url.c_str());
        return std::make_unique<DefaultFileRequest>(resource, callback, *thread);
    }
}
该request方法返回了一个指向DefaultFileRequest的智能指针。然后,它会新起一个线程继续调用DefaultFileRequest::implrequest方法
    void request(AsyncRequest* req, Resource resource, Callback callback) {
        Resource revalidation = resource; const bool hasPrior = resource.priorEtag || resource.priorModified || resource.priorExpires; if (!hasPrior || resource.necessity == Resource::Optional) { auto offlineResponse = offlineDatabase.get(resource);

            if (resource.necessity == Resource::Optional && !offlineResponse) { // Ensure there's always a response that we can send, so the caller knows that // there's no optional data available in the cache. offlineResponse.emplace();
                offlineResponse->noContent = true; offlineResponse->error = std::make_unique<Response::Error>( Response::Error::Reason::NotFound, "Not found in offline database");
            }

            if (offlineResponse) {
                revalidation.priorModified = offlineResponse->modified; revalidation.priorExpires = offlineResponse->expires; revalidation.priorEtag = offlineResponse->etag; callback(*offlineResponse); } } if (resource.necessity == Resource::Required) { Log::Info(Event::General, "----sunshanai---onlineFileSource.request: %s----", resource.url.c_str());//log测试用,下面调用了onlineFileSource的request方法 tasks[req] = onlineFileSource.request(revalidation, [=] (Response onlineResponse) { this->offlineDatabase.put(revalidation, onlineResponse);Log::Info(Event::General, "----sunshanai---onlineResponse: %s----", resource.url.c_str());
                callback(onlineResponse); }); } } 下面看onlineFileSource的request方法 online_file_source.cpp中 std::unique_ptr<AsyncRequest> OnlineFileSource::request(const Resource& resource, Callback callback) { Resource res = resource;

    switch (resource.kind) { case Resource::Kind::Unknown: break;

    case Resource::Kind::Style: res.url = mbgl::util::mapbox::normalizeStyleURL(apiBaseURL, resource.url, accessToken);
        Log::Info(Event::General, "----sunshanai---normalizeStyleURL: %s----", res.url.c_str());
        break; case Resource::Kind::Source: res.url = util::mapbox::normalizeSourceURL(apiBaseURL, resource.url, accessToken);
        Log::Info(Event::General, "----sunshanai---normalizeSourceURL: %s----", res.url.c_str());
        break; case Resource::Kind::Glyphs: res.url = util::mapbox::normalizeGlyphsURL(apiBaseURL, resource.url, accessToken);
        break; case Resource::Kind::SpriteImage: case Resource::Kind::SpriteJSON: res.url = util::mapbox::normalizeSpriteURL(apiBaseURL, resource.url, accessToken);
        break; case Resource::Kind::Tile: res.url = util::mapbox::normalizeTileURL(apiBaseURL, resource.url, accessToken);
        break; } return std::make_unique<OnlineFileRequest>(std::move(res), std::move(callback), *impl);//返回了一个指向OnlineFileRequest的智能指针,此时会调用OnlineFileRequest的构造函数见下面: } OnlineFileRequest::OnlineFileRequest(Resource resource_, Callback callback_, OnlineFileSource::Impl& impl_) : impl(impl_), resource(std::move(resource_)), callback(std::move(callback_)) { impl.add(this);

    // Force an immediate first request if we don't have an expiration time. if (resource.priorExpires) { schedule(resource.priorExpires); } else { std::string abc = "sunshanai_OnlineFileRequest_schedule";
        Log::Info(Event::General, "----sunshanai---OnlineFileRequest_schedule: %s----", abc.c_str());
        schedule(util::now()); //会执行这个schedule方法,见下面: } } void OnlineFileRequest::schedule(optional<Timestamp> expires) { if (impl.isPending(this) || impl.isActive(this)) { // There's already a request in progress; don't start another one. return;
    }

    // If we're not being asked for a forced refresh, calculate a timeout that depends on how many
    // consecutive errors we've encountered, and on the expiration time, if present.
    Duration timeout = std::min(
                            http::errorRetryTimeout(failedRequestReason, failedRequests, retryAfter),
                            http::expirationTimeout(expires, expiredRequests));

    if (timeout == Duration::max()) {
        return; } // Emulate a Connection error when the Offline mode is forced with // a really long timeout. The request will get re-triggered when // the NetworkStatus is set back to Online. if (NetworkStatus::Get() == NetworkStatus::Status::Offline) { failedRequestReason = Response::Error::Reason::Connection;
        failedRequests = 1; timeout = Duration::max();
    }

    timer.start(timeout, Duration::zero(), [&] {
        impl.activateOrQueueRequest(this);//接下来执行这个activateOrQueueRequest,方法体见下面: }); } void activateOrQueueRequest(OnlineFileRequest* request) { assert(allRequests.find(request) != allRequests.end()); assert(activeRequests.find(request) == activeRequests.end()); assert(!request->request); if (activeRequests.size() >= HTTPFileSource::maximumConcurrentRequests()) { queueRequest(request);
        } else {
            activateRequest(request);//接下来执行activateRequest方法,方法体见下面: } } void activateRequest(OnlineFileRequest* request) { activeRequests.insert(request); std::string abc = "sunshanai_activateRequest";
        Log::Info(Event::General, "----sunshanai---activateRequest: %s----", abc.c_str());
        //注意下面调用了httpFileSource.request方法,这里httpFileSource在文件http_file_source.cpp中 request->request = httpFileSource.request(request->resource, [=] (Response response) { activeRequests.erase(request); activatePendingRequest(); request->request.reset(); request->completed(response); }); assert(pendingRequestsMap.size() == pendingRequestsList.size()); } http_file_source.cpp中 std::unique_ptr<AsyncRequest> HTTPFileSource::request(const Resource& resource, Callback callback) { return std::make_unique<HTTPRequest>(*impl->env, resource, callback);
}

生成了一个指向HTTPRequestC++)的智能指针,至此,与前面的对应起来,其构造函数构造了JavaHTTPRequest类,进行了网络请求。

最后,又回到了开始的地方,map.cppsetStyleURL方法,一切从这里开始,又到这里结束,网络请求成功后,回调,然后最终调用方法impl->loadStyleJSON(*res.data);
这个方法就是数据的处理了,下一篇博客继续分析(还有关于SourceURL的的分析)。。。
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值