From 6b8d8f3906fce27bc2bfd71831e8330a4e20500b Mon Sep 17 00:00:00 2001 From: kongchuiyu Date: Thu, 10 Oct 2024 17:30:04 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=B0=86=E7=BD=91=E7=BB=9C=E8=AF=B7?= =?UTF-8?q?=E6=B1=82=E9=83=A8=E5=88=86=E4=BB=8Ets=E8=BF=81=E7=A7=BB?= =?UTF-8?q?=E5=88=B0capi?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../fast_image/src/main/cpp/CMakeLists.txt | 17 +- .../main/cpp/FastImageLoaderTurboModule.cpp | 31 --- .../src/main/cpp/FastImageLoaderTurboModule.h | 169 ------------ .../fast_image/src/main/cpp/FastImageNode.cpp | 9 + .../fast_image/src/main/cpp/FastImageNode.h | 1 + .../src/main/cpp/FastImagePackage.h | 3 - .../src/main/cpp/FastImageSourceResolver.cpp | 93 +++++++ .../src/main/cpp/FastImageSourceResolver.h | 67 +++++ .../cpp/FastImageViewComponentInstance.cpp | 50 ++-- .../main/cpp/FastImageViewComponentInstance.h | 12 +- .../main/cpp/RNCFastImageViewTurboModule.cpp | 102 +++++--- .../main/cpp/RNCFastImageViewTurboModule.h | 19 +- .../main/cpp/downloadUtils/DownloadUri.cpp | 39 +++ .../src/main/cpp/downloadUtils/DownloadUri.h | 17 ++ .../cpp/downloadUtils/HttpTaskProcessor.cpp | 171 ++++++++++++ .../cpp/downloadUtils/HttpTaskProcessor.h | 135 ++++++++++ .../src/main/cpp/downloadUtils/LRUCache.h | 94 +++++++ .../main/ets/FastImageLoaderTurboModule.ts | 132 ---------- .../src/main/ets/FastImagePackage.ts | 5 - .../FastRemoteImageLoader/RemoteImageCache.ts | 66 ----- .../RemoteImageDiskCache.ts | 94 ------- .../RemoteImageLoader.ts | 245 ------------------ .../RemoteImageLoaderError.ts | 2 - .../RemoteImageSource.ts | 42 --- .../main/ets/FastRemoteImageLoader/index.ts | 4 - .../main/ets/RNCFastImageViewTurboModule.ts | 21 +- harmony/fast_image/src/main/ets/TMSpecs.ts | 3 - 27 files changed, 756 insertions(+), 887 deletions(-) delete mode 100644 harmony/fast_image/src/main/cpp/FastImageLoaderTurboModule.cpp delete mode 100644 harmony/fast_image/src/main/cpp/FastImageLoaderTurboModule.h create mode 100644 harmony/fast_image/src/main/cpp/FastImageSourceResolver.cpp create mode 100644 harmony/fast_image/src/main/cpp/FastImageSourceResolver.h create mode 100644 harmony/fast_image/src/main/cpp/downloadUtils/DownloadUri.cpp create mode 100644 harmony/fast_image/src/main/cpp/downloadUtils/DownloadUri.h create mode 100644 harmony/fast_image/src/main/cpp/downloadUtils/HttpTaskProcessor.cpp create mode 100644 harmony/fast_image/src/main/cpp/downloadUtils/HttpTaskProcessor.h create mode 100644 harmony/fast_image/src/main/cpp/downloadUtils/LRUCache.h delete mode 100644 harmony/fast_image/src/main/ets/FastImageLoaderTurboModule.ts delete mode 100644 harmony/fast_image/src/main/ets/FastRemoteImageLoader/RemoteImageCache.ts delete mode 100644 harmony/fast_image/src/main/ets/FastRemoteImageLoader/RemoteImageDiskCache.ts delete mode 100644 harmony/fast_image/src/main/ets/FastRemoteImageLoader/RemoteImageLoader.ts delete mode 100644 harmony/fast_image/src/main/ets/FastRemoteImageLoader/RemoteImageLoaderError.ts delete mode 100644 harmony/fast_image/src/main/ets/FastRemoteImageLoader/RemoteImageSource.ts delete mode 100644 harmony/fast_image/src/main/ets/FastRemoteImageLoader/index.ts diff --git a/harmony/fast_image/src/main/cpp/CMakeLists.txt b/harmony/fast_image/src/main/cpp/CMakeLists.txt index 6c614d5cf..b2d036d3e 100644 --- a/harmony/fast_image/src/main/cpp/CMakeLists.txt +++ b/harmony/fast_image/src/main/cpp/CMakeLists.txt @@ -2,7 +2,20 @@ cmake_minimum_required(VERSION 3.13) set(CMAKE_VERBOSE_MAKEFILE on) -file(GLOB rnoh_fast_image_SRC CONFIGURE_DEPENDS *.cpp colorUtils/*.cpp) +set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR}) +include_directories(${NATIVERENDER_ROOT_PATH} + ${NATIVERENDER_ROOT_PATH}/include) + +if(DEFINED PACKAGE_FIND_FILE) + include(${PACKAGE_FIND_FILE}) +endif() +file(GLOB rnoh_fast_image_SRC CONFIGURE_DEPENDS *.cpp colorUtils/*.cpp downloadUtils/*.cpp) add_library(rnoh_fast_image SHARED ${rnoh_fast_image_SRC}) target_include_directories(rnoh_fast_image PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) -target_link_libraries(rnoh_fast_image PUBLIC rnoh) \ No newline at end of file +target_link_libraries(rnoh_fast_image PUBLIC rnoh) + +target_link_libraries(rnoh_fast_image PUBLIC librcp_c.so + libohfileuri.so libimage_source.so + libace_ndk.z.so ) + +target_link_directories(rnoh_fast_image PUBLIC ${HMOS_SDK_NATIVE}/sysroot/usr/lib/aarch64-linux-ohos) \ No newline at end of file diff --git a/harmony/fast_image/src/main/cpp/FastImageLoaderTurboModule.cpp b/harmony/fast_image/src/main/cpp/FastImageLoaderTurboModule.cpp deleted file mode 100644 index 6c2d06970..000000000 --- a/harmony/fast_image/src/main/cpp/FastImageLoaderTurboModule.cpp +++ /dev/null @@ -1,31 +0,0 @@ -#include "FastImageLoaderTurboModule.h" -#include "RNOH/ArkTSTurboModule.h" -#include "RNOH/RNInstanceCAPI.h" - -using namespace rnoh; -using namespace facebook; - -rnoh::FastImageLoaderTurboModule::FastImageLoaderTurboModule( - const ArkTSTurboModule::Context ctx, - const std::string name) - : rnoh::ArkTSTurboModule(ctx, name){ - methodMap_ = { - ARK_METHOD_METADATA(getConstants, 0), - ARK_ASYNC_METHOD_METADATA(getSize, 1), - ARK_ASYNC_METHOD_METADATA(getCacheFilePath, 1), - ARK_ASYNC_METHOD_METADATA(getSizeWithHeaders, 2), - ARK_ASYNC_METHOD_METADATA(prefetchImage, 2), - ARK_ASYNC_METHOD_METADATA(prefetchImageWithMetadata, 3), - ARK_ASYNC_METHOD_METADATA(queryCache, 1)}; - - if(m_FastImageSourceResolver){ - return; - } - - if(auto instance = m_ctx.instance.lock(); instance != nullptr){ - m_FastImageSourceResolver = std::make_shared(instance); - auto rnInstance = dynamic_cast(instance.get()); - rnInstance->addArkTSMessageHandler(m_FastImageSourceResolver); - } - -} diff --git a/harmony/fast_image/src/main/cpp/FastImageLoaderTurboModule.h b/harmony/fast_image/src/main/cpp/FastImageLoaderTurboModule.h deleted file mode 100644 index ebd898577..000000000 --- a/harmony/fast_image/src/main/cpp/FastImageLoaderTurboModule.h +++ /dev/null @@ -1,169 +0,0 @@ -#pragma once - -#include "Props.h" -#include "RNOH/ArkTSTurboModule.h" -#include -#include -#include "FastImageLoaderTurboModule.h" -#include "RNOH/ArkTSMessageHub.h" -#include "RNOH/Assert.h" -#include "RNOH/RNInstance.h" -#include "RNOH/TaskExecutor/TaskExecutor.h" - -constexpr char FAST_IMAGE_SOURCE_PENDING[] = "FAST_IMAGE_SOURCE_PENDING"; - -namespace rnoh { - -class JSI_EXPORT FastImageLoaderTurboModule : public ArkTSTurboModule { -public: - using Shared = std::shared_ptr; - - FastImageLoaderTurboModule(const ArkTSTurboModule::Context ctx, const std::string name); - - class FastImageSourceResolver : public ArkTSMessageHandler { - public: - using Shared = std::shared_ptr; - - FastImageSourceResolver(std::weak_ptr rnInstance) - : m_rnInstance(rnInstance) {} - - class ImageSourceUpdateListener { - public: - std::string observedUri; - - ImageSourceUpdateListener(FastImageSourceResolver::Shared const &FastImageSourceResolver) - : m_FastImageSourceResolver(FastImageSourceResolver){}; - - ~ImageSourceUpdateListener() { - if(m_FastImageSourceResolver != nullptr){ - m_FastImageSourceResolver->removeListener(this); - } - } - - virtual void onImageSourceCacheUpdate(std::string fileUri) = 0; - virtual void onImageSourceCacheDownloadFileFail() = 0; - - private: - FastImageSourceResolver::Shared const &m_FastImageSourceResolver; - }; - - std::string resolveImageSources(ImageSourceUpdateListener &listener, std::string uri) { - // Subscribe to get information about prefetched URIs. - if (auto it = remoteImageSourceMap.find(uri); - it == remoteImageSourceMap.end() || it->second == FAST_IMAGE_SOURCE_PENDING) { - auto resolvedFileUri = getPrefetchedImageFileUri(uri); - if (!resolvedFileUri.empty()) { - remoteImageSourceMap.emplace(uri, resolvedFileUri); - } - } - - if (auto it = remoteImageSourceMap.find(uri); it != remoteImageSourceMap.end()) { - if (it->second == FAST_IMAGE_SOURCE_PENDING) { - removeListener(&listener); - addListenerForURI(uri, &listener); - return ""; - } - uri = it->second; - } - - return uri; - } - - void addListenerForURI(const std::string &uri, ImageSourceUpdateListener *listener) { - listener->observedUri = uri; - auto it = uriListenersMap.find(uri); - if (it == uriListenersMap.end()) { - uriListenersMap.emplace(uri, std::initializer_list{listener}); - return; - } - if (std::find(it->second.begin(), it->second.end(), listener) != it->second.end()) { - return; - } - it->second.push_back(listener); - } - - void removeListenerForURI(const std::string &uri, ImageSourceUpdateListener *listener) { - auto it = uriListenersMap.find(uri); - if (it == uriListenersMap.end()) { - return; - } - auto &listeners = it->second; - auto listenerPos = std::find(listeners.begin(), listeners.end(), listener); - if (listenerPos != listeners.end()) { - listeners.erase(listenerPos); - if (listeners.empty()) { - uriListenersMap.erase(uri); - } - } - } - - void removeListener(ImageSourceUpdateListener *listener) { - removeListenerForURI(listener->observedUri, listener); - } - - void clearRemoteImageSourceMap(){ - remoteImageSourceMap.clear(); - } - - protected: - virtual void handleArkTSMessage(const Context& ctx) override { - if (ctx.messageName == "UPDATE_FAST_IMAGE_SOURCE_MAP") { - auto remoteUri = ctx.messagePayload["remoteUri"].asString(); - auto fileUri = ctx.messagePayload["fileUri"].asString(); - auto it = uriListenersMap.find(remoteUri); - if (it == uriListenersMap.end()) { - return; - } - auto &listeners = it->second; - remoteImageSourceMap.insert_or_assign(remoteUri, fileUri); - for (auto listener : listeners) { - listener->onImageSourceCacheUpdate(fileUri); - removeListenerForURI(remoteUri, listener); - } - } else if (ctx.messageName == "FAST_IMAGE_DOWNLOAD_FILE_FAIL") { - auto remoteUri = ctx.messagePayload["remoteUri"].asString(); - auto it = uriListenersMap.find(remoteUri); - if (it == uriListenersMap.end()) { - return; - } - auto &listeners = it->second; - for (auto listener : listeners) { - listener->onImageSourceCacheDownloadFileFail(); - removeListenerForURI(remoteUri, listener); - } - } - } - - private: - std::unordered_map> uriListenersMap; - std::unordered_map remoteImageSourceMap; - std::weak_ptr m_rnInstance; - - std::string getPrefetchedImageFileUri(std::string uri) { - auto rnInstance = m_rnInstance.lock(); - if (rnInstance == nullptr) { - return {}; - } - auto imageLoaderTurboModule = rnInstance->getTurboModule("FastImageLoader"); - if (imageLoaderTurboModule != nullptr) { - auto prefetchResult = imageLoaderTurboModule->callSync("getPrefetchResult", {uri}); - if (prefetchResult == "pending") { - // The uri will be updated in onMessageReceived method. - return FAST_IMAGE_SOURCE_PENDING; - } - if (prefetchResult != nullptr) { - return prefetchResult.asString(); - } - - } - return {}; - } - }; - - std::shared_ptr m_FastImageSourceResolver = nullptr; - -private: - -}; - -} // namespace rnoh \ No newline at end of file diff --git a/harmony/fast_image/src/main/cpp/FastImageNode.cpp b/harmony/fast_image/src/main/cpp/FastImageNode.cpp index 4fe9878c7..cdd80ca52 100644 --- a/harmony/fast_image/src/main/cpp/FastImageNode.cpp +++ b/harmony/fast_image/src/main/cpp/FastImageNode.cpp @@ -66,6 +66,15 @@ FastImageNode& FastImageNode::setSources(std::string const& uri, std::string pre } else { item = {.string = uri.c_str()}; } + + maybeThrow(NativeNodeApi::getInstance()->setAttribute( + m_nodeHandle, NODE_IMAGE_SRC, &item)); + return *this; +} + +FastImageNode& FastImageNode::setSources(ArkUI_DrawableDescriptor* image) { + ArkUI_AttributeItem item; + item = {.object = reinterpret_cast(image)}; maybeThrow(NativeNodeApi::getInstance()->setAttribute( m_nodeHandle, NODE_IMAGE_SRC, &item)); return *this; diff --git a/harmony/fast_image/src/main/cpp/FastImageNode.h b/harmony/fast_image/src/main/cpp/FastImageNode.h index 81cfc52c3..44b9241ff 100644 --- a/harmony/fast_image/src/main/cpp/FastImageNode.h +++ b/harmony/fast_image/src/main/cpp/FastImageNode.h @@ -24,6 +24,7 @@ class FastImageNode : public ArkUINode { FastImageNode(); ~FastImageNode(); FastImageNode& setSources(std::string const& uri, std::string prefix = ""); + FastImageNode& setSources(ArkUI_DrawableDescriptor* image); FastImageNode& setResizeMode(facebook::react::ImageResizeMode const& mode); FastImageNode& setTintColor(facebook::react::SharedColor const& sharedColor); FastImageNode& setBlur(facebook::react::Float blur); diff --git a/harmony/fast_image/src/main/cpp/FastImagePackage.h b/harmony/fast_image/src/main/cpp/FastImagePackage.h index e8f815dd8..321884ede 100644 --- a/harmony/fast_image/src/main/cpp/FastImagePackage.h +++ b/harmony/fast_image/src/main/cpp/FastImagePackage.h @@ -2,7 +2,6 @@ #include "ComponentDescriptors.h" #include "FastImageViewJSIBinder.h" #include "RNCFastImageViewTurboModule.h" -#include "FastImageLoaderTurboModule.h" #include "FastImageViewComponentInstance.h" namespace rnoh { @@ -24,8 +23,6 @@ class FastImageTurboModuleFactoryDelegate : public TurboModuleFactoryDelegate { SharedTurboModule createTurboModule(Context ctx, const std::string &name) const override { if (name == "RNCFastImageView") { return std::make_shared(ctx, name); - } else if (name == "FastImageLoader") { - return std::make_shared(ctx, name); } return nullptr; }; diff --git a/harmony/fast_image/src/main/cpp/FastImageSourceResolver.cpp b/harmony/fast_image/src/main/cpp/FastImageSourceResolver.cpp new file mode 100644 index 000000000..349752c72 --- /dev/null +++ b/harmony/fast_image/src/main/cpp/FastImageSourceResolver.cpp @@ -0,0 +1,93 @@ + +#include "FastImageSourceResolver.h" +#include +#include + +namespace rnoh { + +std::string FastImageSourceResolver::resolveImageSources(ImageSourceUpdateListener &listener, std::string uri) { + //先找是不是还在下载 + if(pendingSet.find(uri) != pendingSet.end()){ + removeListener(&listener); + addListenerForURI(uri, &listener); + return ""; + } + DownloadUri downloadUri; + std::string fileUri = downloadUri.getUri(uri); + bool exist = remoteImageSourceMap.get(fileUri); + if(exist){ + uri = "file://"+ fileCacheDir + fileUri; + } + + return uri; +} + +void FastImageSourceResolver::addListenerForURI(const std::string &uri, ImageSourceUpdateListener *listener) { + listener->observedUri = uri; + auto it = uriListenersMap.find(uri); + if (it == uriListenersMap.end()) { + uriListenersMap.emplace(uri, std::initializer_list{listener}); + return; + } + if (std::find(it->second.begin(), it->second.end(), listener) != it->second.end()) { + return; + } + it->second.push_back(listener); +} + +void FastImageSourceResolver::removeListenerForURI(const std::string &uri, ImageSourceUpdateListener *listener) { + auto it = uriListenersMap.find(uri); + if (it == uriListenersMap.end()) { + return; + } + auto &listeners = it->second; + auto listenerPos = std::find(listeners.begin(), listeners.end(), listener); + if (listenerPos != listeners.end()) { + listeners.erase(listenerPos); + if (listeners.empty()) { + uriListenersMap.erase(uri); + } + } +} + +void FastImageSourceResolver::removeListener(ImageSourceUpdateListener *listener) { + if(!listener->observedUri.empty()){ + removeListenerForURI(listener->observedUri, listener); + } +} + +void FastImageSourceResolver::imageDownloadComplete(std::string uri,std::string fileUri){ + auto pend = pendingSet.find(uri); + if(pend != pendingSet.end()){ + pendingSet.erase(uri); + } + + remoteImageSourceMap.put(fileUri, true); + auto it = uriListenersMap.find(uri); + if (it == uriListenersMap.end()) { + return; + } + + auto &listeners = it->second; + for (auto listener : listeners) { + listener->onImageSourceCacheUpdate(fileUri); + removeListenerForURI(uri, listener); + } +} +void FastImageSourceResolver::imageDownloadFail(std::string uri){ + auto pend = pendingSet.find(uri); + if(pend != pendingSet.end()){ + pendingSet.erase(uri); + } + + auto it = uriListenersMap.find(uri); + if (it == uriListenersMap.end()) { + return; + } + auto &listeners = it->second; + for (auto listener : listeners) { + listener->onImageSourceCacheDownloadFileFail(); + removeListenerForURI(uri, listener); + } +} +} \ No newline at end of file diff --git a/harmony/fast_image/src/main/cpp/FastImageSourceResolver.h b/harmony/fast_image/src/main/cpp/FastImageSourceResolver.h new file mode 100644 index 000000000..ffa6bb161 --- /dev/null +++ b/harmony/fast_image/src/main/cpp/FastImageSourceResolver.h @@ -0,0 +1,67 @@ + +#ifndef FASTIMAGESOURCERESOLVER_H +#define FASTIMAGESOURCERESOLVER_H + +#include "RNOH/RNInstance.h" +#include +#include +#include +#include "downloadUtils/DownloadUri.h" +#include "downloadUtils/LRUCache.h" + +namespace fs = std::filesystem; + +namespace rnoh { + +class FastImageSourceResolver { + public: + using Shared = std::shared_ptr; + + FastImageSourceResolver(std::string fileCacheDir) + : uriListenersMap(),fileCacheDir(fileCacheDir) + ,remoteImageSourceMap(128,[this](std::string fileUri,bool a) { + fs::path filePath = this->fileCacheDir + fileUri; + if (fs::exists(filePath) && fs::is_regular_file(filePath)) { + return fs::remove(filePath); + } + return false; + }){} + + class ImageSourceUpdateListener { + public: + std::string observedUri; + + ImageSourceUpdateListener(FastImageSourceResolver::Shared const &FastImageSourceResolver) + : m_FastImageSourceResolver(FastImageSourceResolver){}; + + ~ImageSourceUpdateListener() { + if(!m_FastImageSourceResolver){ + m_FastImageSourceResolver->removeListener(this); + } + } + + virtual void onImageSourceCacheUpdate(std::string image) = 0; + virtual void onImageSourceCacheDownloadFileFail() = 0; + + private: + FastImageSourceResolver::Shared const &m_FastImageSourceResolver; + }; + + std::string resolveImageSources(ImageSourceUpdateListener &listener, std::string uri); + void addListenerForURI(const std::string &uri, ImageSourceUpdateListener *listener); + void removeListenerForURI(const std::string &uri, ImageSourceUpdateListener *listener); + void removeListener(ImageSourceUpdateListener *listener); + + public: + void imageDownloadComplete(std::string uri,std::string fileUri); + void imageDownloadFail(std::string uri); + + std::unordered_map> uriListenersMap;//网络图片与image组件映射 + LRUCache remoteImageSourceMap;//网络图片uri与磁盘文件地址映射 + std::unordered_set pendingSet;//正在下载的文件 + std::string fileCacheDir; + + }; +} + +#endif //FASTIMAGESOURCERESOLVER_H \ No newline at end of file diff --git a/harmony/fast_image/src/main/cpp/FastImageViewComponentInstance.cpp b/harmony/fast_image/src/main/cpp/FastImageViewComponentInstance.cpp index f7fb567c9..ebc16c6bb 100644 --- a/harmony/fast_image/src/main/cpp/FastImageViewComponentInstance.cpp +++ b/harmony/fast_image/src/main/cpp/FastImageViewComponentInstance.cpp @@ -1,6 +1,6 @@ #include "FastImageViewComponentInstance.h" #include "FastImageSource.h" -#include "FastImageLoaderTurboModule.h" +#include "RNCFastImageViewTurboModule.h" #include "Props.h" #include #include @@ -14,9 +14,9 @@ const std::string RAWFILE_PREFIX = "resource://RAWFILE/assets/"; FastImageViewComponentInstance::FastImageViewComponentInstance(Context context) : CppComponentInstance(std::move(context)), - FastImageLoaderTurboModule::FastImageSourceResolver::ImageSourceUpdateListener( - (std::dynamic_pointer_cast( - m_deps->rnInstance.lock()->getTurboModule("FastImageLoader"))) + FastImageSourceResolver::ImageSourceUpdateListener( + (std::dynamic_pointer_cast( + m_deps->rnInstance.lock()->getTurboModule("RNCFastImageView"))) ->m_FastImageSourceResolver) { this->getLocalRootArkUINode().setNodeDelegate(this); this->getLocalRootArkUINode().setInterpolation(ARKUI_IMAGE_INTERPOLATION_HIGH); @@ -32,12 +32,12 @@ std::string FastImageViewComponentInstance::FindLocalCacheByUri(std::string cons return uri; } - auto turboModule = rnInstance->getTurboModule("FastImageLoader"); + auto turboModule = rnInstance->getTurboModule("RNCFastImageView"); if (!turboModule) { return uri; } - auto arkTsTurboModule = std::dynamic_pointer_cast(turboModule); + auto arkTsTurboModule = std::dynamic_pointer_cast(turboModule); if (!arkTsTurboModule) { return uri; } @@ -56,24 +56,24 @@ void FastImageViewComponentInstance::GetHeaderUri( m_eventEmitter->onFastImageError({}); return; } - auto turboModule = rnInstance->getTurboModule("FastImageLoader"); + auto turboModule = rnInstance->getTurboModule("RNCFastImageView"); if (!turboModule) { m_eventEmitter->onFastImageError({}); return; } - auto arkTsTurboModule = std::dynamic_pointer_cast(turboModule); + auto arkTsTurboModule = std::dynamic_pointer_cast(turboModule); if (!arkTsTurboModule) { m_eventEmitter->onFastImageError({}); return; } - std::vector args; - args.push_back(uri); - folly::dynamic args_header = folly::dynamic::object(); + std::map headersData; for (auto it = header.begin(); it != header.end(); ++it) { - args_header[it->name] = it->value; + headersData.insert_or_assign(it->name, it->value); } - args.push_back(args_header); - arkTsTurboModule->callSync("prefetchImage", args); + DownloadUri utils; + std::string fileUri = utils.getUri(uri); + arkTsTurboModule->m_FastImageSourceResolver->addListenerForURI(uri, this); + arkTsTurboModule->preload(uri,arkTsTurboModule->m_FastImageSourceResolver->fileCacheDir + fileUri,headersData); return; } @@ -128,14 +128,16 @@ void FastImageViewComponentInstance::onPropsChanged(SharedConcreteProps const &p this->m_isReload = false; this->m_source = props->source; m_uri = props->source.uri; - std::string uri = FindLocalCacheByUri(m_uri); + std::string cache = FindLocalCacheByUri(m_uri); this->getLocalRootArkUINode().setAutoResize(true); - if (!props->source.headers.empty()) { + if (!props->source.headers.empty() && cache.empty()) { GetHeaderUri(props->source.uri, props->source.headers); } else { - if(!uri.empty()){ - this->getLocalRootArkUINode().setSources(uri, getAbsolutePathPrefix(getBundlePath())); + if(!cache.empty()){ + this->getLocalRootArkUINode().setSources(cache); + } else { + this->getLocalRootArkUINode().setSources(m_uri, getAbsolutePathPrefix(getBundlePath())); } } @@ -163,8 +165,8 @@ void FastImageViewComponentInstance::onPropsChanged(SharedConcreteProps const &p } } -void FastImageViewComponentInstance::onImageSourceCacheUpdate(std::string fileUri) { - this->getLocalRootArkUINode().setSources(fileUri, getAbsolutePathPrefix(getBundlePath())); +void FastImageViewComponentInstance::onImageSourceCacheUpdate(std::string image) { + this->getLocalRootArkUINode().setSources("file://"+ image); } void FastImageViewComponentInstance::onImageSourceCacheDownloadFileFail() { if (m_eventEmitter == nullptr) { @@ -174,13 +176,6 @@ void FastImageViewComponentInstance::onImageSourceCacheDownloadFileFail() { m_eventEmitter->onFastImageLoadEnd({}); } -// void FastImageViewComponentInstance::onStateChanged(SharedConcreteState const& state) { -// CppComponentInstance::onStateChanged(state); -// auto vector = {state->getData().getImageSource()}; -// this->getLocalRootArkUINode().setSources(vector); -// this->getLocalRootArkUINode().setBlur(state->getData().getBlurRadius()); -// } - FastImageNode &FastImageViewComponentInstance::getLocalRootArkUINode() { return m_imageNode; } void FastImageViewComponentInstance::onProgress(uint32_t loaded, uint32_t total) { @@ -190,6 +185,7 @@ void FastImageViewComponentInstance::onProgress(uint32_t loaded, uint32_t total) } void FastImageViewComponentInstance::onComplete(float width, float height) { + if (m_eventEmitter == nullptr) { return; } diff --git a/harmony/fast_image/src/main/cpp/FastImageViewComponentInstance.h b/harmony/fast_image/src/main/cpp/FastImageViewComponentInstance.h index 7ee9f0e41..32f44e568 100644 --- a/harmony/fast_image/src/main/cpp/FastImageViewComponentInstance.h +++ b/harmony/fast_image/src/main/cpp/FastImageViewComponentInstance.h @@ -1,8 +1,10 @@ -#pragma once +#ifndef FASTIMAGEVIEWCOMPONENTINSTANCE_H +#define FASTIMAGEVIEWCOMPONENTINSTANCE_H #include "EventEmitters.h" #include "FastImageNode.h" -#include "FastImageLoaderTurboModule.h" +#include "RNCFastImageViewTurboModule.h" +#include "FastImageSourceResolver.h" #include "ShadowNodes.h" #include "RNOH/CppComponentInstance.h" #include "RNOH/arkui/ImageNode.h" @@ -10,7 +12,7 @@ namespace rnoh { class FastImageViewComponentInstance : public CppComponentInstance, public FastImageNodeDelegate, - public FastImageLoaderTurboModule::FastImageSourceResolver::ImageSourceUpdateListener { + public FastImageSourceResolver::ImageSourceUpdateListener { private: FastImageNode m_imageNode; std::string m_uri; @@ -38,7 +40,9 @@ class FastImageViewComponentInstance : public CppComponentInstance using namespace rnoh; using namespace facebook; +namespace fs = std::filesystem; static jsi::Value hostFunction_RNCFastImageViewTurboModule_preload(jsi::Runtime &rt, react::TurboModule &turboModule, const jsi::Value *args, size_t count) { @@ -44,64 +47,80 @@ static jsi::Value hostFunction_RNCFastImageViewTurboModule_preload(jsi::Runtime return jsi::Value::null(); } void RNCFastImageViewTurboModule::preload(facebook::jsi::Runtime &rt, const facebook::jsi::Value *args, size_t count) { - auto rnInstance = m_ctx.instance.lock(); - auto turboModule = rnInstance->getTurboModule("FastImageLoader"); - auto arkTsTurboModule = std::dynamic_pointer_cast(turboModule); + if(count == 0) + return; + facebook::jsi::Array sources = args[0].asObject(rt).asArray(rt); - for (int i = 0; i < sources.size(rt); i++) { auto value = sources.getValueAtIndex(rt, i); if (!value.isObject()) continue; auto source = value.getObject(rt); if (source.hasProperty(rt, "uri")) { - if (source.hasProperty(rt, "headers")) { - facebook::jsi::Value args[2] = {source.getProperty(rt, "uri"), source.getProperty(rt, "headers")}; - arkTsTurboModule->callAsync(rt, "prefetchImage", args, 2); + std::string uri = source.getProperty(rt, "uri").toString(rt).utf8(rt); + //磁盘有无缓存文件,有则不执行 + DownloadUri utils; + std::string fileUri = utils.getUri(uri); + bool diskCache = m_FastImageSourceResolver->remoteImageSourceMap.get(fileUri); + if (!diskCache) { + m_FastImageSourceResolver->pendingSet.insert(fileUri); } else { - facebook::jsi::Value args[1] = {source.getProperty(rt, "uri")}; - arkTsTurboModule->callAsync(rt, "prefetchImage", args, 1); + return; } + + fileUri = fileCacheDir + fileUri; + std::map headersData; + if (source.hasProperty(rt, "headers")) { + auto headers = source.getProperty(rt, "headers").asObject(rt); + headers.asHostObject(rt)->getPropertyNames(rt); + auto props = source.getProperty(rt, "headers").asObject(rt).getPropertyNames(rt); + int propsSize = props.size(rt); + for (int i =0; i& headers){ + HttpTaskProcessor* processor = new HttpTaskProcessor(); + processor->instance = m_FastImageSourceResolver; + processor->filePath_ = fileUri; + if (headers.size() == 0) { + processor->launchHttpRequest(uri.c_str()); + } else { + processor->launchHttpRequest(uri.c_str(),headers); + } +} + static jsi::Value hostFunction_RNCFastImageViewTurboModule_clearMemoryCache(jsi::Runtime &rt, react::TurboModule &turboModule, const jsi::Value *args, size_t count) { - return static_cast(turboModule).callAsync(rt, "clearMemoryCache", args, count); + auto self = static_cast(&turboModule); + return jsi::Value(self->clearMemoryCache()); +} + +bool RNCFastImageViewTurboModule::clearMemoryCache() { + return true; } static jsi::Value hostFunction_RNCFastImageViewTurboModule_clearDiskCache(jsi::Runtime &rt, react::TurboModule &turboModule, const jsi::Value *args, size_t count) { auto self = static_cast(&turboModule); - self->clearDiskCache(rt); - return self->callAsync(rt, "clearDiskCache", args, count); + return jsi::Value(self->clearDiskCache()); } -void RNCFastImageViewTurboModule::clearDiskCache(facebook::jsi::Runtime &rt) { - auto rnInstance = m_ctx.instance.lock(); - if (!rnInstance) { - return; - } - auto turboModule = rnInstance->getTurboModule("FastImageLoader"); - if (!turboModule) { - return; - } - - auto arkTsTurboModule = std::dynamic_pointer_cast(turboModule); - if (!arkTsTurboModule) { - return; - } - - if (!arkTsTurboModule->m_FastImageSourceResolver) { - return; - } else { - arkTsTurboModule->m_FastImageSourceResolver->clearRemoteImageSourceMap(); - return; - } +bool RNCFastImageViewTurboModule::clearDiskCache() { + return this->m_FastImageSourceResolver->remoteImageSourceMap.clearAll(); } RNCFastImageViewTurboModule::RNCFastImageViewTurboModule(const ArkTSTurboModule::Context ctx, const std::string name) @@ -109,4 +128,17 @@ RNCFastImageViewTurboModule::RNCFastImageViewTurboModule(const ArkTSTurboModule: methodMap_["preload"] = MethodMetadata{0, hostFunction_RNCFastImageViewTurboModule_preload}; methodMap_["clearMemoryCache"] = MethodMetadata{0, hostFunction_RNCFastImageViewTurboModule_clearMemoryCache}; methodMap_["clearDiskCache"] = MethodMetadata{0, hostFunction_RNCFastImageViewTurboModule_clearDiskCache}; + + auto cache = this->callSync("getCacheDir", {}); + fileCacheDir = cache.asString()+"/"; + m_FastImageSourceResolver = std::make_shared(fileCacheDir); + + fs::path directoryPath = fileCacheDir; + if (fs::exists(directoryPath) && fs::is_directory(directoryPath)) { + for (const auto& entry : fs::directory_iterator(directoryPath)) { + if (fs::is_regular_file(entry.path())) { + m_FastImageSourceResolver->remoteImageSourceMap.put(entry.path().filename(),true); + } + } + } } \ No newline at end of file diff --git a/harmony/fast_image/src/main/cpp/RNCFastImageViewTurboModule.h b/harmony/fast_image/src/main/cpp/RNCFastImageViewTurboModule.h index 835bc08f6..31791f335 100644 --- a/harmony/fast_image/src/main/cpp/RNCFastImageViewTurboModule.h +++ b/harmony/fast_image/src/main/cpp/RNCFastImageViewTurboModule.h @@ -28,10 +28,11 @@ * * @generated by codegen project: GeneratePropsH.js */ - -#pragma once +#ifndef RNCFASTIMAGEVIEWTURBOMODULE_H +#define RNCFASTIMAGEVIEWTURBOMODULE_H #include +#include "FastImageSourceResolver.h" #include "RNOH/ArkTSTurboModule.h" #include "RNOH/RNInstance.h" @@ -42,8 +43,18 @@ class JSI_EXPORT RNCFastImageViewTurboModule : public ArkTSTurboModule { RNCFastImageViewTurboModule(const ArkTSTurboModule::Context ctx, const std::string name); void preload(facebook::jsi::Runtime &rt,const facebook::jsi::Value *args, size_t count); - void clearDiskCache(facebook::jsi::Runtime &rt); + void preload(std::string uri,std::string fileUri,const std::map& headers); + bool clearDiskCache(); + bool clearMemoryCache(); + + using Shared = std::shared_ptr; + std::shared_ptr m_FastImageSourceResolver = nullptr; + +private: + std::string fileCacheDir; }; -} // namespace rnoh \ No newline at end of file +} // namespace rnoh + +#endif //RNCFASTIMAGEVIEWTURBOMODULE_H \ No newline at end of file diff --git a/harmony/fast_image/src/main/cpp/downloadUtils/DownloadUri.cpp b/harmony/fast_image/src/main/cpp/downloadUtils/DownloadUri.cpp new file mode 100644 index 000000000..35eb4cf3e --- /dev/null +++ b/harmony/fast_image/src/main/cpp/downloadUtils/DownloadUri.cpp @@ -0,0 +1,39 @@ +// +// Created on 2024/9/18. +// +// Node APIs are not fully supported. To solve the compilation error of the interface cannot be found, +// please include "napi/native_api.h". + +#include "DownloadUri.h" +#include "Props.h" +#include "downloadUtils/HttpTaskProcessor.h" +#include +#include "FastImageSource.h" + +bool isValidBase64(const std::string &str) { + const std::regex base64_regex(R"(^([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==)?$)"); + return std::regex_match(str, base64_regex); +} + +bool isUriRes(const rnoh::Uri &uri) { + return (uri.scheme() == "http" || uri.scheme() == "https" || uri.scheme() == "ftp") && uri.host() != "localhost"; +} + +std::regex pattern("[^a-zA-Z0-9]"); +std::string DownloadUri::getUri(std::string uri) { + if (uri.empty()) { + return uri; + } + std::string handleResult = uri; + rnoh::Uri c_uri(handleResult); + if (isUriRes(c_uri)) { + handleResult = std::regex_replace(handleResult, pattern, ""); + return handleResult; + } + //base64's scheme is [data], need add by self + if (isValidBase64(handleResult) && c_uri.scheme() == "") { + handleResult = "data:image/jpeg;base64," + handleResult; + return handleResult; + } + return handleResult; +} \ No newline at end of file diff --git a/harmony/fast_image/src/main/cpp/downloadUtils/DownloadUri.h b/harmony/fast_image/src/main/cpp/downloadUtils/DownloadUri.h new file mode 100644 index 000000000..5ab6c64bd --- /dev/null +++ b/harmony/fast_image/src/main/cpp/downloadUtils/DownloadUri.h @@ -0,0 +1,17 @@ +// +// Created on 2024/9/18. +// +// Node APIs are not fully supported. To solve the compilation error of the interface cannot be found, +// please include "napi/native_api.h". + +#ifndef HARMONY_DOWNLOADURI_H +#define HARMONY_DOWNLOADURI_H + +#include +class DownloadUri { +public: + + std::string getUri(std::string uri); +}; + +#endif //HARMONY_DOWNLOADURI_H diff --git a/harmony/fast_image/src/main/cpp/downloadUtils/HttpTaskProcessor.cpp b/harmony/fast_image/src/main/cpp/downloadUtils/HttpTaskProcessor.cpp new file mode 100644 index 000000000..6c77a2c11 --- /dev/null +++ b/harmony/fast_image/src/main/cpp/downloadUtils/HttpTaskProcessor.cpp @@ -0,0 +1,171 @@ +// +// Created on 2024/9/2. +// +// Node APIs are not fully supported. To solve the compilation error of the interface cannot be found, +// please include "napi/native_api.h". + +#include "HttpTaskProcessor.h" +#include +#include +#include + +std::vector HttpTaskProcessor::taskList_; +std::unordered_set HttpTaskProcessor::threadIds; + +void ResponseCallback(void *usrCtx, Rcp_Response *response, uint32_t errCode) +{ + HttpTaskProcessor* processor = static_cast(usrCtx); + + // 记录当前线程的ID + std::ostringstream oss; + oss << std::this_thread::get_id(); + processor->pushThreadID(oss.str()); + + if (response != NULL) { + if (response->statusCode == RCP_OK){ + size_t status = processor->saveImage(response->body.buffer, response->body.length); + if(processor->instance && status != 0){ + processor->instance->imageDownloadComplete(processor->imgUrl,processor->filePath_); + } else if (processor->instance) { + processor->instance->imageDownloadFail(processor->imgUrl); + } + } + + response->destroyResponse(response); + } else { + DLOG(ERROR) << "[FastImage] !!! Callback Response Is NULL, errCode: "<< errCode; + if(processor->instance){ + processor->instance->imageDownloadFail(processor->imgUrl); + } + } + + processor->markEnd(); +} + +void HttpTaskProcessor::launchHttpRequest(const char * imgUrl) +{ + this->imgUrl = imgUrl; + markStart(); + // 通过url创建http request 对象 + request_ = HMS_Rcp_CreateRequest(imgUrl); + + // 设置请求方式为GET + request_->method = RCP_METHOD_GET; + uint32_t errCode = 0; + + // 创建http 会话对象 + session_ = HMS_Rcp_CreateSession(NULL, &errCode); + + if (errCode) + { + DLOG(ERROR) << "[FastImage] launchHttpRequest create Session error: "<< errCode; + if(instance){ + instance->imageDownloadFail(imgUrl); + } + return; + } + + // 设置响应数据处理的回调函数 + responseCallback_.callback = ResponseCallback; + responseCallback_.usrCtx = this; + + // API异步发起请求 + errCode = HMS_Rcp_Fetch(session_, request_, &responseCallback_); + if (errCode) + { + DLOG(ERROR) << "[FastImage] launchHttpRequest HMS_Rcp_Fetch error: "<< errCode; + if(instance){ + instance->imageDownloadFail(imgUrl); + } + markEnd(); + } +} + + +void HttpTaskProcessor::launchHttpRequest(const char * imgUrl,const std::map& headers){ + this->imgUrl = imgUrl; + markStart(); + // 通过url创建http request 对象 + request_ = HMS_Rcp_CreateRequest(imgUrl); + Rcp_Headers* header = HMS_Rcp_CreateHeaders(); + //初始化headers数据 + for (auto it = headers.begin(); it != headers.end(); ++it) { + uint32_t error = HMS_Rcp_SetHeaderValue(header, it->first.c_str(), it->second.c_str()); + if(error){ + if(instance){ + instance->imageDownloadFail(imgUrl); + } + DLOG(ERROR) << "[FastImage] launchHttpRequest create Session error: "<< error; + return; + } + } + + // 设置请求方式为GET + request_->method = RCP_METHOD_GET; + request_->headers = header; + uint32_t errCode = 0; + + // 创建http 会话对象 + session_ = HMS_Rcp_CreateSession(NULL, &errCode); + if (errCode){ + DLOG(ERROR) << "[FastImage] launchHttpRequest create Session error: "<< errCode; + return; + } + + // 设置响应数据处理的回调函数 + responseCallback_.callback = ResponseCallback; + responseCallback_.usrCtx = this; + + // API异步发起请求 + errCode = HMS_Rcp_Fetch(session_, request_, &responseCallback_); + if (errCode) + { + DLOG(ERROR) << "[FastImage] launchHttpRequest HMS_Rcp_Fetch error: "<< errCode; + markEnd(); + } +} + +// static +void HttpTaskProcessor::waitAllTask() +{ + bool wait = true; + while(wait) { + wait = false; + for (auto* processor : taskList_) { + if (processor->keep()) + wait = true; + } + if (wait) { + usleep(1000*1000); +// echoThreadIds(); + } + } +} + +// static +void HttpTaskProcessor::destroyAllTask() +{ + for (auto* processor : taskList_) { + processor->echoSpendTime(); + delete processor; + } + + taskList_.clear(); + threadIds.clear(); +} + +// static +void HttpTaskProcessor::echoThreadIds() +{ + for (auto id : threadIds) { + DLOG(INFO) << "[FastImage] NetDownload Task Thread ID: "<< id.c_str(); + } + + DLOG(INFO) << "[FastImage] NetDownload Number Of Thread : "<< threadIds.size(); +} + + +void HttpTaskProcessor::pushThreadID(std::string id) +{ + threadIds.insert(id); +} \ No newline at end of file diff --git a/harmony/fast_image/src/main/cpp/downloadUtils/HttpTaskProcessor.h b/harmony/fast_image/src/main/cpp/downloadUtils/HttpTaskProcessor.h new file mode 100644 index 000000000..663e917eb --- /dev/null +++ b/harmony/fast_image/src/main/cpp/downloadUtils/HttpTaskProcessor.h @@ -0,0 +1,135 @@ +// +// Created on 2024/8/31. +// +// Node APIs are not fully supported. To solve the compilation error of the interface cannot be found, +// please include "napi/native_api.h". + +#ifndef NETDOWNLOADDEMO_RESPONSEPROCCESSOR_H +#define NETDOWNLOADDEMO_RESPONSEPROCCESSOR_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include "RemoteCommunicationKit/rcp.h" +#include "RNCFastImageViewTurboModule.h" + +class HttpTaskProcessor +{ +public: + + HttpTaskProcessor() + { + taskList_.push_back(this); + } + + ~HttpTaskProcessor() + { + if (request_ != nullptr) { + // 释放请求资源 + HMS_Rcp_DestroyRequest(request_); + request_ = nullptr; + } + } + + + static void clearTaskList() + { + taskList_.clear(); + } + + void createParentPath(const fs::path& filePath) { + fs::path parentPath = filePath.parent_path(); + if (!parentPath.empty() && !fs::exists(parentPath)) { + fs::create_directories(parentPath); + } + } + + size_t saveImage(const char * buffer, uint32_t length) + { + size_t status = 0; + if (filePath_ == "") + return status; + createParentPath(filePath_); + fp_ = fopen(filePath_.c_str(),"w"); + if (fp_) + { + status = fwrite(buffer,sizeof(char), length, fp_); + fclose(fp_); + } + else + DLOG(ERROR) << "[FastImage] write to file Failed!!" << filePath_; + + return status; + } + + void markStart() + { + finished_ = false; + auto now = std::chrono::high_resolution_clock::now(); + startTime_ = std::chrono::time_point_cast(now).time_since_epoch(); + } + + void markEnd() + { + // 记录时间戳,标记任务结束使得keep() 返回false + auto now = std::chrono::high_resolution_clock::now(); + endTime_ = std::chrono::time_point_cast(now).time_since_epoch(); + finished_ = true; + } + + bool keep() + { + return !finished_; + } + + void echoSpendTime() + { + DLOG(INFO) << "[FastImage] HttpTaskProcessor Spend Time(ns): " << endTime_.count() - startTime_.count(); + } + + void launchHttpRequest(const char * imgUrl); + void launchHttpRequest(const char * imgUrl,const std::map& headers); + + void pushThreadID(std::string id); + + void* getImgDescOfPixelMap() + { + return imgDesc; + } + + static void waitAllTask(); + static void destroyAllTask(); + static void echoThreadIds(); + + std::shared_ptr instance; + + std::string imgUrl; + std::string filePath_; +private: + static std::vector taskList_; + static std::unordered_set threadIds; + + void* imgDesc; // 解码后 pixelMap的描述对象地址 + void* handleFFRT; // 异步任务handle 用于释放资源 + void* queueHandleFFRT; // 异步任务队列handle 用于释放资源 + + FILE* fp_ = nullptr; + + std::chrono::nanoseconds startTime_; + std::chrono::nanoseconds endTime_; + + bool finished_ = false; + + Rcp_Request *request_ = nullptr; + Rcp_Session *session_ = nullptr; + + Rcp_ResponseCallbackObject responseCallback_; +}; + + +#endif //NETDOWNLOADDEMO_RESPONSEPROCCESSOR_H diff --git a/harmony/fast_image/src/main/cpp/downloadUtils/LRUCache.h b/harmony/fast_image/src/main/cpp/downloadUtils/LRUCache.h new file mode 100644 index 000000000..ad69bc923 --- /dev/null +++ b/harmony/fast_image/src/main/cpp/downloadUtils/LRUCache.h @@ -0,0 +1,94 @@ +#ifndef LRUCACHE_H +#define LRUCACHE_H + +#include +#include +#include + +template +class LRUCache { + private: + std::unordered_map::iterator>> cache; + std::list lruList; + std::mutex mtx; // 用于同步的互斥锁 + std::function deleteCallback; + int cacheCapacity; // 成员变量的容量 + + public: + // 构造函数,允许设置自定义容量和删除回调函数 + LRUCache(int customCapacity = defaultCapacity, std::function callback = nullptr) + : cacheCapacity(customCapacity), deleteCallback(callback) {} + + ValueType get(const KeyType& key) { + std::lock_guard lock(mtx); // 线程安全 + if (cache.find(key) != cache.end()) { + lruList.erase(cache[key].second); + lruList.push_front(key); + cache[key].second = lruList.begin(); + return cache[key].first; + } + return ValueType(); // 如果没找到,返回默认构造的值 + } + + void put(const KeyType& key, const ValueType& value) { + std::lock_guard lock(mtx); // 线程安全 + if (cache.find(key) != cache.end()) { + cache[key].first = value; + lruList.erase(cache[key].second); + lruList.push_front(key); + cache[key].second = lruList.begin(); + } else { + if (cache.size() >= cacheCapacity) { + KeyType lruKey = lruList.back(); + + // 调用删除回调函数 + if (deleteCallback) { + deleteCallback(lruKey, cache[lruKey].first); + } + + cache.erase(lruKey); + lruList.pop_back(); + } + lruList.push_front(key); + cache[key] = std::make_pair(value, lruList.begin()); + } + } + + bool remove(const KeyType& key) { + std::lock_guard lock(mtx); // 线程安全 + bool status = false; + if (cache.find(key) != cache.end()) { + ValueType value = cache[key].first; + lruList.erase(cache[key].second); + if (deleteCallback) { + status = deleteCallback(key, value); + } + + cache.erase(key); + } + return status; + } +bool clearAll() { + std::lock_guard lock(mtx); // 线程安全 + // 从后向前遍历 lruList + for (auto it = lruList.end(); it != lruList.begin();) { + --it; // 先向前移动迭代器 + KeyType key = *it; // 获取当前键 + if (deleteCallback) { + // 调用删除回调并检查返回值 + if (!deleteCallback(key, cache[key].first)) { + return false; // 如果删除失败,返回 false + } + } + cache.erase(key); // 从 cache 中删除该键 + it = lruList.erase(it); // 删除当前节点并更新迭代器 + } + return true; // 成功删除所有元素,返回 true +} +size_t size(){ + return cache.size(); +} + +}; + +#endif //LRUCACHE_H \ No newline at end of file diff --git a/harmony/fast_image/src/main/ets/FastImageLoaderTurboModule.ts b/harmony/fast_image/src/main/ets/FastImageLoaderTurboModule.ts deleted file mode 100644 index b2aebbb5b..000000000 --- a/harmony/fast_image/src/main/ets/FastImageLoaderTurboModule.ts +++ /dev/null @@ -1,132 +0,0 @@ - -import { - RemoteImageDiskCache, - RemoteImageLoader, - RemoteImageLoaderError, - RemoteImageMemoryCache -} from './FastRemoteImageLoader'; -import image from '@ohos.multimedia.image'; -import { RemoteImageSource } from './FastRemoteImageLoader/RemoteImageSource'; -import { TurboModule, TurboModuleContext } from '@rnoh/react-native-openharmony/ts'; - -export class FastImageLoaderTurboModule extends TurboModule { - static NAME = "FastImageLoader" as const - - private imageLoader: RemoteImageLoader - - constructor(protected ctx: TurboModuleContext) { - super(ctx) - this.imageLoader = new RemoteImageLoader( - new RemoteImageMemoryCache(128), new RemoteImageDiskCache(128, ctx.uiAbilityContext.cacheDir), ctx.uiAbilityContext, - ({ remoteUri, fileUri }) => { - ctx.rnInstance.postMessageToCpp('UPDATE_FAST_IMAGE_SOURCE_MAP', { remoteUri, fileUri }); - }, - ({ remoteUri }) => { - ctx.rnInstance.postMessageToCpp('FAST_IMAGE_DOWNLOAD_FILE_FAIL', { remoteUri }); - }, - ) - } - - public getConstants() { - return {} - } - - public async getSize(uri: string): Promise { - if (uri && uri.length > 0) { - const imageSource = await this.imageLoader.getImageSource(uri) - const imageInfo = await imageSource.getImageSource().getImageInfo() - return [imageInfo.size.width, imageInfo.size.height] - } else { - return [0, 0] - } - } - - public async getSizeWithHeaders(uri: string, headers: Object): Promise<{ - width: number, - height: number - } & Record> { - let srcHeaders = headers as Record - let destHeaders: Record = {} - - if (srcHeaders['crossOrigin'] === 'use-credentials') { - destHeaders['Access-Control-Allow-Credentials'] = 'true' - } - - if (srcHeaders['referrerPolicy'] !== undefined) { - destHeaders['Referrer-Policy'] = srcHeaders['referrerPolicy'] - } - - const imageSource = await this.imageLoader.getImageSource(uri, destHeaders) - const imageInfo = await imageSource.getImageSource().getImageInfo() - return Promise.resolve({ width: imageInfo.size.width, height: imageInfo.size.height }) - } - - public async prefetchImage(uri: string, headers?: object): Promise { - return this.imageLoader.prefetch(uri, headers); - } - - public prefetchImageWithMetadata(uri: string, queryRootName: string, rootTag: number): Promise { - this.ctx.logger.warn("ImageLoader::prefetchImageWithMetadata is not supported") - return Promise.resolve(false) - } - - public getPrefetchResult(uri: string): string | undefined { - return this.imageLoader.getPrefetchResult(uri); - } - - public queryCache(uris: Array): Promise { - const cachedUriEntries = uris.map(uri => - [uri, this.imageLoader.queryCache(uri)] - ).filter(([_uri, value]) => value !== undefined); - const cachedUriMap = Object.fromEntries(cachedUriEntries) - return Promise.resolve(cachedUriMap) - } - - public async getRemoteImageSource(uri: string): Promise { - try { - const imageSource = await this.imageLoader.getImageSource(uri); - return imageSource; - } - catch (e) { - if (!(e instanceof RemoteImageLoaderError) && e instanceof Object && e.message) { - throw new RemoteImageLoaderError(`Failed to load the image: ${e.message}`); - } - if (typeof e === 'string') { - throw new RemoteImageLoaderError(e); - } - throw e; - } - } - - public async getImageSource(uri: string): Promise { - return (await this.getRemoteImageSource(uri)).getImageSource() - } - - public async getPixelMap(uri: string): Promise { - try { - return await this.imageLoader.getPixelMap(uri); - } - catch (e) { - if (!(e instanceof RemoteImageLoaderError) && e instanceof Object && e.message) { - throw new RemoteImageLoaderError(`Failed to load the image: ${e.message}`); - } - if (typeof e === 'string') { - throw new RemoteImageLoaderError(e); - } - throw e; - } - } - - public getCacheFilePath(uri: string): string { - return this.imageLoader.getCacheFilePath(uri); - } - - public diskCacheClear(): Promise { - return this.imageLoader.diskCacheClear() - } - - public memoryCacheClear(): Promise { - return this.imageLoader.memoryCacheClear() - } - -} \ No newline at end of file diff --git a/harmony/fast_image/src/main/ets/FastImagePackage.ts b/harmony/fast_image/src/main/ets/FastImagePackage.ts index 84948ffb4..b72f0d6b0 100644 --- a/harmony/fast_image/src/main/ets/FastImagePackage.ts +++ b/harmony/fast_image/src/main/ets/FastImagePackage.ts @@ -34,15 +34,12 @@ import { RNCFastImageViewTurboModule } from './RNCFastImageViewTurboModule'; import app from '@system.app' import {RNCFastImageView} from './TMSpecs' import {FastImageView} from './RNCSpecs' -import {FastImageLoaderTurboModule} from './FastImageLoaderTurboModule' class FastImageTurboModulesFactory extends TurboModulesFactory { createTurboModule(name: string): TurboModule | null { if (name === RNCFastImageView.NAME) { return new RNCFastImageViewTurboModule(this.ctx); - } else if(name === FastImageLoaderTurboModule.NAME){ - return new FastImageLoaderTurboModule(this.ctx); } return null; } @@ -50,8 +47,6 @@ class FastImageTurboModulesFactory extends TurboModulesFactory { hasTurboModule(name: string): boolean { if(name === RNCFastImageView.NAME){ return true; - }else if(name === FastImageLoaderTurboModule.NAME){ - return true; } return false; } diff --git a/harmony/fast_image/src/main/ets/FastRemoteImageLoader/RemoteImageCache.ts b/harmony/fast_image/src/main/ets/FastRemoteImageLoader/RemoteImageCache.ts deleted file mode 100644 index d3b17ed74..000000000 --- a/harmony/fast_image/src/main/ets/FastRemoteImageLoader/RemoteImageCache.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { RemoteImageSource } from './RemoteImageSource'; - -export class RemoteImageCache { - protected data: Map; - protected maxSize: number; - - constructor(maxSize: number) { - this.data = new Map(); - this.maxSize = maxSize; - } - - set(key: string, value: T): void { - this.data.set(key, value); - - // Check if memoryCache size exceeds maxSize, and if so, remove the oldest entry - if (this.data.size > this.maxSize) { - const oldestKey = this.data.keys()[0]; - this.remove(oldestKey); - } - } - - get(key: string): T | undefined { - return this.data.get(key); - } - - has(key: string): boolean { - return this.data.has(key); - } - - remove(key: string): void { - this.data.delete(key); - } - - clear(): void { - this.data.clear(); - } - - size(): number { - return this.data.size; - } - - keys(): IterableIterator { - return this.data.keys(); - } - - values(): IterableIterator { - return this.data.values(); - } - entries(): IterableIterator<[string, T]> { - return this.data.entries(); - } -} - -export class RemoteImageMemoryCache extends RemoteImageCache { - public memoryCacheClear():boolean{ - let PromiseArr = [] - for (const entry of this.data.entries()){ - PromiseArr.push(entry[1].release().then(()=>{this.data.delete(entry[0])})) - } - Promise.all(PromiseArr).then( - ).catch((err)=>{ - return false; - }) - return true; - } -}; diff --git a/harmony/fast_image/src/main/ets/FastRemoteImageLoader/RemoteImageDiskCache.ts b/harmony/fast_image/src/main/ets/FastRemoteImageLoader/RemoteImageDiskCache.ts deleted file mode 100644 index 56681c9a2..000000000 --- a/harmony/fast_image/src/main/ets/FastRemoteImageLoader/RemoteImageDiskCache.ts +++ /dev/null @@ -1,94 +0,0 @@ -import {RemoteImageCache} from './RemoteImageCache'; -import fs from '@ohos.file.fs'; - -const EMPTY_CACHE_KEY = ''; - -export class RemoteImageDiskCache extends RemoteImageCache { - private cacheDir: string; - private reg = /[^a-zA-Z0-9 -]/g; - - constructor(maxSize: number, cacheDir: string) { - super(maxSize); - this.cacheDir = cacheDir; - const filenames: string[] = fs.listFileSync(cacheDir); - - // number of files in cache might be over maxSize but `listFile` cannot specify sort order by modification time - filenames - .map(filename => filename.replace('/', '')) - .forEach(filename => { - this.set(filename, true); - }); - } - - remove(key: string): void { - const cachedKey = this.getCacheKey(key); - if (this.data.has(cachedKey)) { - try { - fs.unlinkSync(cachedKey); - } catch (reason) { - throw new Error('Cache file was not deleted ' + reason); - } - this.data.delete(cachedKey); - } - } - - clear(): void { - this.data.forEach((_value, key) => { - const cachedKey = this.getCacheKey(key); - fs.unlink(cachedKey).catch(reason => { - throw new Error('Cache file was not deleted ' + reason); - }); - }); - this.data.clear(); - } - - set(key: string, value: boolean = true): void { - const cachedKey = this.getCacheKey(key); - if (cachedKey === EMPTY_CACHE_KEY) { - console.warn('Cache key not provided, not using cache'); - return; - } - return super.set(cachedKey, value); - } - - has(key: string): boolean { - const cachedKey = this.getCacheKey(key); - if (super.has(cachedKey)) { - try { - fs.statSync(this.getFilePath(cachedKey)); - return true; - } catch { - console.error('no cache'); - } - } - return false; - } - - // this method does not check if the file exists and is also used to determine file path for prefetch - public getLocation(key: string): string { - const cachedKey = this.getCacheKey(key); - return this.getFilePath(cachedKey); - } - - private getFilePath(key: string): string { - return this.cacheDir + '/' + key; - } - - private getCacheKey(uri: string): string { - if (uri === undefined) { - // if multiple images would have the same uri, they would overwrite each other - // but it's better than crashing the app - console.warn('Cache key not provided, defaulting to empty cache key'); - return EMPTY_CACHE_KEY; - } - - return uri.replace(this.reg, ''); - } - - public diskCacheClear(){ - for (const key of this.data.keys()){ - fs.unlink(this.cacheDir+'/'+key); - this.data.delete(key); - } - } -} diff --git a/harmony/fast_image/src/main/ets/FastRemoteImageLoader/RemoteImageLoader.ts b/harmony/fast_image/src/main/ets/FastRemoteImageLoader/RemoteImageLoader.ts deleted file mode 100644 index 94b61a405..000000000 --- a/harmony/fast_image/src/main/ets/FastRemoteImageLoader/RemoteImageLoader.ts +++ /dev/null @@ -1,245 +0,0 @@ -import image from '@ohos.multimedia.image'; -import type { RemoteImageMemoryCache } from './RemoteImageCache'; -import { RemoteImageLoaderError } from './RemoteImageLoaderError'; -import request from '@ohos.request'; -import type { RemoteImageDiskCache } from './RemoteImageDiskCache'; -import fs from '@ohos.file.fs'; -import { fetchDataFromUrl, FetchOptions, FetchResult } from '@rnoh/react-native-openharmony/src/main/ets/RNOH/HttpRequestHelper'; -import { RemoteImageSource } from './RemoteImageSource'; -import { common } from '@kit.AbilityKit'; - -export class RemoteImageLoader { - private activeRequestByUrl: Map> = new Map(); - private activePrefetchByUrl: Map> = new Map(); - - public constructor( - private memoryCache: RemoteImageMemoryCache, - private diskCache: RemoteImageDiskCache, - private context: common.UIAbilityContext, - private onDiskCacheUpdate: (e: { remoteUri: string, fileUri: string }) => void, - private onDownloadFileFail: (e: { remoteUri: string }) => void, - ) { - } - - private async fetchImage(url: string, headers?: Record): Promise { - if (this.activeRequestByUrl.has(url)) { - return this.activeRequestByUrl.get(url); - } - - let options: FetchOptions = { - usingCache: true, - headers: headers, - }; - const promise = fetchDataFromUrl(url, options); - this.activeRequestByUrl.set(url, promise); - promise.finally(() => { - this.activeRequestByUrl.delete(url); - }); - return promise; - } - - public async getPixelMap(uri: string): Promise { - const imageSource = await this.getImageSource(uri); - return await imageSource.getPixelMapPromise(); - } - - public async getImageSource(uri: string, headers?: Record): Promise { - if (uri.startsWith("data:")) { - const imageSource = image.createImageSource(uri); - return new RemoteImageSource(imageSource, ''); - } - if (this.memoryCache.has(uri)) { - return this.memoryCache.get(uri); - } - - // if the image is being prefetched, wait until it's downloaded, - // ignoring any errors that occur (we'll try to download it to memory instead) - if (this.activePrefetchByUrl.has(uri)) { - await this.activePrefetchByUrl.get(uri).catch(() => { - }); - } - - if (this.diskCache.has(uri)) { - const location = `file://${this.diskCache.getLocation(uri)}`; - const imageSource = image.createImageSource(location); - if (imageSource === null) { - throw new RemoteImageLoaderError("Couldn't create ImageSource"); - } - const remoteImageSource = new RemoteImageSource(imageSource, location); - this.memoryCache.set(uri, remoteImageSource); - return remoteImageSource; - } - - let response: FetchResult; - try { - response = await this.fetchImage(uri, headers); - } catch (e) { - throw new RemoteImageLoaderError( - e.message ?? 'Failed to fetch the image', - ); - } - - // if more than one component tried fetching at the same time, - // it's possible the source is now in the cache. - // Check it first before creating a new source - if (this.memoryCache.has(uri)) { - return this.memoryCache.get(uri); - } - - const imageSource = image.createImageSource(response.result); - if (imageSource === null) { - throw new RemoteImageLoaderError("Couldn't create ImageSource"); - } - // NOTE: workaround for `Image` component not supporting redirects or ImageSource sources. - // If the image is an animated GIF, we cannot convert it to a PixelMap - // since that would display a single frame only. - // Instead, when redirected, we attach the `location` url to the imageSource object - // and use it as the source parameter for `Image` - // eslint-disable-next-line dot-notation - const location = response.headers['location'] ?? uri; - const remoteImageSource = new RemoteImageSource(imageSource, location); - this.memoryCache.set(uri, remoteImageSource); - - if (!this.activePrefetchByUrl.has(uri) && !this.diskCache.has(uri)) { - // some other component might have prefetched it - const promise = this.saveFile(uri, response.result); - this.activePrefetchByUrl.set(uri, promise); - promise.finally(() => { - this.activePrefetchByUrl.delete(uri); - }); - } - return remoteImageSource; - } - - private async saveFile(uri: string, arrayBuffer: ArrayBuffer) { - try { - const path = this.diskCache.getLocation(uri); - const file = await fs.open( - path, - // eslint-disable-next-line no-bitwise - fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE, - ); - await fs.write(file.fd, arrayBuffer); - fs.close(file); - this.diskCache.set(uri); - } catch (error) { - console.error( - 'Error occurred when storing file for disk cache ' + error.message, - ); - } - return true; - } - - public async prefetch(uri: string, headers?: object): Promise { - if (this.diskCache.has(uri)) { - return true; - } - - if (this.activePrefetchByUrl.has(uri)) { - return await this.activePrefetchByUrl.get(uri); - } - - // evict the image from cache if present - if (this.memoryCache.has(uri)) { - this.memoryCache.remove(uri); - } - - const promise = this.downloadFile(uri, headers); - this.activePrefetchByUrl.set(uri, promise); - - promise.catch((e) => { - this.onDownloadFileFail({ remoteUri: uri }); - }) - - promise.finally(() => { - this.activePrefetchByUrl.delete(uri); - const fileUri = `file://${this.diskCache.getLocation(uri)}`; - this.onDiskCacheUpdate({ remoteUri: uri, fileUri }) - }); - - return await promise; - } - - private async performDownload(config: request.DownloadConfig): Promise { - return await new Promise(async (resolve, reject) => { - try { - if(config.header === undefined) - delete config.header - const downloadTask = await request.downloadFile(this.context, config); - downloadTask.on("complete", () => { - resolve(true); - }); - downloadTask.on("fail", (err: number) => { - reject(`Failed to download the task. Code: ${err}`) - }); - } catch (e) { - reject(e); - } - }); - } - - private async downloadFile(uri: string, headers?: object): Promise { - const path = this.diskCache.getLocation(uri); - const tempPath = path + '_tmp'; - - try { - // Download to a temporary location to avoid risks of corrupted files from incomplete downloads, - // as request.downloadFile does not clean up failed downloads automatically. - if (fs.accessSync(tempPath)) { - await fs.unlink(tempPath); - } - await this.performDownload({ url: uri, filePath: tempPath, header: headers }); - // Move the file to the final location and remove the temporary file - await fs.moveFile(tempPath, path); - this.diskCache.set(uri); - } catch (e) { - return Promise.reject(e); - } - return true; - } - - public queryCache(uri: string): 'memory' | 'disk' | undefined { - if (this.diskCache.has(uri)) { - return 'disk'; - } - if (this.memoryCache.has(uri)) { - return 'memory'; - } - return undefined; - } - - public getCacheFilePath(uri: string): string { - if (this.diskCache.has(uri)) { - return `file://${this.diskCache.getLocation(uri)}`; - } - return uri; - } - - public getPrefetchResult(uri: string) { - if (this.activePrefetchByUrl.has(uri)) { - return 'pending'; - } - if (this.diskCache.has(uri)) { - return `file://${this.diskCache.getLocation(uri)}`; - } - return undefined; - } - - public diskCacheClear(): Promise { - try { - this.diskCache.diskCacheClear(); - } catch (e) { - return Promise.reject('[FastImage] diskCacheClear fail') - } - return Promise.resolve(); - } - - public memoryCacheClear(): Promise { - if (this.memoryCache.memoryCacheClear()) { - return Promise.resolve(); - } else { - return Promise.reject('[FastImage] memoryCacheClear fail'); - } - } - -} diff --git a/harmony/fast_image/src/main/ets/FastRemoteImageLoader/RemoteImageLoaderError.ts b/harmony/fast_image/src/main/ets/FastRemoteImageLoader/RemoteImageLoaderError.ts deleted file mode 100644 index 87d599d7b..000000000 --- a/harmony/fast_image/src/main/ets/FastRemoteImageLoader/RemoteImageLoaderError.ts +++ /dev/null @@ -1,2 +0,0 @@ -export class RemoteImageLoaderError extends Error { -} \ No newline at end of file diff --git a/harmony/fast_image/src/main/ets/FastRemoteImageLoader/RemoteImageSource.ts b/harmony/fast_image/src/main/ets/FastRemoteImageLoader/RemoteImageSource.ts deleted file mode 100644 index 3094fa29a..000000000 --- a/harmony/fast_image/src/main/ets/FastRemoteImageLoader/RemoteImageSource.ts +++ /dev/null @@ -1,42 +0,0 @@ -import image from '@ohos.multimedia.image' - -export class RemoteImageSource { - private pixelMapPromise: Promise | undefined - - constructor(private imageSource: image.ImageSource, private location: string) { - } - - getImageSource(): image.ImageSource { - return this.imageSource; - } - - getLocation(): string { - return this.location; - } - - async getPixelMapPromise(): Promise { - if (this.pixelMapPromise === undefined) { - return this.createPixelMap(); - } - - return this.pixelMapPromise; - } - - private createPixelMap(): Promise { - const pixelMapPromise = (async () => { - if (await this.imageSource.getFrameCount() === 1) { - return this.imageSource.createPixelMap() - } - throw Error("Cannot create a PixelMap for an animated image"); - })() - this.pixelMapPromise = pixelMapPromise; - - pixelMapPromise.catch(() => this.pixelMapPromise = undefined); - - return pixelMapPromise; - } - - release():Promise{ - return this.imageSource.release(); - } -} \ No newline at end of file diff --git a/harmony/fast_image/src/main/ets/FastRemoteImageLoader/index.ts b/harmony/fast_image/src/main/ets/FastRemoteImageLoader/index.ts deleted file mode 100644 index bf01b3f19..000000000 --- a/harmony/fast_image/src/main/ets/FastRemoteImageLoader/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export * from "./RemoteImageLoaderError" -export * from "./RemoteImageCache" -export * from "./RemoteImageLoader" -export * from "./RemoteImageDiskCache" \ No newline at end of file diff --git a/harmony/fast_image/src/main/ets/RNCFastImageViewTurboModule.ts b/harmony/fast_image/src/main/ets/RNCFastImageViewTurboModule.ts index 1cc225743..4a3f7e2e1 100644 --- a/harmony/fast_image/src/main/ets/RNCFastImageViewTurboModule.ts +++ b/harmony/fast_image/src/main/ets/RNCFastImageViewTurboModule.ts @@ -23,28 +23,11 @@ */ import { TurboModule } from '@rnoh/react-native-openharmony/ts'; -import { FastImageLoaderTurboModule } from './FastImageLoaderTurboModule'; import Logger from './Logger' import { RNCFastImageView } from "./TMSpecs" export class RNCFastImageViewTurboModule extends TurboModule implements RNCFastImageView.Spec { - - clearMemoryCache(): Promise { - let imageLoaderTurboModule:FastImageLoaderTurboModule = this.ctx.rnInstance.getTurboModule("FastImageLoader"); - return imageLoaderTurboModule.memoryCacheClear(); - } - - clearDiskCache(): Promise { - return new Promise((resolve, reject) => { - let imageLoaderTurboModule:FastImageLoaderTurboModule = this.ctx.rnInstance.getTurboModule("FastImageLoader"); - try{ - imageLoaderTurboModule.diskCacheClear(); - } catch (e) { - Logger.error("clearDiskCache BusinessError code:"+e.code) - reject("clearDiskCache BusinessError code:"+e.code) - } - - resolve(null) - }); + getCacheDir():string{ + return this.ctx.uiAbilityContext.cacheDir; } } diff --git a/harmony/fast_image/src/main/ets/TMSpecs.ts b/harmony/fast_image/src/main/ets/TMSpecs.ts index c8076b8ad..2d1875026 100644 --- a/harmony/fast_image/src/main/ets/TMSpecs.ts +++ b/harmony/fast_image/src/main/ets/TMSpecs.ts @@ -6,9 +6,6 @@ export namespace RNCFastImageView { export type Source = {uri?: string, headers?: Object, priority?: string, cache?: string} export interface Spec { - clearMemoryCache(): Promise; - - clearDiskCache(): Promise; } }