Activity页面的绘制流程,移动app开发公司

ViewRootImpl root;

View panelParentView = null;

1.创建ViewRootImpl对象

root = new ViewRootImpl(view.getContext(), display);

view.setLayoutParams(wparams);

mViews.add(view);

mRoots.add(root);

mParams.add(wparams);

// do this last because it fires off messages to start doing things

try {

2.调用ViewRootImpl的setView方法

root.setView(view, wparams, panelParentView);

} catch (RuntimeException e) {

// BadTokenException or InvalidDisplayException, clean up.

if (index >= 0) {

removeViewLocked(index, true);

}

throw e;

}

}

}

注释1是个很重要的点,这里创建了一个ViewRootImpl对象。

我们来解析一下这个ViewRootImpl里面需要关注的几个点。

我们先看看ViewRootImpl的构方法:

public ViewRootImpl(Context context, Display display) {

//获取Session对象

mWindowSession = WindowManagerGlobal.getWindowSession();

mDisplay = display;

mBasePackageName = context.getBasePackageName();

//主线程

mThread = Thread.currentThread();

//创建Choreographer对象,这个对象很重要,可以把它理解为一个Handler

//Android 16.6ms刷新一次页面它启到了主要作用

//我们马上看看这个Choreographer是个什么

mChoreographer = Choreographer.getInstance();

mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);

}

ViewRootImpl的构造方法里面我们暂时值关注两件事

1.获取了Session对象这个对象是我们与WindowManagerService交互的桥梁。

2.创建了Choreographer对象。

我们现来看看这个Choreographer对象到底是什么,为什么它那么重要呢?

public static Choreographer getInstance() {

return sThreadInstance.get();

}

// Thread local storage for the choreographer.

private static final ThreadLocal sThreadInstance =

new ThreadLocal() {

@Override

protected Choreographer initialValue() {

//注意了这里其实就是主线程的Looper

//ViewRootImpl对象就是在主线程创建的

Looper looper = Looper.myLooper();

if (looper == null) {

throw new IllegalStateException(“The current thread must have a looper!”);

}

//创建Choreographer对象

Choreographer choreographer = new Choreographer(looper, VSYNC_SOURCE_APP);

if (looper == Looper.getMainLooper()) {

mMainInstance = choreographer;

}

return choreographer;

}

};

private Choreographer(Looper looper, int vsyncSource) {

mLooper = looper;

//前面我们说了正常情况瞎这个looper对象就是主线程的looper对象

//所以通过这个Handler发送的消息都是在主线程处理的

mHandler = new FrameHandler(looper);

//创建FrameDisplayEventReceiver

//这个对象可以理解为一个任务

mDisplayEventReceiver = USE_VSYNC

? new FrameDisplayEventReceiver(looper, vsyncSource)
null;

mLastFrameTimeNanos = Long.MIN_VALUE;

mFrameIntervalNanos = (long)(1000000000 / getRefreshRate());

//可以理解为绘制任务队列

mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1];

for (int i = 0; i <= CALLBACK_LAST; i++) {

mCallbackQueues[i] = new CallbackQueue();

}

// b/68769804: For low FPS experiments.

setFPSDivisor(SystemProperties.getInt(ThreadedRenderer.DEBUG_FPS_DIVISOR, 1));

}

private final class FrameDisplayEventReceiver extends DisplayEventReceiver

implements Runnable {

private boolean mHavePendingVsync;

private long mTimestampNanos;

private int mFrame;

public FrameDisplayEventReceiver(Looper looper) {

super(looper);

}

//这个的onVsync方法是什么时候调用的呢?

//我们知道Android没16.6ms都会刷新一次屏幕,原理其实就是这个Vsync导致的

//这个Vsync信号是从底层传递上来的

//onVsync这个方法也是通过jni从底层调用上来,这个方法不会被java层调用

//每16.6ms调用一次

@Override

public void onVsync(long timestampNanos, int builtInDisplayId, int frame) {

// Ignore vsync from secondary display.

// This can be problematic because the call to scheduleVsync() is a one-shot.

// We need to ensure that we will still receive the vsync from the primary

// display which is the one we really care about. Ideally we should schedule

// vsync for a particular display.

// At this time Surface Flinger won’t send us vsyncs for secondary displays

// but that could change in the future so let’s log a message to help us remember

// that we need to fix this.

mTimestampNanos = timestampNanos;

mFrame = frame;

Message msg = Message.obtain(mHandler, this);

msg.setAsynchronous(true);

//通过Handler把自身作为一个任务发送到主线程的消息队列去做处理

mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);

}

