1 /*
2 * Copyright (c) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15 #include "core/components_ng/manager/memory/memory_manager.h"
16
17 #include "core/components_ng/pattern/image/image_pattern.h"
18 #include "core/pipeline_ng/pipeline_context.h"
19 #include "frameworks/core/common/container.h"
20
21 namespace {
22 constexpr int32_t BACKGROUND_RECYCLE_WAIT_TIME_MS = 500;
23 constexpr int32_t RECYCLE_PAGE_IMAGE_NUM = 20;
24 }
25 namespace OHOS::Ace::NG {
MemoryManager()26 MemoryManager::MemoryManager()
27 {
28 isTrimMemWork_ = false;
29 }
30
AddRecyclePageNode(const RefPtr<FrameNode> & node)31 void MemoryManager::AddRecyclePageNode(const RefPtr<FrameNode>& node)
32 {
33 if (!isTrimMemWork_) {
34 return;
35 }
36 CHECK_NULL_VOID(node);
37 auto weak = WeakClaim(RawPtr(node));
38 if (std::find(pageNodes_.begin(), pageNodes_.end(), weak) == pageNodes_.end()) {
39 pageNodes_.emplace_back(weak);
40 }
41 }
42
RemoveRecyclePageNode(int32_t nodeId)43 void MemoryManager::RemoveRecyclePageNode(int32_t nodeId)
44 {
45 if (!isTrimMemWork_ || pageNodes_.empty()) {
46 return;
47 }
48 pageNodes_.remove_if([nodeId](const WeakPtr<FrameNode>& node) {
49 return !node.Upgrade() || nodeId == node.Upgrade()->GetId();
50 });
51 }
52
RecycleImage(const RefPtr<UINode> & node,int & recycleNum)53 void MemoryManager::RecycleImage(const RefPtr<UINode>& node, int& recycleNum)
54 {
55 CHECK_NULL_VOID(node);
56 const auto& children = node->GetChildren(true);
57 if (children.empty() || (recycleNum <= 0)) {
58 return;
59 }
60 for (const auto& child : children) {
61 auto childNode = AceType::DynamicCast<FrameNode>(child);
62 if (childNode && (childNode->GetTag() == V2::IMAGE_ETS_TAG)) {
63 auto imagePattern = childNode->GetPattern<ImagePattern>();
64 if ((!imagePattern) || (!imagePattern->RecycleImageData())) {
65 continue;
66 }
67 recycleNum--;
68 if (recycleNum <= 0) {
69 return;
70 }
71 } else {
72 RecycleImage(child, recycleNum);
73 }
74 }
75 }
76
RecycleImageByPage(const RefPtr<FrameNode> & node)77 void MemoryManager::RecycleImageByPage(const RefPtr<FrameNode>& node)
78 {
79 CHECK_NULL_VOID(node);
80 node->SetTrimMemRecycle(true);
81 int32_t recycleNum = RECYCLE_PAGE_IMAGE_NUM;
82 RecycleImage(node, recycleNum);
83 }
84
RebuildImageByPage(const RefPtr<FrameNode> & node)85 void MemoryManager::RebuildImageByPage(const RefPtr<FrameNode>& node)
86 {
87 CHECK_NULL_VOID(node);
88 if (!isTrimMemWork_ || !node->IsTrimMemRecycle()) {
89 return;
90 }
91 ACE_SCOPED_TRACE("memory manager rebuild image by page");
92 node->SetTrimMemRecycle(false);
93 RebuildImage(node);
94 }
95
RebuildImage(const RefPtr<UINode> & node)96 void MemoryManager::RebuildImage(const RefPtr<UINode>& node)
97 {
98 CHECK_NULL_VOID(node);
99 const auto& children = node->GetChildren(true);
100 if (children.empty()) {
101 return;
102 }
103 for (const auto& child : children) {
104 auto childNode = AceType::DynamicCast<FrameNode>(child);
105 if (childNode && childNode->GetTag() == V2::IMAGE_ETS_TAG) {
106 auto imagePattern = childNode->GetPattern<ImagePattern>();
107 if ((!imagePattern) || (!childNode->IsTrimMemRecycle())) {
108 continue;
109 }
110 TAG_LOGI(AceLogTag::ACE_IMAGE, "start rebuild image: %{public}d", childNode->GetId());
111 imagePattern->LoadImageDataIfNeed();
112 childNode->SetTrimMemRecycle(false);
113 } else {
114 RebuildImage(child);
115 }
116 }
117 }
118
PostMemRecycleTask()119 void MemoryManager::PostMemRecycleTask()
120 {
121 auto container = Container::Current();
122 if (!isTrimMemWork_ || (container && container->IsSceneBoardWindow())) {
123 return;
124 }
125 auto pipeline = PipelineContext::GetCurrentContext();
126 CHECK_NULL_VOID(pipeline);
127 auto taskExecutor = pipeline->GetTaskExecutor();
128 CHECK_NULL_VOID(taskExecutor);
129 taskExecutor->PostDelayedTask([weak = WeakClaim(this)] {
130 auto memMgr = weak.Upgrade();
131 CHECK_NULL_VOID(memMgr);
132 memMgr->TrimMemRecycle();
133 },
134 TaskExecutor::TaskType::UI,
135 BACKGROUND_RECYCLE_WAIT_TIME_MS,
136 "TrimMemManagerToRecycleImage");
137 }
138
TrimMemRecycle()139 void MemoryManager::TrimMemRecycle()
140 {
141 if (pageNodes_.empty()) {
142 return;
143 }
144 TAG_LOGI(AceLogTag::ACE_IMAGE, "start recycle image from page");
145 for (auto iter = pageNodes_.begin(); iter != pageNodes_.end(); ++iter) {
146 auto frameNode = iter->Upgrade();
147 if (!frameNode) {
148 iter = pageNodes_.erase(iter);
149 continue;
150 }
151 if (frameNode->IsVisible() || frameNode->IsTrimMemRecycle()) {
152 continue;
153 }
154 RecycleImageByPage(frameNode);
155 }
156 }
157
Reset()158 void MemoryManager::Reset()
159 {
160 if (!isTrimMemWork_) {
161 return;
162 }
163 pageNodes_.clear();
164 }
165 } // namespace OHOS::Ace::NG
166