invalidate

更新时间:2022-11-22 17:30:24 阅读: 评论:0


2022年11月22日发(作者:钱的英语是)

源码分析篇-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()代码。

privatestaticfinalThreadLocalsThreadInstance=

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小时内删除。

上一篇:indirect
下一篇:vicious
标签:invalidate
相关文章
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图