当前位置: 首页 > news >正文

Android帧绘制流程深度解析 (一)

Android帧绘制技术有很多基础的知识,比如多buffer、vsync信号作用等基础知识点很多笔记讲的已经很详细了,我也不必再去总结,所以此处不再过多赘述安卓帧绘制技术,基础知识这篇文章总结的很好,一文读懂"系列:Android屏幕刷新机制 - 掘金 (juejin.cn),本文重点记录代码的学习笔记。
代码流程图:在这里插入图片描述

1、 Invalidate()

Android帧绘制的入口就是invalidate()函数,在调用invalidate函数后,当前界面上的内容就会被设置为脏区,需要进行更新。

void invalidate() {mDirty.set(0, 0, mWidth, mHeight);if (!mWillDrawSoon) {//防止在更新界面时,重复触发更新流程scheduleTraversals();}
}

2、 scheduleTraversals

void scheduleTraversals() {if (!mTraversalScheduled) {//同样是防止重复触发界面更新流行mTraversalScheduled = true;mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();//开启同步屏障,让帧绘//制的消息尽快得到处理mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);//(1)发送帧绘制的消息,以请求vsync信号。notifyRendererOfFramePending();pokeDrawLockIfNeeded();}
}

(1)处通过choreographer触发请求vsync流程。
此处的调用逻辑如流程图中5-7,最后调用到了postCallbackDelayedInternal方法:

private void postCallbackDelayedInternal(int callbackType,Object action, Object token, long delayMillis) {if (DEBUG_FRAMES) {Log.d(TAG, "PostCallback: type=" + callbackType+ ", action=" + action + ", token=" + token+ ", delayMillis=" + delayMillis);}synchronized (mLock) {final long now = SystemClock.uptimeMillis();//获取当前时间final long dueTime = now + delayMillis;//上面调用过来的时候,时间延迟为0mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);//将callback放进callbackQueue中,不同的callbackType对应不用的事件,后面再具体说明,此处type是Traversalif (dueTime <= now) {scheduleFrameLocked(now);//立即触发帧绘制} else {Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);msg.arg1 = callbackType;msg.setAsynchronous(true);mHandler.sendMessageAtTime(msg, dueTime);//时间还没到,所以通过延迟消息触发请求vsync流程}}
}

3、 scheduleFrameLocked:

因为帧绘制都是立即执行的,所以此处直接查看scheduleFrameLocked,其实如果是走到延迟消息的话,流程也差不多,这里大概的解释下:
首先通过mHandler.sendMessageAtTime(msg, dueTime)发送延时消息,消息的target就是choreographer中的mHandler,而mHandler的类型是FrameHandler。所以找到FrameHandler的定义:

private final class FrameHandler extends Handler {public FrameHandler(Looper looper) {super(looper);}@Overridepublic void handleMessage(Message msg) {switch (msg.what) {case MSG_DO_FRAME:doFrame(System.nanoTime(), 0, new DisplayEventReceiver.VsyncEventData());break;case MSG_DO_SCHEDULE_VSYNC:doScheduleVsync();break;case MSG_DO_SCHEDULE_CALLBACK:doScheduleCallback(msg.arg1);//此处会走到这里。break;}}
}

可以看到此处会走到case2,然后继续往下找就是这里:

void doScheduleCallback(int callbackType) {synchronized (mLock) {if (!mFrameScheduled) {final long now = SystemClock.uptimeMillis();if (mCallbackQueues[callbackType].hasDueCallbacksLocked(now)) {scheduleFrameLocked(now);}}}
}

所以最终还是走到了scheduleFrameLocked方法了。再次回到scheduleFrameLocked方法的解读:

private void scheduleFrameLocked(long now) {if (!mFrameScheduled) {//同样是为了防止重复触发该流程。mFrameScheduled = true;if (USE_VSYNC) {//如果是使用Vsync机制,这里好像是android4之后都是默认使用vsync机制的if (DEBUG_FRAMES) {Log.d(TAG, "Scheduling next frame on vsync.");}if (isRunningOnLooperThreadLocked()) {//如果当前线程有looper的话scheduleVsyncLocked();//请求vsync} else {//如果当前线程无Looper的话需要通过消息机制发送消息到Looper线程进行触发Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);msg.setAsynchronous(true);mHandler.sendMessageAtFrontOfQueue(msg);}} else {//这里就是无vsync机制时的绘帧逻辑final long nextFrameTime = Math.max(mLastFrameTimeNanos / TimeUtils.NANOS_PER_MS + sFrameDelay, now);if (DEBUG_FRAMES) {Log.d(TAG, "Scheduling next frame in " + (nextFrameTime - now) + " ms.");}Message msg = mHandler.obtainMessage(MSG_DO_FRAME);msg.setAsynchronous(true);mHandler.sendMessageAtTime(msg, nextFrameTime);}}
}

上述代码中有个比较有意思的概念,就是isRunningOnLooperThreadLocked()这个方法,这个方法的解读就是运行在looper线程上。方法定义如下:

private boolean isRunningOnLooperThreadLocked() {return Looper.myLooper() == mLooper;
}

这里可以参考之前的说Android消息机制这节内容,这个myLooper方法还会牵扯到ThreadLocal的内容,这里就不再细说,感兴趣的可以看看:Android消息机制-CSDN博客。
主要思想就是不是每个线程都会有Looper对象(choreographer的初始化参数中有Looper这个量),但是有Looper的都可以初始化一个choreographer,所以这里就是检查当前线程的Looper是否与choreographer关联的线程相同。

4、 scheduleVsyncLocked:

该方法用来请求Vsync信号:

private void scheduleVsyncLocked() {try {Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Choreographer#scheduleVsyncLocked");mDisplayEventReceiver.scheduleVsync();} finally {Trace.traceEnd(Trace.TRACE_TAG_VIEW);}
}public void scheduleVsync() {if (mReceiverPtr == 0) {Log.w(TAG, "Attempted to schedule a vertical sync pulse but the display event "+ "receiver has already been disposed.");} else {nativeScheduleVsync(mReceiverPtr);}
}

可以看到该方法能直接回调到native层的方法以请求Vsync。这里的native层的调用后续会再详细详细讲解,本次暂时还未学习,剩下的流程13-20还需要再仔细看看

相关文章:

  • 筛斗数据:如何利用数据提取技术提高能源利用效率
  • 2024 年最新 Python 基于百度智能云实现短语音识别、语音合成详细教程
  • memcached介绍和详解
  • 【尚庭公寓SpringBoot + Vue 项目实战】图片上传(十)
  • 数学术语:“suprema” 和 “supremum”指什么
  • 刺客信条找不到emp.dll怎么解决?emp.dll缺失的解决方法解析
  • Arduino入门1——认识Arduino,点亮一个LED
  • 8个常用的辅助函数!!
  • try-with-resources 工作原理
  • DockerHub无法访问,国内镜像拉取迂回解决方案
  • 万字长文爆肝Spring(一)
  • CSS选择器种类总结
  • Spring Boot中Excel的导入导出的实现之EasyPoi框架使用教程
  • docker安装消息队列mq中的rabbit服务
  • python操作数据库,django操作数据库
  • 【跃迁之路】【444天】程序员高效学习方法论探索系列(实验阶段201-2018.04.25)...
  • Apache的80端口被占用以及访问时报错403
  • conda常用的命令
  • CSS魔法堂:Absolute Positioning就这个样
  • es的写入过程
  • IndexedDB
  • Java 23种设计模式 之单例模式 7种实现方式
  • Joomla 2.x, 3.x useful code cheatsheet
  • learning koa2.x
  • Windows Containers 大冒险: 容器网络
  • 阿里云ubuntu14.04 Nginx反向代理Nodejs
  • 阿里云前端周刊 - 第 26 期
  • 仿天猫超市收藏抛物线动画工具库
  • 基于web的全景—— Pannellum小试
  • 利用阿里云 OSS 搭建私有 Docker 仓库
  • 前端面试之闭包
  • 区块链分支循环
  • TPG领衔财团投资轻奢珠宝品牌APM Monaco
  • ​ 全球云科技基础设施:亚马逊云科技的海外服务器网络如何演进
  • ​​​​​​​ubuntu16.04 fastreid训练过程
  • ​​​​​​​开发面试“八股文”:助力还是阻力?
  • ​渐进式Web应用PWA的未来
  • ​软考-高级-系统架构设计师教程(清华第2版)【第20章 系统架构设计师论文写作要点(P717~728)-思维导图】​
  • #define、const、typedef的差别
  • #HarmonyOS:Web组件的使用
  • $NOIp2018$劝退记
  • (1)Android开发优化---------UI优化
  • (Redis使用系列) SpringBoot中Redis的RedisConfig 二
  • (大众金融)SQL server面试题(1)-总销售量最少的3个型号的车及其总销售量
  • (附源码)springboot美食分享系统 毕业设计 612231
  • (三)uboot源码分析
  • (四)图像的%2线性拉伸
  • (五)关系数据库标准语言SQL
  • .NET “底层”异步编程模式——异步编程模型(Asynchronous Programming Model,APM)...
  • .NET 4 并行(多核)“.NET研究”编程系列之二 从Task开始
  • .Net Attribute详解(上)-Attribute本质以及一个简单示例
  • .NET Core工程编译事件$(TargetDir)变量为空引发的思考
  • .Net OpenCVSharp生成灰度图和二值图
  • .net 程序发生了一个不可捕获的异常
  • .net 获取某一天 在当月是 第几周 函数