写点什么

Activity 页面的绘制流程,移动端跨平台开发

用户头像
Android架构
关注
发布于: 10 小时前

//WindowManagerImpl 的 createLocalWindowManager 创建的是一个 WindowManagerImpl 对象


public WindowManagerImpl createLocalWindowManager(Window parentWindow) {


return new WindowManagerImpl(mContext, parentWindow);


}


根据以上源码我们知道了注释 2 的 vm 其实就是 WindowManagerImpl 对象。注释 1 注释 2 这两个前菜已经给大家分析完了。下面我们进入主题 Activity 页面的绘制。


- (2)WindowManagerImpl.addView


通过前面的分析,我们可以知道注释 3 处其实就是调用了 WindowManagerImpl 的 addView 方法。下面我们来看看这个方法到底做了什么。


@Override


public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {


applyDefaultToken(params);


//调用 WindowManagerGlobal 的 addView 方法


mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);


}


//WindowManagerGlobal 的 addView


public void addView(View view, ViewGroup.LayoutParams params,


Display display, Window parentWindow) {


......


......


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<Choreographer> sThreadInstance =


new ThreadLocal<Choreographer>() {


@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 t


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


hat 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);


...


...

用户头像

Android架构

关注

还未添加个人签名 2021.10.31 加入

还未添加个人简介

评论

发布
暂无评论
Activity页面的绘制流程,移动端跨平台开发