canvas教程

用两张图告诉你,为什么你的App会卡顿? 微码农(4)

字号+ 作者:H5之家 来源:H5之家 2017-04-18 18:00 我要评论( )

ViewRootImpl.java //这个方法只有在ViewRootImpl初始化时才会被调用private void profileRendering(boolean enabled) {...mRenderProfiler = new Choreographer.FrameCallback() {@Overridepublic void doFrame(lo

ViewRootImpl.java

//这个方法只有在ViewRootImpl初始化时才会被调用 private void profileRendering(boolean enabled) { ... mRenderProfiler = new Choreographer.FrameCallback() { @Override public void doFrame(long frameTimeNanos) { ... scheduleTraversals(); //请求一个Vsync信号,后面还会提到这个方法 mChoreographer.postFrameCallback(mRenderProfiler); //每次回调时,重新将FrameCallback post到Choreographer中 ... } }; ... mChoreographer.postFrameCallback(mRenderProfiler); //将FrameCallback post到Choreographer中 ... }

上面代码出现了一个重要方法scheduleTraversals()。下面我们看看它究竟为何重要。
ViewRootImpl.java

void scheduleTraversals() { ... mChoreographer.postCallback( Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); //向Choreographer中post一个TraversalRunnable //这又是一个十分重要的对象 ... }

可以看出scheduleTraversals()每次调用时会向Choreographer中post一个TraversalRunnable,它会促使Choreographer去请求一个Vsync信号。所以这个方法的作用就是用来请求一次Vsync信号刷新界面的。事实上,你可以看到,在invalidate()、requestLayout()等操作中,都能够看到它被调用。原因就是这些操作需要刷新界面,所以需要请求一个Vsync信号来出发新界面的绘制。

ViewRootImpl.java

final class TraversalRunnable implements Runnable { @Override public void run() { doTraversal(); //开始遍历视图树,这意味着开始绘制一帧内容了 } }

从图中可以看到,每当doTraversal()被调用时,一系列的测量、布局和绘制操作就开始了。在绘制时,会通过Surface来获取一个Canvas内存块交给DecorView,用于视图的绘制。整个View视图的内容都是被绘制到这个Canvas中。

Choreographer中的风起云涌

前面反复提到向Choreographer中post回调,那么post过去发生了些什么呢?从图中可以看到,所有的post操作最终都进入到postCallbackDelayedInternal()中。

用两张图告诉你,为什么你的App会卡顿?

image

Choreographer.java

private void postCallbackDelayedInternal(int callbackType, Object action, Object token, long delayMillis) { ... synchronized (mLock) { final long now = SystemClock.uptimeMillis(); final long dueTime = now + delayMillis; mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token); //将Callback添加到CallbackQueue[]中 if (dueTime <= now) { scheduleFrameLocked(now); //如果回调时间到了,请求一个Vsync信号 //在接收到后会调用doFrame()回调这个Callback。 } else { Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action); msg.arg1 = callbackType; msg.setAsynchronous(true); //异步消息,避免被拦截器拦截 mHandler.sendMessageAtTime(msg, dueTime); //如果还没到回调的时间,向FrameHandelr中发送 //MSG_DO_SCHEDULE_CALLBACK消息 } } ... }

上面这段代码会把post到Choreographer中的Callback添加到Callback[]中,并且当它因该被回调时,请求一个Vsync信号,在接收到下一个Vsync信号时回调这个Callback。如果没有到回调的时间,则向FrameHandler中发送一个MSG_DO_SCHEDULE_CALLBACK消息,但最终还是会请求一个Vsync信号,然后回调这个Callback。

简单提一下CallbackQueue:简单说一下CallbackQueue。它和MessageQueue差不多,都是单链表结构。在我的这篇【惊天秘密!从Thread开始,揭露Android线程通讯的诡计和主线程的阴谋】文章中,你能够看到更多关于MessageQueue和Handler机制的内容。不同的是它同时还是一个一维数组,下标表示Callback类型。事实上,算上每种类型的单链表结构,它更像是二维数组的样子。简单点描述,假设有一个MessageQueue[]数组,里面存了几个MessageQueue。来看看它的创建你可能就明白,它是在Choreographer初始化时创建的。

private Choreographer(Looper looper) { mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1]; //CALLBACK_LAST值为3。 for (int i = 0; i <= CALLBACK_LAST; i++) { mCallbackQueues[i] = new CallbackQueue(); } }

现在来看看前面代码中调用的scheduleFrameLocked()是如何请求一个Vsync信号的。

private void scheduleFrameLocked(long now) { ... //先判断当前是不是在UI线程 if (isRunningOnLooperThreadLocked()) { scheduleVsyncLocked(); //是UI线程就请求一个Vsync信号 } else { Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC); msg.setAsynchronous(true); mHandler.sendMessageAtFrontOfQueue(msg); //不在UI线程向FrameHandler发送一个MSG_DO_SCHEDULE_VSYNC消息 //来请求一个Vsync信号 } } private void scheduleVsyncLocked() { mDisplayEventReceiver.scheduleVsync(); //通过DisplayEventReceiver请求一个Vsync信号 //这是个恨角色,待会儿会聊聊它。 //MSG_DO_SCHEDULE_VSYNC消息也是通过调用这个方法请求Vsync信号的。 }

上面我们提到过,Choreographer在一个线程中只有一个。所以,如果在其它线程,需要通过Handler来切换到UI线程,然后再请求Vsync信号。

下面看看刚刚出场的mDisplayEventReceiver是个什么鬼?

用两张图告诉你,为什么你的App会卡顿?

 

1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

相关文章
  • 为什么要学习HTML5

    为什么要学习HTML5

    2017-03-26 08:00

  • 为什么使用html5的十大原因!!!

    为什么使用html5的十大原因!!!

    2017-03-19 12:00

  • 用Canvas画图时为什么会闪烁,(只画一条线)

    用Canvas画图时为什么会闪烁,(只画一条线)

    2017-03-09 09:04

  • 为什么要用画图工具来画原型?

    为什么要用画图工具来画原型?

    2017-02-12 14:00

网友点评