美文网首页Android开发经验谈Android技术知识
从源码的角度分析View的绘制流程

从源码的角度分析View的绘制流程

作者: Joker_Wan | 来源:发表于2019-12-09 12:55 被阅读0次

从源码的角度分析View的绘制流程

温馨提示:以下源码分析会截取源码进行说明,部分方法源码较长会只保留关键代码,其他代码用...省略

为了便于大家更好的理解源码调用逻辑,在看源码之前我们先来看下源码调用流程图


image.png

View的绘制入口是ActivityThread#handleResumeActivity

final void handleResumeActivity(IBinder token,
            boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
            ...
            
            // TODO Push resumeArgs into the activity for consideration
            // 回调Activity的onResume()
            r = performResumeActivity(token, clearHide, reason);
            
            ...
            
            if (r.window == null && !a.mFinished && willBeVisible) {
            r.window = r.activity.getWindow();
            View decor = r.window.getDecorView();
            decor.setVisibility(View.INVISIBLE);
            ViewManager wm = a.getWindowManager();
            
            // 初始化窗口布局属性对象
            WindowManager.LayoutParams l = r.window.getAttributes();
            a.mDecor = decor;
            l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
            l.softInputMode |= forwardBit;
            if (r.mPreserveWindow) {
                a.mWindowAdded = true;
                r.mPreserveWindow = false;
        
                ViewRootImpl impl = decor.getViewRootImpl();
                if (impl != null) {
                    impl.notifyChildRebuilt();
                }
            }
            if (a.mVisibleFromClient) {
                if (!a.mWindowAdded) {
                    a.mWindowAdded = true;
                    // 注释1
                    wm.addView(decor, l);
                } else {
                    a.onWindowAttributesChanged(l);
                }
            }

        
        } else if (!willBeVisible) {
            if (localLOGV) Slog.v(TAG, "Launch " + r + " mStartedActivity set");
            r.hideForNow = true;
        }
             
}

可以看到其中又调用了performResumeActivity,此方法会回调Activity的onResume(),注释1处调用了wm.addView(decor, l)传入DecorView和窗口的布局参数,此处的wm是一个接口ViewManager,是通过a.getWindowManager()赋值的,通过这个方法可知wm是ViewManager的实现类WindowManager,此处的a是Activity对象,跟进Activity#getWindowManager

public WindowManager getWindowManager() {
        return mWindowManager;
}

直接返回一个WindowManager对象,继续查找该对象在哪里赋值的

 final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config, String referrer, IVoiceInteractor voiceInteractor,
            Window window, ActivityConfigCallback activityConfigCallback) {
            
            ...
            
             mWindowManager = mWindow.getWindowManager();
            
            ...
}

可以看到是在Activity的attach()中赋值,继续跟进Window#getWindowManager发现,依旧是返回一个mWindowManager,找到mWindowManager赋值处

public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
            boolean hardwareAccelerated) {
        mAppToken = appToken;
        mAppName = appName;
        mHardwareAccelerated = hardwareAccelerated
                || SystemProperties.getBoolean(PROPERTY_HARDWARE_UI, false);
        if (wm == null) {
            wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
        }
        mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
    }

可以看到,调用WindowManagerImpl的createLocalWindowManager来初始化mWindowManager,继续跟进WindowManagerImpl#createLocalWindowManager

public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
        return new WindowManagerImpl(mContext, parentWindow);
}

可以看到返回值是WindowManagerImpl,那么之前分析的在handleResumeActivity()中调用了vm.addView中的vm是WindowManagerImpl的对象,接着找到WindowManagerImpl#addView

public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}

可以看到调用了mGlobal的addView方法,mGlobal是WindowManagerGlobal对象,继续跟进WindowManagerGlobal#addView

public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
    
            ...


            ViewRootImpl root;
            View panelParentView = null;

            ...
           
            if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
                    wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
                final int count = mViews.size();
                for (int i = 0; i < count; i++) {
                    if (mRoots.get(i).mWindow.asBinder() == wparams.token) {
                        panelParentView = mViews.get(i);
                    }
                }
            }

            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
                root.setView(view, wparams, panelParentView);
            } catch (RuntimeException e) {
                // BadTokenException or InvalidDisplayException, clean up.
                if (index >= 0) {
                    removeViewLocked(index, true);
                }
                throw e;
            }
        }
    }

这里创建了一个ViewRootImpl的对象root,并调用ViewRootImpl#setView,将DecorView传入进去,继续跟进ViewRootImpl#setView发现里面调用了requestLayout()

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

可以看到里面调用了一个checkThread()方法,主要用来检查当前的绘制过程是否是在主线程执行

    void checkThread() {
        if (mThread != Thread.currentThread()) {
            throw new CalledFromWrongThreadException(
                    "Only the original thread that created a view hierarchy can touch its views.");
        }
    }

继续看requestLayout代码,接着又调用了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方法,里面传入了一个mTraversalRunnable,跟进这个mTraversalRunnable

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

最后会调用TraversalRunnable里面的run(),run()里面调用了doTraversal()

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

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

            performTraversals();

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

doTraversal里面接着调用了 performTraversals方法,performTraversals源码太长,就不贴出来了,里面调用了绘制的三大步骤:performMeasure、performLayout、performDraw,本篇文档先分析到此,后续会继续分析performMeasure、performLayout、performDraw具体源码

总结下源码调用逻辑
ActivityThread.H.handleMessage(处理Activity生命周期)
-> ActivityThread#handleLaunchActivity()
-> ActivityThread#performLaunchActivity()
-> ActivityThread#handleResumeActivity()
-> ActivityThread#performResumeActivity() (回调Activity的生命周期onResume())
-> WindowManagerImpl#addView(decorView,layoutParams)
-> WindowManagerGlobal#addView:
root = new ViewRootImpl()
root.setView()
-> ViewRootImpl#requestLayout() (会检查当前线程是否是主线程checkThread)
-> ViewRootImpl#scheduleTraversals()
mChoreographer.postCallback(xx,mTraversalRunnable,xx)
-> ViewRootImpl#TraversalRunnable.run()
-> ViewRootImpl#doTraversal()
-> ViewRootImpl#performTraversals()
performMeasure()
performLayout()
performDraw()

相关文章

网友评论

    本文标题:从源码的角度分析View的绘制流程

    本文链接:https://www.haomeiwen.com/subject/kpybgctx.html