从 View 绘制流程手把手教你看 Android 源码

从 View 绘制流程手把手教你看 Android 源码

  • 基于 android 29
  • 工具 Android Studio

注意:本篇文章的目的有俩!!

1.带你看懂 Android 的 View 显示到屏幕上的要经过哪些流程

2.带你用会 AS 看源码,告别源码不会看和跟踪不下去的苦恼!

工具咋用

  • 方法调用追踪:

    ctrl + 点击鼠标左键 || 点击鼠标滚轮 || ctrl + b

  • 文件内查找:

    ctrl + f

  • 搜索源码:

    双击 shift 并勾选 Include non-project items

流程详解

首先我们都知道 View 的绘制是从 Activity 的 onResume 后开始的,那么根据 AS 的调用追踪功能我们找到了onResume 调用的方法是 Instrumentation 类中的 callActivityOnResume 方法。

那么 callActivityOnResume 又是谁调用的呢?根据继续查找调用关系得出其就是在 Activity 中的 performResume 中被调用的。然而这些调用方法里居然都没有关于 View 绘制相关的代码,这就说明其调用时机是在某个地方调用 performResume 后调用的,而不是在 performResume 里面调用的,因此我们需要继续追踪。然后追踪是发现这里居然跟不下去。所以在这之前我们需要了解 Activity 的生命周期都是在 ActivityThread 类中通过事件驱动的,因此这里我们需要去 ActivityThread 类中搜索。

果然发现其就在 ActivityThread 的 performResumeActivity 中被调用了,然而我们发现这个方法里还是没有 View 绘制的相关代码,没事继续找!最终终于在 ActivityThread 的 handleResumeActivity 中我们找到了这样一行代码。

从 addView 这个方法的注释上便能发现就是这句话将视图(View)添加到了窗口(Window)上。但是这里的 ViewManager 是个接口,所以接下来我们便要找其具体的实现类。我们跟踪 a.getWindowManager 后发现其返回了 Activity 中的 WindowManager,继续跟踪发现其返回了 Window 中的 WindowManager,再跟踪下去则发现其是由 WindowManagerImpl 中的 createLocalWindowManager 方法创建的,其返回类型就是 WindowManagerImpl,也就是说这里的 addView 方法就是调用的 WindowManagerImpl 中的 addView 方法,而这个方法又调用了 WindowManagerGlobal 中的 addView 方法,因此最终我们来看这个方法中的关键代码。

这里调用了 ViewRootImpl 中的 setView 方法,在这里我们看到了一个熟悉的方法 requestLayout。

@Override
public void requestLayout() {
    if (!mHandlingLayoutInLayoutRequest) {
        checkThread();
        mLayoutRequested = true;
        scheduleTraversals();
    }
}

在这里我们发现了该方法首先检查了线程是否是 UI 线程,再执行了一个 scheduleTraversals 方法,因此我们再来看这个方法。

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

这里的关键代码就是这个 mChoreographer.postCallback 方法啦,这里的 mChoreographer 其实就是 Choreographer 类,在了解这句代码具体做了什么之前有必要先了解一下这个类是什么,我们先来看一下这个类的注释。

Coordinates the timing of animations, input and drawing.
The choreographer receives timing pulses (such as vertical synchronization) from the display subsystem then schedules work to occur as part of rendering the next display frame.
Applications typically interact with the choreographer indirectly using higher level abstractions in the animation framework or the view hierarchy. 

简单的翻译就是这个类主要用于协调动画、输入和绘图的时间,他可以接收系统发出的定时信号比如垂直同步(VSync)信号,然后安排什么时候去显示下一个帧。在简单的知道这个类的用处后,回到刚才的方法我们在 Choreographer 中找到了 postCallback 的实现。

public void postCallback(int callbackType, Runnable action, Object token) {
    postCallbackDelayed(callbackType, action, token, 0);
}

继续跟踪后便能发现其真正实现是这个方法。

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;
        mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);

        if (dueTime <= now) {
            scheduleFrameLocked(now);
        } else {
            Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
            msg.arg1 = callbackType;
            msg.setAsynchronous(true);
            mHandler.sendMessageAtTime(msg, dueTime);
        }
    }
}

看到这里不难看出 mChoreographer.postCallback( Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null) 这句代码的其实就是发送了一个 0 延迟的消息到 Choreographer 中的 Handler 中,因此我们需要看的是 mTraversalRunnable 这个 Runnable 具体做了什么。

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

可以看到其就做了一件事,就是调用了 ViewRootImpl 中的 doTraversal 方法。

void doTraversal() {
    if (mTraversalScheduled) {
        mTraversalScheduled = false;
        mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);

        if (mProfile) {
            Debug.startMethodTracing("ViewAncestor");
        }

        performTraversals();

        if (mProfile) {
            Debug.stopMethodTracing();
            mProfile = false;
        }
    }
}

这个方法里主要又去调用了 performTraversals 方法,追踪进去后终于发现了 View 绘制最终的三大方法!!

在这个方法里分别依次调用了 performMeasure,performLayout,performDraw。

至此 View 绘制基本流程就结束啦,接下来的的三大详细绘制步骤可以看这篇哦!
View 是如何显示到屏幕上的

流程总结

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Y-S-J

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值