@Override

public void run() {

mHavePendingVsync = false;

//当绘制任务被处理时调用doFrame方法

doFrame(mTimestampNanos, mFrame);

}

}

void doFrame(long frameTimeNanos, int frame) {

if (frameTimeNanos < mLastFrameTimeNanos) {

if (DEBUG_JANK) {

Log.d(TAG, "Frame time appears to be going backwards. May be due to a "

  • “previously skipped frame. Waiting for next vsync.”);

}

scheduleVsyncLocked();

return;

}

//执行这一次队列中的绘制任务

Trace.traceBegin(Trace.TRACE_TAG_VIEW, “Choreographer#doFrame”);

AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS);

mFrameInfo.markInputHandlingStart();

doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);

mFrameInfo.markAnimationsStart();

doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);

mFrameInfo.markPerformTraversalsStart();

doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);

doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos);

}

void doCallbacks(int callbackType, long frameTimeNanos) {

CallbackRecord callbacks;

final long now = System.nanoTime();

//根据callbackType取出mCallbackQueues的任务

callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(

now / TimeUtils.NANOS_PER_MS);

if (callbacks == null) {

return;

}

synchronized (mLock) {

try {

Trace.traceBegin(Trace.TRACE_TAG_VIEW, CALLBACK_TRACE_TITLES[callbackType]);

//for循环执行需要绘制的任务

for (CallbackRecord c = callbacks; c != null; c = c.next) {

if (DEBUG_FRAMES) {

Log.d(TAG, “RunCallback: type=” + callbackType

  • “, action=” + c.action + “, token=” + c.token

  • “, latencyMillis=” + (SystemClock.uptimeMillis() - c.dueTime));

}

c.run(frameTimeNanos);

}

}

}

上面我们简单的讲了ViewRootImpl的创建和Choreographer的创建。我们知道了每16.6ms底层都会通过jni调用成员变了mDisplayEventReceiver的onVsync方法,这个方法里会通过成员变量mHandler(与主线程Looper绑定的Handler),把自己作为一个任务发送到主线程去执行。

最后调用doFrame()方法来处理mCallbackQueues队列中的绘制任务。我们可以猜测最后我们Activity页面的绘制任务也会被添加到这个mCallbackQueues队列中。

下面我们再来看最后也是最重要的一步。

- (3)ViewRootImpl.setView

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {

synchronized (this) {

if (mView == null) {

mView = view;

// Schedule the first layout -before- adding to the window

// manager, to make sure we do the relayout before receiving

// any other events from the system.

//1.请求布局绘制

requestLayout();

if ((mWindowAttributes.inputFeatures

& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {

mInputChannel = new InputChannel();

}

mForceDecorViewVisibility = (mWindowAttributes.privateFlags

& PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY) != 0;

try {

mOrigWindowType = mWindowAttributes.type;

mAttachInfo.mRecomputeGlobalAttributes = true;

collectViewAttributes();

//2.通过构造方法里创建的Session对象向WindowManangerService请求将我们创建好的Surface对象添加到 屏幕上

res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,

getHostVisibility(), mDisplay.getDisplayId(), mWinFrame,

mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,

mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel);

}

}

}

}

由于今天我们讲绘制流程,我们就主要来看看注释1做了写什么事。我们按照方法的调用流程来看看源码。

@Override

public void requestLayout() {

if (!mHandlingLayoutInLayoutRequest) {

//检查是否是主线程要求更新UI

checkThread();

mLayoutRequested = true;

scheduleTraversals();

}

}

void scheduleTraversals() {

if (!mTraversalScheduled) {

mTraversalScheduled = true;

mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();

//调用Choreographer的postCallback方法

//mTraversalRunnable就是一个处理绘制的任务

mChoreographer.postCallback(

Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);

}

}

final class TraversalRunnable implements Runnable {

@Override

public void run() {

doTraversal();

}

}

void doTraversal() {

performTraversals();

}

//performTraversals最后会调用performMeasure,performLayout,performDraw

//来处理界面的测量,布局和绘制流程

private void performTraversals() {

//处理测量流程

performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);

//处理布局流程

performLayout(lp, mWidth, mHeight);

//处理绘制流程

performDraw();

}

//下面我们开始分析Choreographer postCallback流程

public void postCallback(int callbackType, Runnable action, Object token) {

postCallbackDelayed(callbackType, action, token, 0);

}

public void postCallbackDelayed(int callbackType,

Runnable action, Object token, long delayMillis) {

postCallbackDelayedInternal(callbackType, action, token, delayMillis);

}

private void postCallbackDelayedInternal(int callbackType,

Object action, Object token, long delayMillis) {

synchronized (mLock) {

//这里小小的说明一下这里记录的是开机到现在的时间

//不受设置时间的影响

//如果要计算时间间隔最好用 SystemClock.uptimeMillis()

final long now = SystemClock.uptimeMillis();

学习分享

在当下这个信息共享的时代,很多资源都可以在网络上找到,只取决于你愿不愿意找或是找的方法对不对了

很多朋友不是没有资料,大多都是有几十上百个G,但是杂乱无章,不知道怎么看从哪看起,甚至是看后就忘

如果大家觉得自己在网上找的资料非常杂乱、不成体系的话,我也分享一套给大家,比较系统,我平常自己也会经常研读。

2021最新上万页的大厂面试真题

Activity页面的绘制流程,移动app开发公司_第1张图片

七大模块学习资料:如NDK模块开发、Android框架体系架构…

Activity页面的绘制流程,移动app开发公司_第2张图片

只有系统,有方向的学习,才能在段时间内迅速提高自己的技术。

这份体系学习笔记,适应人群:
**第一,**学习知识比较碎片化,没有合理的学习路线与进阶方向。
**第二,**开发几年,不知道如何进阶更进一步,比较迷茫。
**第三,**到了合适的年纪,后续不知道该如何发展,转型管理,还是加强技术研究。如果你有需要,我这里恰好有为什么,不来领取!说不定能改变你现在的状态呢!

由于文章内容比较多,篇幅不允许,部分未展示内容以截图方式展示 。如有需要获取完整的资料文档的朋友点击我的【GitHub】免费获取。

资料,大多都是有几十上百个G,但是杂乱无章,不知道怎么看从哪看起,甚至是看后就忘

如果大家觉得自己在网上找的资料非常杂乱、不成体系的话,我也分享一套给大家,比较系统,我平常自己也会经常研读。

2021最新上万页的大厂面试真题

[外链图片转存中…(img-4S08yINo-1646218745071)]

七大模块学习资料:如NDK模块开发、Android框架体系架构…

[外链图片转存中…(img-MMcod8Qi-1646218745073)]

只有系统,有方向的学习,才能在段时间内迅速提高自己的技术。

这份体系学习笔记,适应人群:
**第一,**学习知识比较碎片化,没有合理的学习路线与进阶方向。
**第二,**开发几年,不知道如何进阶更进一步,比较迷茫。
**第三,**到了合适的年纪,后续不知道该如何发展,转型管理,还是加强技术研究。如果你有需要,我这里恰好有为什么,不来领取!说不定能改变你现在的状态呢!

由于文章内容比较多,篇幅不允许,部分未展示内容以截图方式展示 。如有需要获取完整的资料文档的朋友点击我的【GitHub】免费获取。

你可能感兴趣的:(程序员,面试,移动开发,android)