[android] use AAssetManager instead of libzip

This commit is contained in:
Konstantin Käfer
2017-04-11 17:18:09 +02:00
parent eed7dedf03
commit 197f0ca6b4
18 changed files with 135 additions and 189 deletions

View File

@@ -531,10 +531,6 @@ run-android-core-test-$1-%: android-core-test-$1
# Ensure clean state on the device
adb shell "rm -Rf $(MBGL_ANDROID_LOCAL_WORK_DIR) && mkdir -p $(MBGL_ANDROID_LOCAL_WORK_DIR)/test"
# Generate zipped asset files
cd test/fixtures/api && zip -r assets.zip assets && cd -
cd test/fixtures/storage && zip -r assets.zip assets && cd -
# Push all needed files to the device
adb push $(MBGL_ANDROID_CORE_TEST_DIR)/test.jar $(MBGL_ANDROID_LOCAL_WORK_DIR) > /dev/null 2>&1
adb push test/fixtures $(MBGL_ANDROID_LOCAL_WORK_DIR)/test > /dev/null 2>&1

View File

@@ -24,6 +24,9 @@ public:
DefaultFileSource(const std::string& cachePath,
const std::string& assetRoot,
uint64_t maximumCacheSize = util::DEFAULT_MAX_CACHE_SIZE);
DefaultFileSource(const std::string& cachePath,
std::unique_ptr<FileSource>&& assetFileSource,
uint64_t maximumCacheSize = util::DEFAULT_MAX_CACHE_SIZE);
~DefaultFileSource() override;
bool supportsOptionalRequests() const override {

View File

@@ -5,6 +5,7 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.Environment;
import android.support.annotation.NonNull;
import android.content.res.AssetManager;
import com.mapbox.mapboxsdk.Mapbox;
import com.mapbox.mapboxsdk.constants.MapboxConstants;
@@ -40,8 +41,7 @@ public class FileSource {
public static synchronized FileSource getInstance(Context context) {
if (INSTANCE == null) {
String cachePath = getCachePath(context);
String apkPath = context.getPackageCodePath();
INSTANCE = new FileSource(cachePath, apkPath);
INSTANCE = new FileSource(cachePath, context.getResources().getAssets());
}
return INSTANCE;
@@ -107,8 +107,8 @@ public class FileSource {
private long nativePtr;
private FileSource(String cachePath, String apkPath) {
initialize(Mapbox.getAccessToken(), cachePath, apkPath);
private FileSource(String cachePath, AssetManager assetManager) {
initialize(Mapbox.getAccessToken(), cachePath, assetManager);
}
public native void setAccessToken(@NonNull String accessToken);
@@ -127,7 +127,7 @@ public class FileSource {
*/
public native void setResourceTransform(final ResourceTransformCallback callback);
private native void initialize(String accessToken, String cachePath, String apkPath);
private native void initialize(String accessToken, String cachePath, AssetManager assetManager);
@Override
protected native void finalize() throws Throwable;

View File

@@ -16,7 +16,6 @@ if ((ANDROID_ABI STREQUAL "armeabi") OR (ANDROID_ABI STREQUAL "armeabi-v7a") OR
endif()
mason_use(jni.hpp VERSION 3.0.0 HEADER_ONLY)
mason_use(libzip VERSION 1.1.3)
mason_use(nunicode VERSION 1.7.1)
mason_use(sqlite VERSION 3.14.2)
mason_use(gtest VERSION 1.8.0)
@@ -34,8 +33,11 @@ macro(mbgl_platform_core)
# File source
PRIVATE platform/android/src/http_file_source.cpp
PRIVATE platform/android/src/asset_file_source.cpp
PRIVATE platform/android/src/asset_manager.hpp
PRIVATE platform/android/src/asset_manager_file_source.cpp
PRIVATE platform/android/src/asset_manager_file_source.hpp
PRIVATE platform/default/default_file_source.cpp
PRIVATE platform/default/asset_file_source.cpp
PRIVATE platform/default/local_file_source.cpp
PRIVATE platform/default/online_file_source.cpp
@@ -76,7 +78,6 @@ macro(mbgl_platform_core)
target_add_mason_package(mbgl-core PUBLIC sqlite)
target_add_mason_package(mbgl-core PUBLIC nunicode)
target_add_mason_package(mbgl-core PUBLIC libzip)
target_add_mason_package(mbgl-core PUBLIC geojson)
target_add_mason_package(mbgl-core PUBLIC jni.hpp)
target_add_mason_package(mbgl-core PUBLIC rapidjson)
@@ -305,10 +306,6 @@ macro(mbgl_platform_test)
PRIVATE -fvisibility=hidden
)
target_compile_definitions(mbgl-test
PRIVATE MBGL_ASSET_ZIP=1
)
target_link_libraries(mbgl-test
PRIVATE mbgl-android
PRIVATE -Wl,--gc-sections

View File

@@ -1,113 +0,0 @@
#include <mbgl/storage/asset_file_source.hpp>
#include <mbgl/storage/response.hpp>
#include <mbgl/util/util.hpp>
#include <mbgl/util/thread.hpp>
#include <mbgl/util/url.hpp>
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wshadow"
#include <zip.h>
#pragma GCC diagnostic pop
namespace {
struct ZipHolder {
ZipHolder(struct zip* archive_) : archive(archive_) {}
~ZipHolder() {
if (archive) ::zip_close(archive);
}
struct zip* archive;
};
struct ZipFileHolder {
ZipFileHolder(struct zip_file* file_) : file(file_) {}
~ZipFileHolder() {
if (file) ::zip_fclose(file);
}
struct zip_file* file;
};
}
namespace mbgl {
class AssetFileRequest : public AsyncRequest {
public:
AssetFileRequest(std::unique_ptr<WorkRequest> workRequest_)
: workRequest(std::move(workRequest_)) {
}
std::unique_ptr<WorkRequest> workRequest;
};
class AssetFileSource::Impl {
public:
Impl(const std::string& root_)
: root(root_) {
}
void request(const std::string& url, FileSource::Callback callback) {
ZipHolder archive = ::zip_open(root.c_str(), 0, nullptr);
if (!archive.archive) {
reportError(Response::Error::Reason::Other, "Could not open zip archive", callback);
return;
}
struct zip_stat stat;
::zip_stat_init(&stat);
std::string path = std::string("assets/") + mbgl::util::percentDecode(url.substr(8));
int ret = ::zip_stat(archive.archive, path.c_str(), 0, &stat);
if (ret < 0 || !(stat.valid & ZIP_STAT_SIZE)) {
reportError(Response::Error::Reason::NotFound, "Could not stat file in zip archive", callback);
return;
}
ZipFileHolder file = ::zip_fopen(archive.archive, path.c_str(), 0);
if (!file.file) {
reportError(Response::Error::Reason::NotFound, "Could not open file in zip archive", callback);
return;
}
std::shared_ptr<std::string> buf = std::make_shared<std::string>();
buf->resize(stat.size);
ret = ::zip_fread(file.file, &buf->front(), stat.size);
if (ret < 0) {
reportError(Response::Error::Reason::Other, "Could not read file in zip archive", callback);
return;
}
Response response;
response.data = buf;
callback(response);
}
void reportError(Response::Error::Reason reason, const char * message, FileSource::Callback callback) {
Response response;
response.error = std::make_unique<Response::Error>(reason, message);
callback(response);
}
private:
std::string root;
};
AssetFileSource::AssetFileSource(const std::string& root)
: thread(std::make_unique<util::Thread<Impl>>(
util::ThreadContext{"AssetFileSource", util::ThreadPriority::Low},
root)) {
}
AssetFileSource::~AssetFileSource() = default;
std::unique_ptr<AsyncRequest> AssetFileSource::request(const Resource& resource, Callback callback) {
return thread->invokeWithCallback(&Impl::request, resource.url, callback);
}
}

View File

@@ -0,0 +1,14 @@
#pragma once
namespace mbgl {
namespace android {
class AssetManager {
public:
static constexpr auto Name() {
return "android/content/res/AssetManager";
};
};
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,53 @@
#include "asset_manager_file_source.hpp"
#include <mbgl/storage/response.hpp>
#include <mbgl/util/util.hpp>
#include <mbgl/util/thread.hpp>
#include <mbgl/util/url.hpp>
#include <android/asset_manager.h>
#include <android/asset_manager_jni.h>
namespace mbgl {
class AssetManagerFileSource::Impl {
public:
Impl(AAssetManager* assetManager_) : assetManager(assetManager_) {
}
void request(const std::string& url, FileSource::Callback callback) {
// Note: AssetManager already prepends "assets" to the filename.
const std::string path = mbgl::util::percentDecode(url.substr(8));
Response response;
if (AAsset* asset = AAssetManager_open(assetManager, path.c_str(), AASSET_MODE_BUFFER)) {
response.data = std::make_shared<std::string>(
reinterpret_cast<const char*>(AAsset_getBuffer(asset)), AAsset_getLength64(asset));
AAsset_close(asset);
} else {
response.error = std::make_unique<Response::Error>(Response::Error::Reason::NotFound,
"Could not read asset");
}
callback(response);
}
private:
AAssetManager* assetManager;
};
AssetManagerFileSource::AssetManagerFileSource(jni::JNIEnv& env, jni::Object<android::AssetManager> assetManager_)
: assetManager(assetManager_.NewGlobalRef(env)),
thread(std::make_unique<util::Thread<Impl>>(
util::ThreadContext{"AssetManagerFileSource", util::ThreadPriority::Low},
AAssetManager_fromJava(&env, jni::Unwrap(**assetManager)))) {
}
AssetManagerFileSource::~AssetManagerFileSource() = default;
std::unique_ptr<AsyncRequest> AssetManagerFileSource::request(const Resource& resource, Callback callback) {
return thread->invokeWithCallback(&Impl::request, resource.url, callback);
}
} // namespace mbgl

View File

@@ -0,0 +1,28 @@
#pragma once
#include <mbgl/storage/file_source.hpp>
#include "asset_manager.hpp"
#include <jni/jni.hpp>
namespace mbgl {
namespace util {
template <typename T> class Thread;
} // namespace util
class AssetManagerFileSource : public FileSource {
public:
AssetManagerFileSource(jni::JNIEnv&, jni::Object<android::AssetManager>);
~AssetManagerFileSource() override;
std::unique_ptr<AsyncRequest> request(const Resource&, Callback) override;
private:
jni::UniqueObject<android::AssetManager> assetManager;
class Impl;
std::unique_ptr<util::Thread<Impl>> thread;
};
} // namespace mbgl

View File

@@ -2,21 +2,24 @@
#include <mbgl/util/logging.hpp>
#include <string>
#include "asset_manager_file_source.hpp"
#include "jni/generic_global_ref_deleter.hpp"
#include <string>
namespace mbgl {
namespace android {
// FileSource //
FileSource::FileSource(jni::JNIEnv& _env, jni::String accessToken, jni::String _cachePath, jni::String _apkPath) {
FileSource::FileSource(jni::JNIEnv& _env,
jni::String accessToken,
jni::String _cachePath,
jni::Object<AssetManager> assetManager) {
// Create a core default file source
fileSource = std::make_unique<mbgl::DefaultFileSource>(
jni::Make<std::string>(_env, _cachePath) + "/mbgl-offline.db",
jni::Make<std::string>(_env, _apkPath));
std::make_unique<AssetManagerFileSource>(_env, assetManager));
// Set access token
fileSource->setAccessToken(jni::Make<std::string>(_env, accessToken));
@@ -80,7 +83,7 @@ void FileSource::registerNative(jni::JNIEnv& env) {
// Register the peer
jni::RegisterNativePeer<FileSource>(
env, FileSource::javaClass, "nativePtr",
std::make_unique<FileSource, JNIEnv&, jni::String, jni::String, jni::String>,
std::make_unique<FileSource, JNIEnv&, jni::String, jni::String, jni::Object<AssetManager>>,
"initialize",
"finalize",
METHOD(&FileSource::getAccessToken, "getAccessToken"),

View File

@@ -2,6 +2,8 @@
#include <mbgl/storage/default_file_source.hpp>
#include "asset_manager.hpp"
#include <jni/jni.hpp>
namespace mbgl {
@@ -23,7 +25,7 @@ public:
static jni::Class<ResourceTransformCallback> javaClass;
};
FileSource(jni::JNIEnv&, jni::String, jni::String, jni::String);
FileSource(jni::JNIEnv&, jni::String, jni::String, jni::Object<AssetManager>);
~FileSource();

View File

@@ -13,7 +13,6 @@ extern JavaVM* theJVM;
extern std::string cachePath;
extern std::string dataPath;
extern std::string apkPath;
bool attach_jni_thread(JavaVM* vm, JNIEnv** env, std::string threadName);
void detach_jni_thread(JavaVM* vm, JNIEnv** env, bool detach);

View File

@@ -167,9 +167,15 @@ private:
DefaultFileSource::DefaultFileSource(const std::string& cachePath,
const std::string& assetRoot,
uint64_t maximumCacheSize)
: DefaultFileSource(cachePath, std::make_unique<AssetFileSource>(assetRoot), maximumCacheSize) {
}
DefaultFileSource::DefaultFileSource(const std::string& cachePath,
std::unique_ptr<FileSource>&& assetFileSource_,
uint64_t maximumCacheSize)
: thread(std::make_unique<util::Thread<Impl>>(util::ThreadContext{"DefaultFileSource", util::ThreadPriority::Low},
cachePath, maximumCacheSize)),
assetFileSource(std::make_unique<AssetFileSource>(assetRoot)),
assetFileSource(std::move(assetFileSource_)),
localFileSource(std::make_unique<LocalFileSource>()) {
}

View File

@@ -89,14 +89,7 @@ TEST(CustomLayer, Basic) {
HeadlessBackend backend { test::sharedDisplay() };
BackendScope scope { backend };
OffscreenView view { backend.getContext() };
#ifdef MBGL_ASSET_ZIP
// Regenerate with `cd test/fixtures/api/ && zip -r assets.zip assets/`
DefaultFileSource fileSource(":memory:", "test/fixtures/api/assets.zip");
#else
DefaultFileSource fileSource(":memory:", "test/fixtures/api/assets");
#endif
ThreadPool threadPool(4);
Map map(backend, view.getSize(), 1, fileSource, threadPool, MapMode::Still);

View File

@@ -29,13 +29,7 @@ TEST(API, TEST_REQUIRES_SERVER(RenderMissingTile)) {
HeadlessBackend backend { test::sharedDisplay() };
BackendScope scope { backend };
OffscreenView view { backend.getContext(), { 256, 512 } };
#ifdef MBGL_ASSET_ZIP
// Regenerate with `cd test/fixtures/api/ && zip -r assets.zip assets/`
DefaultFileSource fileSource(":memory:", "test/fixtures/api/assets.zip");
#else
DefaultFileSource fileSource(":memory:", "test/fixtures/api/assets");
#endif
ThreadPool threadPool(4);
Log::setObserver(std::make_unique<FixtureLogObserver>());

View File

@@ -25,13 +25,7 @@ TEST(API, RepeatedRender) {
HeadlessBackend backend { test::sharedDisplay() };
BackendScope scope { backend };
OffscreenView view { backend.getContext(), { 256, 512 } };
#ifdef MBGL_ASSET_ZIP
// Regenerate with `cd test/fixtures/api/ && zip -r assets.zip assets/`
DefaultFileSource fileSource(":memory:", "test/fixtures/api/assets.zip");
#else
DefaultFileSource fileSource(":memory:", "test/fixtures/api/assets");
#endif
ThreadPool threadPool(4);
Log::setObserver(std::make_unique<FixtureLogObserver>());

View File

@@ -356,12 +356,7 @@ TEST(Map, WithoutVAOExtension) {
test.backend.getContext().disableVAOExtension = true;
#ifdef MBGL_ASSET_ZIP
// Regenerate with `cd test/fixtures/api/ && zip -r assets.zip assets/`
DefaultFileSource fileSource(":memory:", "test/fixtures/api/assets.zip");
#else
DefaultFileSource fileSource(":memory:", "test/fixtures/api/assets");
#endif
Map map(test.backend, test.view.getSize(), 1, fileSource, test.threadPool, MapMode::Still);
map.setStyleJSON(util::read_file("test/fixtures/api/water.json"));
@@ -589,14 +584,7 @@ TEST(Map, TEST_DISABLED_ON_CI(ContinuousRendering)) {
BackendScope scope { backend };
OffscreenView view { backend.getContext() };
ThreadPool threadPool { 4 };
#ifdef MBGL_ASSET_ZIP
// Regenerate with `cd test/fixtures/api/ && zip -r assets.zip assets/`
DefaultFileSource fileSource(":memory:", "test/fixtures/api/assets.zip");
#else
DefaultFileSource fileSource(":memory:", "test/fixtures/api/assets");
#endif
Map map(backend, view.getSize(), 1, fileSource, threadPool, MapMode::Continuous);
using namespace std::chrono_literals;

View File

@@ -4,7 +4,7 @@
#include <TargetConditionals.h>
#endif
#if __ANDROID__
#if ANDROID
#define TEST_READ_ONLY 0
#define TEST_HAS_SERVER 0
#elif TARGET_OS_IOS

View File

@@ -6,28 +6,16 @@
#include <gtest/gtest.h>
namespace {
std::string getFileSourceRoot() {
#ifdef MBGL_ASSET_ZIP
// Regenerate with `cd test/fixtures/storage/ && zip -r assets.zip assets/`
return "test/fixtures/storage/assets.zip";
#else
return "test/fixtures/storage/assets";
#endif
}
} // namespace
using namespace mbgl;
#if !ANDROID
TEST(AssetFileSource, Load) {
util::RunLoop loop;
AssetFileSource fs(getFileSourceRoot());
AssetFileSource fs("test/fixtures/storage/assets");
// iOS seems to run out of file descriptors...
#if TARGET_OS_IPHONE || __ANDROID__
#if TARGET_OS_IPHONE
unsigned numThreads = 30;
#else
unsigned numThreads = 50;
@@ -91,7 +79,7 @@ TEST(AssetFileSource, Load) {
TEST(AssetFileSource, EmptyFile) {
util::RunLoop loop;
AssetFileSource fs(getFileSourceRoot());
AssetFileSource fs("test/fixtures/storage/assets");
std::unique_ptr<AsyncRequest> req = fs.request({ Resource::Unknown, "asset://empty" }, [&](Response res) {
req.reset();
@@ -107,7 +95,7 @@ TEST(AssetFileSource, EmptyFile) {
TEST(AssetFileSource, NonEmptyFile) {
util::RunLoop loop;
AssetFileSource fs(getFileSourceRoot());
AssetFileSource fs("test/fixtures/storage/assets");
std::unique_ptr<AsyncRequest> req = fs.request({ Resource::Unknown, "asset://nonempty" }, [&](Response res) {
req.reset();
@@ -123,7 +111,7 @@ TEST(AssetFileSource, NonEmptyFile) {
TEST(AssetFileSource, NonExistentFile) {
util::RunLoop loop;
AssetFileSource fs(getFileSourceRoot());
AssetFileSource fs("test/fixtures/storage/assets");
std::unique_ptr<AsyncRequest> req = fs.request({ Resource::Unknown, "asset://does_not_exist" }, [&](Response res) {
req.reset();
@@ -140,7 +128,7 @@ TEST(AssetFileSource, NonExistentFile) {
TEST(AssetFileSource, ReadDirectory) {
util::RunLoop loop;
AssetFileSource fs(getFileSourceRoot());
AssetFileSource fs("test/fixtures/storage/assets");
std::unique_ptr<AsyncRequest> req = fs.request({ Resource::Unknown, "asset://directory" }, [&](Response res) {
req.reset();
@@ -157,7 +145,7 @@ TEST(AssetFileSource, ReadDirectory) {
TEST(AssetFileSource, URLEncoding) {
util::RunLoop loop;
AssetFileSource fs(getFileSourceRoot());
AssetFileSource fs("test/fixtures/storage/assets");
std::unique_ptr<AsyncRequest> req = fs.request({ Resource::Unknown, "asset://%6eonempty" }, [&](Response res) {
req.reset();
@@ -169,3 +157,4 @@ TEST(AssetFileSource, URLEncoding) {
loop.run();
}
#endif