栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Android启动优化、布局优化必经之路—如何精准获取页面绘制时间

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Android启动优化、布局优化必经之路—如何精准获取页面绘制时间

看一下 view.post() 方法的源码

public boolean post(Runnable action) {
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
return attachInfo.mHandler.post(action);
}

// Postpone the runnable until we know on which thread it needs to run.
// Assume that the runnable will be successfully placed after attach.
getRunQueue().post(action);
return true;
}

这里会首先判断 attachInfo 是否为空,不为空的话,会直接调用 handler.post() 方法。也就是说,如果 attachInfo 对象不为空,view.post() 和 new Handler().post() 的效果是相同的。

反之,如果 attachInfo 为空,就会调用 mRunQueue 对象的 post() 方法

public void postDelayed(Runnable action, long delayMillis) {
final HandlerAction handlerAction = new HandlerAction(action, delayMillis);

synchronized (this) {
if (mActions == null) {
mActions = new HandlerAction[4];
}
mActions = GrowingArrayUtils.append(mActions, mCount, handlerAction);
mCount++;
}
}

查看该方法的源码,会发现它并没有将任务直接发送,而是创建了一个 HandlerAction 数组保存了起来。也就是说,如果 attachInfo 对象为空,就将任务暂时保存到数组中,在后续的某一个时刻,再进行发送。

ViewRootImpl.java

private void performTraversals() {

// host即DecorView
host.dispatchAttachedToWindow(mAttachInfo, 0);

performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);

performLayout(lp, mWidth, mHeight);

performDraw();

}

View.java

void dispatchAttachedToWindow(AttachInfo info, int visibility) {
mAttachInfo = info;

// Transfer all pending runnables.
if (mRunQueue != null) {
mRunQueue.executeActions(info.mHandler);
mRunQueue = null;
}

}

可以看到,在 dispatchAttachedToWindow 方法里,通过执行 executeActions 将之前保存的任务全部发送。

这里可能会有人有疑问,dispatchAttachedToWindow 方法是在 performMeasure 等绘制操作之前进行的,也就是 view.post() 中的任务是在绘制之前发送的,为什么它还能获取到 view 的真实宽高呢?

这就涉及到 Android 的消息机制了,整个 Android 体系都是由消息来驱动的,我们这里只涉及到主线程,所以我们通过 view.post(), new Handler().post() 等方式发送的任务,都被添加到了主线程到消息队列中,等待执行,而 performTraversals() 方法也是在另一个任务中执行的,源码如下:

ViewRootImpl.java
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}

final TraversalRunnable mTraversalRunnable = new TraversalRunnable();

void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfframePending();
pokeDrawLockIfNeeded();
}
}

所以,executeActions() 方法发送的任务,只是将其添加到主线程到任务队列中,只有当 performTraversals() 方法所在的任务执行完毕后,才会执行队列中的其他任务。

回到最初的问题,我们在 onResume() 方法中直接执行 view.post() 方法的时候,attachInfo 对象此时为空,具体原理请参考 【Android源码解析】View.post()到底干了啥。所以 view.post() 中的任务会被暂时存放到数组中,在开始绘制之前被发送到主线程的消息队列中,绘制完成后会被执行。但是被缓存的任务一定不止我们添加的这一个,还有一些其他的系统任务,所以我们还要通过 new Handler().post() 在主线程的消息队列尾部重新添加一个任务,用来作为绘制结束的标记,是相对

《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》

浏览器打开:qq.cn.hn/FTe 免费领取

准确的。

作者:huangbo
链接:https://juejin.im/post/6882326732511608840

文末

【360°全方位性能调优】
性能优化相关所有知识点已经为你们整理成了PDF,从理论着手,利用大量实战案例带你全方位掌握APP性能优化的各个知识点和思路,需要的小伙伴**点赞+关注后简信【1】**即可无偿获取或直接在我的GitHub直接下载~

最后,祝愿大家在这并不友好的环境下都能找到自己心仪的归宿!

*点赞+关注后简信【1】**即可无偿获取或直接在我的GitHub直接下载~

[外链图片转存中…(img-B0lWrQeh-1636298897118)]

最后,祝愿大家在这并不友好的环境下都能找到自己心仪的归宿!

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/439885.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号