前些日子,组里同事在做Flutter性能优化,但是遇到了一个问题,我们自己写的flutter apk,Flutter 1.UI线程是被 SurfaceFlinger的app唤醒的。但是三方应用的是被主线程唤醒的。
我们看下trace:
第一种情况:flutter的1.ui被 SurfaceFlinger的app唤醒。
第二种情况:flutter的1.ui被 ui主线程唤醒
我们看下代码:
// |VsyncWaiter|
void VsyncWaiterAndroid::AwaitVSync() {
if (use_ndk_choreographer_) {
auto* weak_this = new std::weak_ptr<VsyncWaiter>(shared_from_this());
fml::TaskRunner::RunNowOrPostTask(
task_runners_.GetUITaskRunner(), [weak_this]() {
AndroidChoreographer::PostFrameCallback(&OnVsyncFromNDK, weak_this);
});
} else {
// TODO(99798): Remove it when we drop support for API level < 29.
auto* weak_this = new std::weak_ptr<VsyncWaiter>(shared_from_this());
jlong java_baton = reinterpret_cast<jlong>(weak_this);
task_runners_.GetPlatformTaskRunner()->PostTask([java_baton]() {
JNIEnv* env = fml::jni::AttachCurrentThread();
env->CallStaticVoidMethod(g_vsync_waiter_class->obj(), //
g_async_wait_for_vsync_method_, //
java_baton //
);
});
}
}
如果use_ndk_choreographer_, 就使用上面的AndroidChoreographer::PostFrameCallback,最终调用到cs.android.com/android/pla…
void Choreographer::postFrameCallbackDelayed(AChoreographer_frameCallback cb,
AChoreographer_frameCallback64 cb64,
AChoreographer_vsyncCallback vsyncCallback, void* data,
nsecs_t delay) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
FrameCallback callback{cb, cb64, vsyncCallback, data, now + delay};
{
std::lock_guard<std::mutex> _l{mLock};
mFrameCallbacks.push(callback);
}
if (callback.dueTime <= now) {
if (std::this_thread::get_id() != mThreadId) {
if (mLooper != nullptr) {
Message m{MSG_SCHEDULE_VSYNC};
mLooper->sendMessage(this, m);
} else {
scheduleVsync();
}
} else {
scheduleVsync();
}
} else {
if (mLooper != nullptr) {
Message m{MSG_SCHEDULE_CALLBACKS};
mLooper->sendMessageDelayed(delay, this, m);
} else {
scheduleCallbacks();
}
}
}
通过这种形式 scheduleVsync, 也就是需要SF的app信号来唤醒。
通过代码分析,use_ndk_choreographer_ = true的,逻辑是判断so里是不是包含一些符号表。那么为什么有的应用不是这样呢?
我们分析下 flutter engine的代码提交记录
发现flutter2022年修改过这一块,左边是之前的代码,是通过jni调用了 java层的Choreographer.getInstance().postFrameCallback(obtainFrameCallback(cookie));来实现,这种会通过 主线程唤醒1.ui了。
private final FlutterJNI.AsyncWaitForVsyncDelegate asyncWaitForVsyncDelegate =
new FlutterJNI.AsyncWaitForVsyncDelegate() {
private Choreographer.FrameCallback obtainFrameCallback(final long cookie) {
if (frameCallback != null) {
frameCallback.cookie = cookie;
FrameCallback ret = frameCallback;
frameCallback = null;
return ret;
}
return new FrameCallback(cookie);
}
@Override
public void asyncWaitForVsync(long cookie) {
Choreographer.getInstance().postFrameCallback(obtainFrameCallback(cookie));
}
};