Android⾯试题-来说⼀遍View的绘制流程
作为⼀名开发不管你⼏年经验,view的绘制流程熟记于⼼总少不了吧,今天带⼤家⾛⼀遍,也给⾃⼰加深印象。
setContentView是我们⽤来给activity设置我们写的布局界⾯,我们就从这⾥⼊⼿。
Activity#setContentView
1@UnsupportedAppUsage
2    private Window mWindow;
3
4 public Window getWindow() {
5        return mWindow;
6    }
7
制作android软件流程
8public void setContentView(@LayoutRes int layoutResID) {
9        getWindow().setContentView(layoutResID);
10        initWindowDecorActionBar();
11    }
12
可以看到,在activity调⽤了mWindow.setContentView⽅法,⽽window是抽象类,所以我们得它的⼦类PhoneWindow
PhoneWindow#setContentView
1@Override
2    public void setContentView(int layoutResID) {
3        if (mContentParent == null) {
4            installDecor();
5        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
6            veAllViews();
7        }
8
9        if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
10            final Scene newScene = SceneForLayout(mContentParent, layoutResID,
11                    getContext());
12            transitionTo(newScene);
13        } else {
14            mLayoutInflater.inflate(layoutResID, mContentParent);
15        }
16        questApplyInsets();
17        final Callback cb = getCallback();
18        if (cb != null && !isDestroyed()) {
19            cb.onContentChanged();
20        }
21        mContentParentExplicitlySet = true;
22    }
⾸先判断mContentParent是否为空,是会调⽤installDecor()⽅法做些初始化⼯作。然后再通过LayoutInflater将布局⽂件加载到mContentParent上⾯去。
PhoneWindow#installDecor
1private DecorView mDecor;
2private ViewGroup mContentParent;
3private ViewGroup mContentRoot;
4
5private void installDecor() {
6    if (mDecor == null) {
7        mDecor = generateDecor(-1);
8        mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
9        mDecor.setIsRootNamespace(true);
10        if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
11            mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
12        }
13    }
14    if (mContentParent == null) {
15        mContentParent = generateLayout(mDecor);
16        mDecor.makeOptionalFitsSystemWindows();
17        //...
18    }
19}
可以看到mDecor是DecorView类对象,⽽DecorView继承⾃FrameLayout,generateDecor()⽅法就是初始化创建⼀个空的FrameLayout,generateLayout(mDecor) ⽤来初始化mContentParent。
PhoneWindow#generateLayout(mDecor)
1protected ViewGroup generateLayout(DecorView decor) {
2    TypedArray a = getWindowStyle();
3    if (a.getBoolean(R.styleable.Window_windowNoTitle, false)) {
4        requestFeature(FEATURE_NO_TITLE);
5    } else if (a.getBoolean(R.styleable.Window_windowActionBar, false)) {
6        requestFeature(FEATURE_ACTION_BAR);
7    }
8
9    if (a.getBoolean(R.styleable.Window_windowActionBarOverlay, false)) {
10        requestFeature(FEATURE_ACTION_BAR_OVERLAY);
11    }
12    //...
13    if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
14        layoutResource = R.layout.screen_swipe_dismiss;
15    } else if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {
16        if (mIsFloating) {
17            TypedValue res = new TypedValue();
18            getContext().getTheme().resolveAttribute(
19                    R.attr.dialogTitleIconsDecorLayout, res, true);
20            layoutResource = sourceId;
21        } else {
22            layoutResource = R.layout.screen_title_icons;
23        }
24        removeFeature(FEATURE_ACTION_BAR);
25    } else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0
26            && (features & (1 << FEATURE_ACTION_BAR)) == 0) {
27        layoutResource = R.layout.screen_progress;
28
29    } else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) {}
30    //...
31    int layoutResource;
32    //...
33    View in = mLayoutInflater.inflate(layoutResource, null);
34    decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
35
36    mContentRoot = (ViewGroup) in;
37
38    ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
39    //...
40    return contentParent;
41 }
可以看到,generateLayout()⽅法会根据我们Acivity主题样式,选择加载不同的系统布局资源,并将该视图添加到DecorView中去。
到这⾥只是将布局⽂件绑定到Activity  -->  mWindow -->  DecorView  -->  mContentParent 上,此时界⾯还未开始绘制。这时我们需要去到关键类ActivityThread类当中了。
ActivityThread#handleResumeActivity
1@Override
2    public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
3            String reason) {
4        //...
5        if (r.window == null && !a.mFinished && willBeVisible) {
6            r.window = Window();
7            View decor = DecorView();
8            decor.setVisibility(View.INVISIBLE);
9            ViewManager wm = a.getWindowManager();
10            WindowManager.LayoutParams l = Attributes();
11              a.mDecor = decor;
12            l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
13            l.softInputMode |= forwardBit;
14            if (r.mPreserveWindow) {
15                  a.mWindowAdded = true;
16                r.mPreserveWindow = false;
17                ViewRootImpl impl = ViewRootImpl();
18                if (impl != null) {
19                    ifyChildRebuilt();
20                }
21            }
22            //...
23            if (r.activity.mVisibleFromClient) {
24                r.activity.makeVisible();
25            }
26        //...
27    }
可以看到调⽤了 WindowManager.addView()⽅法,⽽WindowManager是⼀个接⼝,也就是调⽤WindowManagerGlobl.addView()⽅法。
WindowManagerGlobl#addView():
1public void addView(View view, ViewGroup.LayoutParams params,
2            Display display, Window parentWindow) {
3      //...
4            root = new Context(), display);
5
6            view.setLayoutParams(wparams);
7
8            mViews.add(view);
9            mRoots.add(root);
10            mParams.add(wparams);
11        }
12
13        // do this last because it fires off messages to start doing things
14        try {
15            root.setView(view, wparams, panelParentView);
16        } catch (RuntimeException e) {
17            // BadTokenException or InvalidDisplayException, clean up.
18            synchronized (mLock) {
19                final int index = findViewLocked(view, false);
20                if (index >= 0) {
21                    removeViewLocked(index, true);
22                }
23            }
24            throw e;
25        }
26    }
该⽅法中实例化了 ViewRootImpl,并且调⽤了ViewRootImpl.setView()⽅法来持有当前的DecorView。
ViewRootImpl#setView()
1
2public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
3        synchronized (this) {
4            if (mView == null) {
5                //..
6                requestLayout();
7                //...
8            }
9        }
10}
11
12 @Override
13    public void requestLayout() {
14        if (!mHandlingLayoutInLayoutRequest) {
15            checkThread();
16            mLayoutRequested = true;
17            scheduleTraversals();
18        }
19    }
20
21    //校验所在线程,mThread是在ViewRootImpl初始化的时候执⾏mThread = Thread.currentThread()进⾏赋值的,也就是初始化ViewRootImpl所在的线程。
22    void checkThread() {
23        if (mThread != Thread.currentThread()) {
24            throw new CalledFromWrongThreadException(
25                    "Only the original thread that created a view hierarchy can touch its views.");
26        }
27    }
28
29    void scheduleTraversals() {
30        if (!mTraversalScheduled) {
31            mTraversalScheduled = true;
32            mTraversalBarrier = Looper().postSyncBarrier();
33            mChoreographer.postCallback(
34                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
35            if (!mUnbufferedInputDispatch) {
36                scheduleConsumeBatchedInput();
37            }
38            notifyRendererOfFramePending();
39        }
40    }
41
42    final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
43
44    final class TraversalRunnable implements Runnable {
45        @Override
46        public void run() {
47            doTraversal();
48        }
49    }
50
51    //做任务
52    void doTraversal() {
53        if (mTraversalScheduled) {
54            mTraversalScheduled = false;
55            Looper().removeSyncBarrier(mTraversalBarrier);
56
57            if (mProfile) {
58                Debug.startMethodTracing("ViewAncestor");
59            }
60
61            aceBegin(Trace.TRACE_TAG_VIEW, "performTraversals");

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。