源码分析篇-Android绘制流程(三)requestLayout()与
invalidat。。。
本⽂主要探讨能够触发performTraversals()执⾏的invalidate()、postInvalidate()和requestLayout()⽅法的流程。在调⽤这三个⽅法到最
后执⾏到performTraversals()⽅法,涉及到到通过Choroegrapher请求Vsync信号,实现按帧绘制的流程,所以还会介绍Choroegrapher类的
⼯作流程。
⼀、requestLayout()流程
invalidate()和postInvalidate()能够触发View的重画,这两个⽅法最终会调⽤到performTraversals()中的performDraw()来完成重绘制,但
是是否会执⾏onMeasure()和onLayout()过程要根据标志位的状况来决定;requetLayout()⽅法也会调⽤到performTraversals()⽅法,但是
只会执⾏measure和layout流程,不会调⽤到draw流程来触发重画动作。直接来看tLayout()代码。
@CallSuper
publicvoidrequestLayout(){
if(mMeasureCache!=null)();
//如果当前的整个View树在进⾏布局流程的话,则会调⽤requestLayoutDuringLayout()
//让这次的布局延时执⾏
if(mAttachInfo!=null&&equestingLayout==null){
//Onlytriggerrequest-during-layoutlogicifthisistheviewrequestingit,
//nottheviewsinitsparenthierarchy
ViewRootImplviewRoot=getViewRootImpl();
if(viewRoot!=null&&yout()){
if(!tLayoutDuringLayout(this)){
return;
}
}
equestingLayout=this;
}
//PFLAG_FORCE_LAYOUT会在执⾏View的measure()和layout()⽅法时判断
//只有设置过该标志位,才会执⾏measure()和layout()流程
mPrivateFlags|=PFLAG_FORCE_LAYOUT;
mPrivateFlags|=PFLAG_INVALIDATED;
if(mParent!=null&&!utRequested()){
tLayout();
}
if(mAttachInfo!=null&&equestingLayout==this){
equestingLayout=null;
}
}
该⽅法主要是设置了PFLAG_FORCE_LAYOUT和PFLAG_INVALIDATED到当前View的Flag中,然后调⽤到当前View(当前View可
能是⼀个控件View,也可能是⼀个布局View,因为对于这两类View都能调⽤requestLayout()⽅法)的⽗布局View的requestLayout()⽅法,
⽗布局View是ViewGroup类型,没有重写该requestLayout()⽅法,所以实际还是调回到tLayout()⽅法的这套逻辑。这个过程,
就是设置当前View标志位后,就不断的向上调⽤⽗布局View的requestLayout(),最后调⽤到根View即DecorView的requestLayout(),⽽
DecorView的mParent变量指向的是当前窗⼝对应的ViewRootImpl对象,最后⼀次设置完DecorView标志位后,调⽤到
tLayout()⽅法,进⼊该代码。
@Override
publicvoidrequestLayout(){
//该boolean变量会在mLayout()开始时置为ture,结束置fal
//表⽰当前不处于Layout过程
if(!mHandlingLayoutInLayoutRequest){
checkThread();
mLayoutRequested=true;
scheduleTraversals();
}
}
如果当前不是正在执⾏layout过程,则会调⽤scheduleTraversals()⽅法,进⼊leTraversals()。
voidscheduleTraversals(){
if(!mTraversalScheduled){
//在下⼀段代码处会置回fal
//表⽰在排好这次绘制请求前,不再排其它的绘制请求
mTraversalScheduled=true;
mTraversalBarrier=per().getQueue().postSyncBarrier();
llback(
CK_TRAVERSAL,mTraversalRunnable,null);
if(!mUnbufferedInputDispatch){
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
这⾥主要是调⽤到了ViewRootImpl的另⼀个重要的变量mChoreographer,它是Choreographer类型的,这个对象会请求Vsync信号来
控制绘制的进⾏,实现了按帧进⾏绘制的机制,这个类会在后⽂进⾏介绍。该⽅法对于绘制的请求经过了Choreographer的编排后,最终会
调⽤回ersal()⽅法。
voiddoTraversal(){
if(mTraversalScheduled){
mTraversalScheduled=fal;
per().getQueue().removeSyncBarrier(mTraversalBarrier);
...//⽤于调试相关代码
performTraversals();
...//⽤于调试相关代码
}
}
然后调⽤到mTraversals()⽅法。
⼆、invalidate()与postInvalidate()流程
invalidate()与postInvalidate()都是⽤于被调⽤来触发View的更新(重画)动作,区别在于invalidate()⽅法是在UI线程⾃⾝中使⽤,⽽
postInvalidate()是⾮UI线程中使⽤。⾸先来看validate()。
publicvoidpostInvalidate(){
postInvalidateDelayed(0);
}
publicvoidpostInvalidateDelayed(longdelayMilliconds){
//WetryonlywiththeAttachInfobecauthere'snopointininvalidating
//ifwearenotattachedtoourwindow
finalAttachInfoattachInfo=mAttachInfo;
if(attachInfo!=null){
chInvalidateDelayed(this,delayMilliconds);
}
}
调⽤到了对应的ViewRootImpl对象的dispatchInvalidateDelayed()⽅法,进⼊该代码。
publicvoiddispatchInvalidateDelayed(Viewview,longdelayMilliconds){
Messagemsg=Message(MSG_INVALIDATE,view);
ssageDelayed(msg,delayMilliconds);
}
这⾥实现了⼀个消息机制,发送了MSG_INVSLIDSTE。进⼊处理消息的Message()⽅法。
@Override
publicvoidhandleMessage(Messagemsg){
switch(){
caMSG_INVALIDATE:
((View)).invalidate();
break;
...
}
这⾥实际上就是调回了调⽤postInvalidate()⽅法的View的invalidate()⽅法。由于invalidate()⽅法只能在UI线程执⾏,所以postInvalidate
只是实现了⼀个消息机制,让⽤户能够在⾮UI线程使⽤,最终还是调⽤到invalidate()⽅法来触发重画,实现界⾯更新动作。继续来看
date()⽅法,该⽅法逻辑的实际实际上时调⽤到invalidateInternal()⽅法来实现的。
publicvoidinvalidate(){
invalidate(true);
}
voidinvalidate(booleaninvalidateCache){
//mLeft、mRigth、mTop、mBottom记录的是当前View边界距离其⽗布局View边界的距离
invalidateInternal(0,0,mRight-mLeft,mBottom-mTop,invalidateCache,true);
}
voidinvalidateInternal(intl,intt,intr,intb,booleaninvalidateCache,
booleanfullInvalidate){
if(mGhostView!=null){
date(true);
return;
}
//如果当前视图为不可见状态且没有动画正在执⾏,且其⽗布局也没有过渡动画执⾏,则跳过
if(skipInvalidate()){
return;
}
//当前View没有正在执⾏该⽅法
//或绘制缓存可⽤或未重绘过或透明度发⽣改变
//PFLAG_DRAWN会在该⽅法内去改标志位
//PFLAG_INVALIDATED会在()⽅法执⾏时去掉该标志位
if((mPrivateFlags&(PFLAG_DRAWN|PFLAG_HAS_BOUNDS))==(PFLAG_DRAWN|PFLAG_HAS_BOUNDS)
||(invalidateCache&&(mPrivateFlags&PFLAG_DRAWING_CACHE_VALID)==PFLAG_DRAWING_CACHE_VALID)
||(mPrivateFlags&PFLAG_INVALIDATED)!=PFLAG_INVALIDATED
||(fullInvalidate&&isOpaque()!=mLastIsOpaque)){
//如果需要全部重绘,invalidate()未传参调⽤时默认为true
if(fullInvalidate){
mLastIsOpaque=isOpaque();
mPrivateFlags&=~PFLAG_DRAWN;
}
mPrivateFlags|=PFLAG_DIRTY;
if(invalidateCache){
mPrivateFlags|=PFLAG_INVALIDATED;
mPrivateFlags&=~PFLAG_DRAWING_CACHE_VALID;
}
//Propagatethedamagerectangletotheparentview.
//damage记录的区域是需要更新的dirty区域,当前的坐标时相对于⾃⾝来设置的
//通过不断调⽤到⽗类的invalidateChild()⽅法,来不断更新dirty区域的相对坐标
finalAttachInfoai=mAttachInfo;
finalViewParentp=mParent;
if(p!=null&&ai!=null&&l
finalRectdamage=valRect;
(l,t,r,b);
dateChild(this,damage);
}
//Damagetheentireprojectionreceiver,ifnecessary.
if(mBackground!=null&&ected()){
finalViewreceiver=getProjectionReceiver();
if(receiver!=null){
InParent();
}
}
//DamagetheentireIsolatedZVolumereceivingthisview'sshadow.
if(isHardwareAccelerated()&&getZ()!=0){
damageShadowReceiver();
}
}
}
这⾥会通过调⽤mParent的invalidateChild()⽅法,来触发⽗类对于dirty区域的调整(可能会调整可能还是原区域)及改区域相对坐标的
调整。进⼊dateChild()⽅法。
@Override
publicfinalvoidinvalidateChild(Viewchild,finalRectdirty){
ViewParentparent=this;
finalAttachInfoattachInfo=mAttachInfo;
if(attachInfo!=null){
//Ifthechildisdrawingananimation,wewanttocopythisflagonto
//ourlvesandtheparenttomakesuretheinvalidaterequestgoes
//through
//drawAnimation记录调⽤该⽅法的⼦View是否正在执⾏动画
finalbooleandrawAnimation=(teFlags&PFLAG_DRAW_ANIMATION)
==PFLAG_DRAW_ANIMATION;
//Checkwhetherthechildthatrequeststheinvalidateisfullyopaque
//Viewsbeinganimatedortransformedarenotconsideredopaquebecauwemay
//beinvalidatingtheiroldpositionandneedtheparenttopaintbehindthem.
//调⽤该⽅法的⼦View是否不透明:处于不透明状态且没有在执⾏动画且变化矩阵没有变化
//Matrix可以⽤于View的平移、缩放、扩放、旋转等操作,⽐如某些应⽤上的双指缩放功能
MatrixchildMatrix=rix();
finalbooleanisOpaque=ue()&&!drawAnimation&&
mation()==null&&tity();
//Markthechildasdirty,usingtheappropriateflag
//Makesurewedonottbothflagsatthesametime
intopaqueFlag=isOpaque?PFLAG_DIRTY_OPAQUE:PFLAG_DIRTY;
if(Type!=LAYER_TYPE_NONE){
mPrivateFlags|=PFLAG_INVALIDATED;
mPrivateFlags&=~PFLAG_DRAWING_CACHE_VALID;
}
finalint[]location=idateChildLocation;
//记录⼦View边界距离⽗View左边界和上边界的距离到Location中,⽤于下⼀段代码中的计算
location[CHILD_LEFT_INDEX]=;
location[CHILD_TOP_INDEX]=;
//如果⼦View设置了变换矩阵,则根据变换矩阵调整dirty区域
if(!tity()||
(mGroupFlags&_SUPPORT_STATIC_TRANSFORMATIONS)!=0){
RectFboundingRect=ansformRect;
(dirty);
MatrixtransformMatrix;
if((mGroupFlags&_SUPPORT_STATIC_TRANSFORMATIONS)!=0){
Transformationt=ansformation;
booleantransformed=getChildStaticTransformation(child,t);
if(transformed){
transformMatrix=trix;
(rix());
if(!tity()){
cat(childMatrix);
}
}el{
transformMatrix=childMatrix;
}
}el{
transformMatrix=childMatrix;
}
t(boundingRect);
((int)(),
(int)(),
(int)(),
(int)());
}
//这是⼀个从当前的布局View向上不断遍历当前布局View的⽗布局,最后遍历到ViewRootImpl的循环
do{
Viewview=null;
//parent可能为ViewGroup类型,也可能为ViewRootImpl类型
//最后⼀次循环执⾏时为ViewRootImpl类型
if(parentinstanceofView){
view=(View)parent;
}
//如果⼦View正在执⾏动画,设置遍历的⽗布局View的动画标识
if(drawAnimation){
if(view!=null){
teFlags|=PFLAG_DRAW_ANIMATION;
}elif(parentinstanceofViewRootImpl){
((ViewRootImpl)parent).mIsAnimating=true;
}
}
//Iftheparentisdirtyopaqueornotdirty,markitdirtywiththeopaque
//flagcomingfromthechildthatinitiatedtheinvalidate
//设置当前ViewGroup的Dirty标识,表⽰当前的ViewGroup需要重绘
if(view!=null){
if((lags&FADING_EDGE_MASK)!=0&&
idColor()==0){
opaqueFlag=PFLAG_DIRTY;
}
if((teFlags&PFLAG_DIRTY_MASK)!=PFLAG_DIRTY){
teFlags=(teFlags&~PFLAG_DIRTY_MASK)|opaqueFlag;
}
}
//调⽤当前布局View的invalidateChildParent()⽅法,返回的值为当前布局View的⽗布局
//通过循环向上调⽤,最后返回的根布局是ViewRootImpl对象
parent=dateChildInParent(location,dirty);
if(view!=null){
//Accountfortransformoncurrentparent
Matrixm=rix();
if(!tity()){
RectFboundingRect=ansformRect;
(dirty);
t(boundingRect);
((int)(),
(int)(),
(int)(),
(int)());
}
}
}while(parent!=null);
}
}
在do-while循环中会调⽤到parent=dateChildInParent(location,dirty),这⾥执⾏到dateChildInParent()⽅
法。
@Override
publicViewParentinvalidateChildInParent(finalint[]location,finalRectdirty){
//
if((mPrivateFlags&PFLAG_DRAWN)==PFLAG_DRAWN||
(mPrivateFlags&PFLAG_DRAWING_CACHE_VALID)==PFLAG_DRAWING_CACHE_VALID){
//如果ViewGroup有没有动画执⾏或者动画已经完成
if((mGroupFlags&(FLAG_OPTIMIZE_INVALIDATE|FLAG_ANIMATION_DONE))!=
FLAG_OPTIMIZE_INVALIDATE){
//dirty记录的是最开始调到invalidate()的View的区域
//dirty的四个坐标值值在执⾏下⾯代码是相对于当前循环到上⼀个ViewGroup来确定的
//这⾥做了⼀个偏移动作,偏移的量是当前上⼀个ViewGroup相对于现在ViewGroup的偏移值
//做完下⾯的偏移操作后,dirty的四个坐标就是想对于当前ViewGroup的坐标值了
([CHILD_LEFT_INDEX]-mScrollX,
location[CHILD_TOP_INDEX]-mScrollY);
//如果当前ViewGroup需要裁剪View
//则将当前ViewGroup的区域与View的区域做求并集的操作
if((mGroupFlags&FLAG_CLIP_CHILDREN)==0){
(0,0,mRight-mLeft,mBottom-mTop);
}
finalintleft=mLeft;
finalinttop=mTop;
//如果当前ViewGroup需要裁剪View,且ViewGroup区域与View区域没有并集,则dirty置空
if((mGroupFlags&FLAG_CLIP_CHILDREN)==FLAG_CLIP_CHILDREN){
if(!ect(0,0,mRight-left,mBottom-top)){
ty();
}
}
mPrivateFlags&=~PFLAG_DRAWING_CACHE_VALID;
//⽤于循环到下⼀个ViewGroup时做offt操作
location[CHILD_LEFT_INDEX]=left;
location[CHILD_TOP_INDEX]=top;
if(mLayerType!=LAYER_TYPE_NONE){
mPrivateFlags|=PFLAG_INVALIDATED;
}
returnmParent;
}el{//如果当前ViewGroup中有动画要执⾏
mPrivateFlags&=~PFLAG_DRAWN&~PFLAG_DRAWING_CACHE_VALID;
location[CHILD_LEFT_INDEX]=mLeft;
location[CHILD_TOP_INDEX]=mTop;
//如果需要对⼦View裁剪则设置dirty为当前ViewGroup区域
//如果不需要则求当前ViewGroup区域与原ditry区域并集
if((mGroupFlags&FLAG_CLIP_CHILDREN)==FLAG_CLIP_CHILDREN){
(0,0,mRight-mLeft,mBottom-mTop);
}el{
//incathedirtyrectextendsoutsidetheboundsofthiscontainer
(0,0,mRight-mLeft,mBottom-mTop);
}
if(mLayerType!=LAYER_TYPE_NONE){
mPrivateFlags|=PFLAG_INVALIDATED;
}
returnmParent;
}
}
returnnull;
}
invalidateChildInParent()主要是完成了dirty区域在调⽤该⽅法的ViewGroup中的更新,dirty指⽰的区域就是需要重绘制的区域。如果
ViewGroup没有动画在执⾏,则dirty区域还是原来的区域,只需要通过偏移操作更改该区域的坐标值从相对于上⼀个ViewGroup(⽗
ViewGroup),到相对于当前ViewGroup;如果有动画要执⾏,则表⽰当前整个ViewGroup都需要重绘,更改dirty值为当前ViewGroup区
域。
do-while最后⼀次循环最后会调⽤到dateChildInParent()⽅法,进⼊该代码。
@Override
publicViewParentinvalidateChildInParent(int[]location,Rectdirty){
checkThread();
if(DEBUG_DRAW)Log.v(mTag,"Invalidatechild:"+dirty);
//如果传⼊⼀个nulldrity,则表⽰要重绘当前ViewRootImpl指⽰的整个区域
//如果传⼊⼀个emptydirty,则表⽰经过计算需要重绘的区域不需要绘制
if(dirty==null){
invalidate();
returnnull;
}elif(y()&&!mIsAnimating){
returnnull;
}
...
invalidateRectOnScreen(dirty);
returnnull;
}
调⽤到了dateRectOnScreen()⽅法,进⼊该代码。
privatevoidinvalidateRectOnScreen(Rectdirty){
//mDirty记录的是当前ViewRootImpl⾥还未进⾏重绘需要重绘的区域
//mDirty会在()⽅法结尾处设置为empty
finalRectlocalDirty=mDirty;
if(!y()&&!ns(dirty)){
noreDirtyState=true;
eDirtyState=true;
}
//Addthenewdirtyrecttothecurrentone
//当前已有的dirty区域与此次dirty区域做并集
(,,,);
//Interctwiththeboundsofthewindowtoskip
//updatesthatlieoutsideofthevisibleregion
finalfloatappScale=cationScale;
//处理窗⼝缩放与做完并集的localDirty做交集
finalbooleanintercted=ect(0,0,
(int)(mWidth*appScale+0.5f),(int)(mHeight*appScale+0.5f));
//如果没有交集
if(!intercted){
ty();
}
//mWillDrawSoon在performTraversals()⽅法开始时置为true,结束时置fal
//如果没有在执⾏performTraversals&&(intercted||正在执⾏动画)
if(!mWillDrawSoon&&(intercted||mIsAnimating)){
scheduleTraversals();
}
}
最后会调⽤到scheduleTraversals()⽅法,后续在请求到Vsync信号后,便会调⽤到peformTraversals()⽅法。
三、Choreographer类分析
“编舞类”Choreoprapher的作⽤是编排输⼊事件、动画事件和绘制事件的执⾏,通过调⽤llback()⽅法,向
Choreoprapher加⼊需要编排的事件,⽽Choreoprapher则通过请求Vsync信号,来控制这些事件按照屏幕刷新周期有规律的执⾏,即是实现
了按帧绘制的机制。
在ViewRootImpl中,会调⽤mChoreographer=tance()来初始化⼀个Choreographer变量。进⼊
tance()代码。
privatestaticfinalThreadLocal
newThreadLocal
@Override
protectedChoreographerinitialValue(){
Looperlooper=er();
if(looper==null){
thrownewIllegalStateException("Thecurrentthreadmusthavealooper!");
}
returnnewChoreographer(looper);
}
};
publicstaticChoreographergetInstance(){
();
}
这⾥实际调⽤了ThreadLocal类型的静态常量的get()⽅法,ThreadLocal中保存的类型是Choreographer类。根据ThreadLocal机
制,()⽅法会调⽤到上⾯代码中实现的initialValue()⽅法,该⽅法返回⼀个Choregrapher类型对象,返回的该对象即作
为getInstance()⽅法的返回,也是最后赋值给了ViewRootImpl中的mChoreogropher变量。在initialValue()⽅法中会new⼀个Choreographer
对象,进⼊构建⽅法。
privateChoreographer(Looperlooper){
//调⽤该⽅法的源头是UI线程,所有looper为UI线程的looper
mLooper=looper;
mHandler=newFrameHandler(looper);
//如果系统使⽤Vsync机制,则创建⼀个Vsync信号的接收器FrameDisplayEventReceiver类
mDisplayEventReceiver=USE_VSYNC?newFrameDisplayEventReceiver(looper):null;
mLastFrameTimeNanos=_VALUE;
mFrameIntervalNanos=(long)(1000000000/getRefreshRate());
//创建回调数组,CALLBAKCK_LAST=3,后⽂详解
mCallbackQueues=newCallbackQueue[CALLBACK_LAST+1];
for(inti=0;i<=CALLBACK_LAST;i++){
mCallbackQueues[i]=newCallbackQueue();
}
}
⾸先来说mCallbackQueues,这是⼀个长度为4的CallbackQueue类型的数组,即保存了四个回调队列。每个回调队列能够保存多个
CallbackRecord,即是回调事件。这四个队列分别保存四类回调事件:Input事件、Animation事件、Draw事件,还有⼀种是⽤来解决动画启
动问题的事件。在leTraversals()⽅法中,便会调⽤相关⽅法向队列中添加⼀个Draw事件,并触发后续到请求信号来处
理事件的动作。
voidscheduleTraversals(){
...
llback(
CK_TRAVERSAL,mTraversalRunnable,null);
...
}
继续来看llback()⽅法,该⽅法是调⽤到postCallbackDelayedInternal()⽅法来实现主要逻辑。
publicvoidpostCallback(intcallbackType,Runnableaction,Objecttoken){
postCallbackDelayed(callbackType,action,token,0);
}
publicvoidpostCallbackDelayed(intcallbackType,
Runnableaction,Objecttoken,longdelayMillis){
...//异常情况判断
postCallbackDelayedInternal(callbackType,action,token,delayMillis);
}
privatevoidpostCallbackDelayedInternal(intcallbackType,
Objectaction,Objecttoken,longdelayMillis){
...//Debuglog
synchronized(mLock){
finallongnow=Millis();
finallongdueTime=now+delayMillis;
//将此次回调事件添加到对应类型的事件队列
mCallbackQueues[callbackType].addCallbackLocked(dueTime,action,token);
if(dueTime<=now){
//⽴刻安排执⾏
scheduleFrameLocked(now);
}el{
//延时处理,还是会调⽤到scheduleFrameLocked()⽅法
Messagemsg=Message(MSG_DO_SCHEDULE_CALLBACK,action);
1=callbackType;
nchronous(true);
ssageAtTime(msg,dueTime);
}
}
}
调⽤addCallbackLock()⽅法,会根据本次事件信息⽣成⼀个CallbackRecord,添加到队列中,但并不⼀定添加在队列到尾部。队列中
所有事件的排列是按照dueTime的值由⼩到⼤排列⼤,即越快要求执⾏的事件排列得越前,所以在添加事件到队列时会根据dueTime插⼊到
对应的位置。
插⼊队列操作完成后,会调⽤scheduleFrameLoacked()⽅法。
privatevoidscheduleFrameLocked(longnow){
if(!mFrameScheduled){
mFrameScheduled=true;
if(USE_VSYNC){//如果使⽤了Vsync机制
if(DEBUG_FRAMES){
Log.d(TAG,"Schedulingnextframeonvsync.");
}
//IfrunningontheLooperthread,thenschedulethevsyncimmediately,
//otherwipostamessagetoschedulethevsyncfromtheUIthread
//assoonaspossible.
//如果前线程开启了Looper,则调⽤scheduleVsyncLocked()请求Vsync信号
if(isRunningOnLooperThreadLocked()){
scheduleVsyncLocked();
}el{//如果当前线程未启动Looper
//则发消息到调⽤创建Choreographer的线程来请求Vsync信号
Messagemsg=Message(MSG_DO_SCHEDULE_VSYNC);
nchronous(true);
ssageAtFrontOfQueue(msg);
}
}el{//如果未使⽤Vsync机制,则⼿动计算下⼀次绘制时间,使⽤延时消息来控制
finallongnextFrameTime=(
mLastFrameTimeNanos/_PER_MS+sFrameDelay,now);
if(DEBUG_FRAMES){
Log.d(TAG,"Schedulingnextframein"+(nextFrameTime-now)+"ms.");
}
Messagemsg=Message(MSG_DO_FRAME);
nchronous(true);
ssageAtTime(msg,nextFrameTime);
}
}
}
⼀般情况下是实⽤Vsync机制的,且scheduleFrameLocked()也是被UI线程调⽤执⾏的,所以直接调⽤到
leVsyncLocked()⽅法,进⼊该代码。
privatevoidscheduleVsyncLocked(){
leVsync();
}
这⾥直接调⽤到mDisplayEventReceiver的scheduleVsync()⽅法,该变量是FrameDisplayEventReceiver类型的,该类继承⾃
DisplayEventReceiver类。scheduleVsync()相当于发起了⼀次Vsync请求,这样在请求之后下⼀个Vsync信号发出
时,FrameDisplayEventReceiver类便能接收到这词Vsync信号,会调⽤到FrameDisplayEventReceiver类的onVsync()⽅法,在onVsync()
⽅法中会发送消息到UI线程,调⽤到doFrame()⽅法,Frame是帧的意思,doFrame则表⽰这次接收到Vsync信号的这⼀帧内要做的事,进⼊
e()⽅法(FrameDisplayEventReceiver类时Choreographer内部类),
voiddoFrame(longframeTimeNanos,intframe){
finallongstartNanos;
synchronized(mLock){
//该变量会在scheduleFrameLocked()⽅法开始时设置为true,本⽅法结束置为fal
//表⽰有callback事件需要安排执⾏
if(!mFrameScheduled){
return;//noworktodo
}
if(DEBUG_JANK&&mDebugPrintNextFrameTimeDelta){
mDebugPrintNextFrameTimeDelta=fal;
Log.d(TAG,"Frametimedelta:"
+((frameTimeNanos-mLastFrameTimeNanos)*0.000001f)+"ms");
}
//frameTimeNanos表⽰Vsync信号发出的时间或者帧开始的时间
longintendedFrameTimeNanos=frameTimeNanos;
//当前时间
startNanos=me();
finallongjitterNanos=startNanos-frameTimeNanos;
//当前时间距离Vsync信号时间超过了屏幕的刷新周期,即⼀帧16ms的时间
if(jitterNanos>=mFrameIntervalNanos){
finallongskippedFrames=jitterNanos/mFrameIntervalNanos;
//如果超过太多,即跳过了太多帧,则打出Log提⽰跳过了太多帧,可能是主线程做了太多事了
if(skippedFrames>=SKIPPED_FRAME_WARNING_LIMIT){
Log.i(TAG,"Skipped"+skippedFrames+"frames!"
+"Theapplicationmaybedoingtoomuchworkonitsmainthread.");
}
finallonglastFrameOfft=jitterNanos%mFrameIntervalNanos;
if(DEBUG_JANK){
Log.d(TAG,"Misdvsyncby"+(jitterNanos*0.000001f)+"ms"
+"whichismorethantheframeintervalof"
+(mFrameIntervalNanos*0.000001f)+"ms!"
+"Skipping"+skippedFrames+"framesandttingframe"
+"timeto"+(lastFrameOfft*0.000001f)+"msinthepast.");
}
frameTimeNanos=startNanos-lastFrameOfft;
}
//如果距离最后⼀帧时间未超过屏幕刷新周期,则重新请求Vsync信号
if(frameTimeNanos
if(DEBUG_JANK){
Log.d(TAG,"uetoa"
+"gfornextvsync.");
}
scheduleVsyncLocked();
return;
}
nc(intendedFrameTimeNanos,frameTimeNanos);
mFrameScheduled=fal;
//设置本次帧的执⾏时间为最后⼀次的帧执⾏时间
mLastFrameTimeNanos=frameTimeNanos;
}
try{
egin(_TAG_VIEW,"Choreographer#doFrame");
imationClock(frameTimeNanos/_PER_MS);
//依次从队列中取出这四类事件进⾏执⾏
//但不⼀定都会执⾏这四类事件,要看队列中是否有post过且符合这⼀帧执⾏到条件的事件
putHandlingStart();
doCallbacks(CK_INPUT,frameTimeNanos);
imationsStart();
doCallbacks(CK_ANIMATION,frameTimeNanos);
rformTraversalsStart();
doCallbacks(CK_TRAVERSAL,frameTimeNanos);
doCallbacks(CK_COMMIT,frameTimeNanos);
}finally{
AnimationClock();
nd(_TAG_VIEW);
}
if(DEBUG_FRAMES){
finallongendNanos=me();
Log.d(TAG,"Frame"+frame+":Finished,took"
+(endNanos-startNanos)*0.000001f+"ms,latency"
+(startNanos-frameTimeNanos)*0.000001f+"ms.");
}
}
该⽅法会调⽤doCallbacks⽅法来依次执⾏当前时间对应的四类事件。由于CALLBACK_COMMIT是⼀种修正属性动画启动事件过长导
致掉帧问题的⼀种机制,并不是真正会执⾏在主线程的流程,这⾥不做详解。所以在执⾏事件时,主要是依次执⾏了input、animation和
traversal事件。我们可以抓⼀个systrace来直观的了解这个过程,以UC浏览器双指扩放页⾯的绘制过程中的某⼀帧为例。
doFrame()⽅法中⾸先执⾏来input事件的处理,然后后⾯有个很短的矩形体条,执⾏的是animation事件;之后便是执⾏到了traversal事
件,在执⾏traversal流程中执⾏了draw流程,但并没有执⾏measure和layout流程,因为本次绘制不需要重新测量和布局;在执⾏draw流程过
程中实际调⽤到了View的draw()⽅法。
继续来看backs()⽅法的实现。
voiddoCallbacks(intcallbackType,longframeTimeNanos){
CallbackRecordcallbacks;
synchronized(mLock){
//Weu"now"todeterminewhencallbacksbecomeduebecauit'spossible
//forearlierprocessingphasinaframetopostcallbacksthatshouldrun
//inafollowingpha,suchasaninputeventthatcausananimationtostart.
finallongnow=me();
//根据帧开始的时间,取出当前该类型队列中的⼀个callback事件
callbacks=mCallbackQueues[callbackType].extractDueCallbacksLocked(
now/_PER_MS);
if(callbacks==null){
return;
}
mCallbacksRunning=true;
...//CALLBACK_COMMIT事件的处理try{
egin(_TAG_VIEW,CALLBACK_TRACE_TITLES[callbackType]);
for(CallbackRecordc=callbacks;c!=null;c=){
if(DEBUG_FRAMES){
Log.d(TAG,"RunCallback:type="+callbackType
+",action="++",token="+
+",latencyMillis="+(Millis()-e));
}
(frameTimeNanos);
}
}finally{
synchronized(mLock){
mCallbacksRunning=fal;
do{
finalCallbackRecordnext=;
recycleCallbackLocked(callbacks);
callbacks=next;
}while(callbacks!=null);
}
nd(_TAG_VIEW);
}
}
⾸先来看下tDueCallbacksLocked()⽅法,了解队列取事件执⾏的机制。
publicCallbackRecordextractDueCallbacksLocked(longnow){
//返回队列头事件,即要求最快要执⾏的事件
CallbackRecordcallbacks=mHead;
if(callbacks==null||e>now){
returnnull;
}
//把头回调事件后⾯所有执⾏时间已经到了事件全部舍弃
CallbackRecordlast=callbacks;
CallbackRecordnext=;
while(next!=null){
if(e>now){
=null;
break;
}
last=next;
next=;
}
//next表⽰的是未到执⾏时间且要求执⾏到时间最早的事件
mHead=next;
returncallbacks;
}
取出当前帧需要执⾏的回调事件后,便会执⾏到该事件的run()⽅法,在使⽤这⾥会调⽤到CallbackRecord的run()⽅法。
privatestaticfinalclassCallbackRecord{
publicCallbackRecordnext;
publiclongdueTime;
publicObjectaction;//RunnableorFrameCallback
publicObjecttoken;
publicvoidrun(longframeTimeNanos){
if(token==FRAME_CALLBACK_TOKEN){
((FrameCallback)action).doFrame(frameTimeNanos);
}el{
((Runnable)action).run();
}
}
}
回想我们在ViewRootImpl中调⽤postCallback()⽅法的三个参数值,第⼀个事件类型为CK_TRAVERSAL,表
⽰是绘制事件,⽤于指⽰该事件放⼊对应队列,第⼆个则是⼀个TraversalRunnable类型的Runnable,则赋值给了这⾥的action,第三个是
null,所以上⾯代码的run()⽅法,实际执⾏到了TraversalRunnable的run()⽅法。
finalclassTraversalRunnableimplementsRunnable{
@Override
publicvoidrun(){
doTraversal();
}
}
该⽅法则调⽤到了doTraversal()⽅法,后续则调⽤到了mTraversals()⽅法。由于run在了UI线程,所以后续到绘制
动作也是在UI线程执⾏到。⾄此完成了Choroegrapher类的分析。
本文发布于:2022-11-22 17:30:24,感谢您对本站的认可!
本文链接:http://www.wtabcd.cn/fanwen/fan/90/531.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
留言与评论(共有 0 条评论) |