相关文章链接:

1. Android Framework - 学习启动篇
2. 源码阅读分析 - Window底层原理与系统架构

相关源码文件:

/frameworks/base/core/java/android/view/ViewRootImpl.java/frameworks/base/core/java/android/view/Choreographer.java/frameworks/base/core/java/android/view/DisplayEventReceiver.java/frameworks/base/core/jni/android_view_DisplayEventReceiver.cpp/frameworks/native/libs/gui/DisplayEventReceiver.cpp/frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp/frameworks/native/services/surfaceflinger/EventThread.cpp/frameworks/native/libs/gui/BitTube.cpp

1. 梳理概述

在开始阅读文章前,希望大家能认真思考几个问题:

  • 界面卡顿的原理是怎样的?
  • ViewRootImpl 与 SurfaceFlinger 是怎么通信的?
  • invalidate / requestLayout 会不会立马刷新屏幕?
  • SurfaceView / GLSurfaceView 的底层实现原理?

搞 Android 搞了几年,我们对 VSync 信号应该会有一些了解,但是未必真正能理解其具体原理。比如 VSync 信号是从哪里来的?发到哪里去?有什么作用?本文主要讲解发到哪里去,至于从哪里来的大家可以看看之前的内容。

2. 请求 VSync 信号

如果我们的界面需要发生变化,一般都会来到 ViewRootImpl 的 requestLayout 方法,有可能是手动触发的也有可能是被动触发的,在这个方法里面我们会主动去请求接收 VSync 信号,当下一次 VSync 信号的来的时候会主动回掉回来,然后才开始真正的绘制流程。

    @Override    public void requestLayout() {      if (!mHandlingLayoutInLayoutRequest) {        checkThread();        mLayoutRequested = true;        scheduleTraversals();      }    }    void scheduleTraversals() {      if (!mTraversalScheduled) {        mTraversalScheduled = true;        // 插入一条消息屏障        mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();        // post 一个 Callback        mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);      }    }    private void postCallbackDelayedInternal(int callbackType, Object action, Object token, long delayMillis) {      synchronized (mLock) {        ...        if (dueTime <= now) {          scheduleFrameLocked(now);        } else {          ...        }      }    }    private void scheduleFrameLocked(long now) {      if (!mFrameScheduled) {        mFrameScheduled = true;        if (USE_VSYNC) {          // 是否在 Choreographer 的工作线程          if (isRunningOnLooperThreadLocked()) {            scheduleVsyncLocked();          } else {            Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);            msg.setAsynchronous(true);            mHandler.sendMessageAtFrontOfQueue(msg);          }        } else {          ...        }      }    }    // 请求接收下一次 VSync 信号    private void scheduleVsyncLocked() {      mDisplayEventReceiver.scheduleVsync();    }

由上面的源码可以看出,每一次调用 requestLayout 方法,都会主动调用 scheduleVsync 方法来接收下一次的 VSync 信号。也就是说在下一次 VSync 信号来之前,就算连续调用 n 次的 requestLayout 方法,也并不会触发刷新绘制流程。

3. 接收 VSync 信号

应用 App 请求了要接收下一次的 VSync 信号,那么 SurfaceFlinger 服务怎么把 VSync 信号,发给我们的应用 App ?这个得从 DisplayEventReceiver 的初始化入手,涉及到跨进程通信也涉及到 Native 层源码。

    public DisplayEventReceiver(Looper looper) {      ...      // nativeInit       mReceiverPtr = nativeInit(new WeakReference(this), mMessageQueue);    }    static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak, jobject messageQueueObj) {      sp receiver = new NativeDisplayEventReceiver(env, receiverWeak, messageQueue);      // 初始化方法      status_t status = receiver->initialize();      receiver->incStrong(gDisplayEventReceiverClassInfo.clazz); // retain a reference for the object      return reinterpret_cast(receiver.get());    }    status_t NativeDisplayEventReceiver::initialize() {      // 接收端的 fd 添加到 Looper      int rc = mMessageQueue->getLooper()->addFd(mReceiver.getFd(), 0, Looper::EVENT_INPUT,this, NULL);      return OK;    }    // 跨进程创建一个 mEventConnection 对象    DisplayEventReceiver::DisplayEventReceiver() {      sp sf(ComposerService::getComposerService());      if (sf != NULL) {        mEventConnection = sf->createDisplayEventConnection();        if (mEventConnection != NULL) {            mDataChannel = mEventConnection->getDataChannel();        }      }    }    // 获取接收端的 fd    int DisplayEventReceiver::getFd() const {      if (mDataChannel == NULL)          return NO_INIT;      return mDataChannel->getFd();    }    // VSync 信号来会回调到这个方法    int NativeDisplayEventReceiver::handleEvent(int receiveFd, int events, void* data) {      // Drain all pending events, keep the last vsync.      nsecs_t vsyncTimestamp;      int32_t vsyncDisplayId;      uint32_t vsyncCount;      if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount)) {        mWaitingForVsync = false;        dispatchVsync(vsyncTimestamp, vsyncDisplayId, vsyncCount);      }      return 1; // keep the callback    }    void NativeDisplayEventReceiver::dispatchVsync(nsecs_t timestamp, int32_t id, uint32_t count) {      JNIEnv* env = AndroidRuntime::getJNIEnv();      ScopedLocalRef receiverObj(env, jniGetReferent(env, mReceiverWeakGlobal));      if (receiverObj.get()) {        // 回掉到 Java 层的 dispatchVsync 方法        env->CallVoidMethod(receiverObj.get(),                gDisplayEventReceiverClassInfo.dispatchVsync, timestamp, id, count);      }    }    @Override    public void onVsync(long timestampNanos, int builtInDisplayId, int frame) {      // 发消息执行 doFrame 方法,真正开始刷新绘制流程      mTimestampNanos = timestampNanos;      mFrame = frame;      Message msg = Message.obtain(mHandler, this);      msg.setAsynchronous(true);      mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);    }

DisplayEventReceiver 在初始化时会创建与 SurfaceFlinger 的 Connection 连接,当应用 App 主动发起 requestNextVsync 后,SurfaceFlinger 会在下一个 VSync 信号来的时候,主动通知我们的应用 App ,回掉到 Java 层的 onVsync 方法,开始真正的刷新绘制流程。

视频地址:https://pan.baidu.com/s/1tQ7omRNg8BgldnkjdlBPlw 
视频密码:6hlc

更多相关文章

  1. Android studio 连接数据库小经历遇到的问题以及解决方法(java.sq
  2. Android源码分析(四)-----Android源码编译及刷机步骤
  3. Android 源码分析-打电话和发短信
  4. android中Handler的源码分析
  5. android 永远锁屏解决方法
  6. Android 进阶16:IntentService 使用及源码解析

随机推荐

  1. 关于eclipse中关联各版本Android.jar对应
  2. 思考Android架構(一):What & Why《Android
  3. Android使用Sqlite存储数据用法示例
  4. Android(安卓)淡入淡出动画
  5. :开源社区是个好地方:第一个android小程序
  6. [置顶] 一路16有你,一起17前行。Keep不止,A
  7. Android通知管理(NotificationManager)的使
  8. android 情人鸟(情人专属利器)
  9. Android(安卓)内存优化解决方案 (OOM)
  10. Android压缩图片到100K以下并保持不失真