总结:
ArkTs 注册的可见性变化封装到 piplineContext node 中保存,会在 Vsync 每一帧中遍历调用 node 来计算可见性,根据触发阈值条件若触发则 napi 调用 arkTs 可见性变化。随着监听可见组件增多,每一帧间逻辑复杂度也随之增加,遍历耗时增加;
解决方案:
使用 setOnVisibleAreaApproximateChange 代替 (存在问题:复用场景不会回调 api17 release 系统修复)
整体流程:
1 监听可见变化
代码中常通过这样的方式来设置可见性变化:
2 处理流程
FrameNode 通过 AddVisibleAreaChangeCallback 注册监听,并把自己加入 PipelineContext_NG 的 onVisibleAreaChangeNodeIds_ 。
void PipelineContext::AddVisibleAreaChangeNode(const RefPtr<FrameNode>& node,
const std::vector<double>& ratios, const VisibleRatioCallback& callback, bool isUserCallback,
bool isCalculateInnerClip)
{
CHECK_NULL_VOID(node);
VisibleCallbackInfo addInfo;
addInfo.callback = callback;
addInfo.isCurrentVisible = false;
onVisibleAreaChangeNodeIds_.emplace(node->GetId());
if (isUserCallback) {
node->SetVisibleAreaUserCallback(ratios, addInfo);
} else {
node->SetVisibleAreaInnerCallback(ratios, addInfo, isCalculateInnerClip);
}
}
添加 onVisibleAreaChangeNodeIds_ 代表节点需要关注可见变化,具体会在 FlushWindowStateChangedCallback FlushVsync 等节点进行调用
void PipelineContext::FlushVsync(uint64_t nanoTimestamp, uint32_t frameCount)
{
ProcessDelayTasks();
DispatchDisplaySync(nanoTimestamp);
FlushAnimation(nanoTimestamp);
FlushFrameCallback(nanoTimestamp);
auto hasRunningAnimation = FlushModifierAnimation(nanoTimestamp);
FlushTouchEvents();
FlushDragEvents();
FlushFrameCallbackFromCAPI(nanoTimestamp, frameCount);
FlushBuild();
taskScheduler_->StartRecordFrameInfo(GetCurrentFrameInfo(recvTime_, nanoTimestamp));
taskScheduler_->FlushTask();
UIObserverHandler::GetInstance().HandleLayoutDoneCallBack();
// flush correct rect again
taskScheduler_->FlushPersistAfterLayoutTask();
taskScheduler_->FinishRecordFrameInfo();
FlushNodeChangeFlag();
FlushAnimationClosure();
TryCallNextFrameLayoutCallback();
HandleOnAreaChangeEvent(nanoTimestamp);
HandleVisibleAreaChangeEvent(nanoTimestamp);
}
void PipelineContext::FlushWindowStateChangedCallback(bool isShow)
{
if (!CheckThreadSafe()) {
LOGW("FlushWindowStateChangedCallback doesn't run on UI thread!");
}
std::set<int32_t> onWindowStateChangedCallbacks;
std::swap(onWindowStateChangedCallbacks, onWindowStateChangedCallbacks_);
auto iter = onWindowStateChangedCallbacks.begin();
while (iter != onWindowStateChangedCallbacks.end()) {
auto node = ElementRegister::GetInstance()->GetUINodeById(*iter);
if (!node) {
iter = onWindowStateChangedCallbacks.erase(iter);
} else {
if (isShow) {
node->OnWindowShow();
} else {
node->OnWindowHide();
}
++iter;
}
}
std::swap(onWindowStateChangedCallbacks, onWindowStateChangedCallbacks_);
HandleVisibleAreaChangeEvent(GetTimeFromExternalTimer());
HandleSubwindow(isShow);
}
PipelineContext_NG::HandleVisibleAreaChangeEvent 遍历 onVisibleAreaChangeNodeIds_ ,对每个 FrameNode 调用 TriggerVisibleAreaChangeCallback
void PipelineContext::HandleVisibleAreaChangeEvent(uint64_t nanoTimestamp)
{
ACE_FUNCTION_TRACE();
if (onVisibleAreaChangeNodeIds_.empty()) {
return;
}
auto nodes = FrameNode::GetNodesById(onVisibleAreaChangeNodeIds_);
for (auto&& frameNode : nodes) {
frameNode->TriggerVisibleAreaChangeCallback(nanoTimestamp);
}
}
FrameNode::TriggerVisibleAreaChangeCallback 计算自身与父可见区域的交集、可见状态和比例。如果变化满足条件,通过 ProcessVisibleAreaChangeEvent 回调 arkts 定义的可见回调函数
void FrameNode::TriggerVisibleAreaChangeCallback(uint64_t timestamp, bool forceDisappear)
{
auto context = GetContext();
CHECK_NULL_VOID(context);
CHECK_NULL_VOID(eventHub_);
ProcessThrottledVisibleCallback(forceDisappear);
auto hasInnerCallback = eventHub_->HasVisibleAreaCallback(false);
auto hasUserCallback = eventHub_->HasVisibleAreaCallback(true);
if (!hasInnerCallback && !hasUserCallback) {
return;
}
auto& visibleAreaUserRatios = eventHub_->GetVisibleAreaRatios(true);
auto& visibleAreaUserCallback = eventHub_->GetVisibleAreaCallback(true);
auto& visibleAreaInnerRatios = eventHub_->GetVisibleAreaRatios(false);
auto& visibleAreaInnerCallback = eventHub_->GetVisibleAreaCallback(false);
auto visibleResult = GetCacheVisibleRect(timestamp, IsDebugInspectorId());
SetVisibleAreaChangeTriggerReason(VisibleAreaChangeTriggerReason::VISIBLE_AREA_CHANGE);
if (hasInnerCallback) {
if (isCalculateInnerVisibleRectClip_) {
ProcessVisibleAreaChangeEvent(visibleResult.innerVisibleRect, visibleResult.frameRect,
visibleAreaInnerRatios, visibleAreaInnerCallback, false);
} else {
ProcessVisibleAreaChangeEvent(visibleResult.visibleRect, visibleResult.frameRect, visibleAreaInnerRatios,
visibleAreaInnerCallback, false);
}
}
if (hasUserCallback) {
ProcessVisibleAreaChangeEvent(
visibleResult.visibleRect, visibleResult.frameRect, visibleAreaUserRatios, visibleAreaUserCallback, true);
}
}
PipelineContext_NG::ProcessVisibleAreaChangeEvent 遍历 visibleAreaChangeNodes_ ,执行注册的回调
void FrameNode::ProcessAllVisibleCallback(const std::vector<double>& visibleAreaUserRatios,
VisibleCallbackInfo& visibleAreaUserCallback, double currentVisibleRatio, double lastVisibleRatio,
bool isThrottled, bool isInner)
{
bool isHandled = false;
bool isVisible = false;
double* lastVisibleCallbackRatio = isThrottled ? &lastThrottledVisibleCbRatio_ :
(isInner ? &lastInnerVisibleCallbackRatio_ : &lastVisibleCallbackRatio_);
// 遍历比较判断是否满足用户定义的阈值
for (const auto& callbackRatio : visibleAreaUserRatios) {
if (GreatNotEqual(currentVisibleRatio, callbackRatio) && LessOrEqual(lastVisibleRatio, callbackRatio)) {
*lastVisibleCallbackRatio = currentVisibleRatio;
isVisible = true;
isHandled = true;
} else if (LessNotEqual(currentVisibleRatio, callbackRatio) && GreatOrEqual(lastVisibleRatio, callbackRatio)) {
*lastVisibleCallbackRatio = currentVisibleRatio;
isVisible = false;
isHandled = true;
} else if (NearEqual(callbackRatio, VISIBLE_RATIO_MIN) && NearEqual(currentVisibleRatio, callbackRatio)) {
*lastVisibleCallbackRatio = VISIBLE_RATIO_MIN;
currentVisibleRatio = VISIBLE_RATIO_MIN;
isVisible = false;
isHandled = true;
} else if (NearEqual(callbackRatio, VISIBLE_RATIO_MAX) && NearEqual(currentVisibleRatio, callbackRatio)) {
*lastVisibleCallbackRatio = VISIBLE_RATIO_MAX;
currentVisibleRatio = VISIBLE_RATIO_MAX;
isVisible = true;
isHandled = true;
}
}
auto callback = visibleAreaUserCallback.callback;
if (isHandled && callback) {
if (GetTag() == V2::WEB_ETS_TAG) {
TAG_LOGI(AceLogTag::ACE_UIEVENT, "exp=%{public}d ratio=%{public}s %{public}d-%{public}s reason=%{public}d",
isVisible, std::to_string(currentVisibleRatio).c_str(), GetId(),
std::to_string(GetAccessibilityId()).c_str(), static_cast<int32_t>(visibleAreaChangeTriggerReason_));
}
// 回调 ArkTS
callback(isVisible, currentVisibleRatio);
}
}