UI绘制流程

156 阅读2分钟

从系统角度理解Android的界面绘制

任何一个操作系统要实现界面绘制,都需要处理好应用层、系统层和硬件层的分工协作:

应用层负责定义画面的内容

系统层负责综合整个屏幕的画面并保证流畅

硬件层负责把数据输出到显示设备上

image.png

1、view是如何被添加到屏幕上

1、创建顶层布局容器DecorView

2、在顶层布局中加载基础布局ViewGroup

3、将ContentView添加到基础布局中的FramLayout中

Activity

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.add_item_confirmation_activity);
}
public void setContentView(@LayoutRes int layoutResID) {
    getWindow().setContentView(layoutResID);
    initWindowDecorActionBar();
}
PhoneWindow

@Override
public void setContentView(int layoutResID) {
    if (mContentParent == null) {
        installDecor(); // 1
    } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
         .removeAllViews();
    }

    if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
        final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
                getContext());
        transitionTo(newScene);
    } else {
        mLayoutInflater.inflate(layoutResID, mContentParent);// 2
    }
}
private void installDecor() {
    mForceDecorInstall = false;
    if (mDecor == null) {
            mDecor = generateDecor(-1);// 1 生成一个 new DecorView
            ...
    } else {
            mDecor.setWindow(this);
    }
    if (mContentParent == null) {
            mContentParent = generateLayout(mDecor);// 2   R.id.content
            ...
    }
}
protected ViewGroup generateLayout(DecorView decor) {
   ...
    if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) {
        layoutResource = R.layout.screen_simple_overlay_action_mode;
    } else {
        // Embedded, so no decoration is needed.
        layoutResource = R.layout.screen_simple; // 1 以此为例
        // System.out.println("Simple!");
    }

    mDecor.startChanging();
    mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);

    // 2 com.android.internal.R.id.content
    ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT); 
    if (contentParent == null) {
        throw new RuntimeException("Window couldn't find content container view");
    }

    mDecor.finishChanging();
    return contentParent;
}

\Android\sdk\platforms\android-30\data\res\layout

image.png

2、view的绘制流程

1、绘制入口 ActivityThread.handleResumeActivity -->WindowManagerImpl.addView(decorView,layoutParams) -->WindowManagerGlobal.addView()

2、绘制的类及方法 ViewRootImpl.setView(decorView,layoutParams,parentView) -->ViewRootImpl.requestLayout-->scheduleTraversals -->doTraversal-->performTraversals

3、绘制三大步骤 测量:ViewRootImpl.performMeasure 布局:ViewRootImpl.performLayout 绘制:ViewRootImpl.performDraw

@Override
public void handleResumeActivity(IBinder token, 
        boolean finalStateRequest, boolean isForward,String reason) {

        // 1
        final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason); 
        if (r == null) {
            // We didn't actually resume the activity, so skipping any follow-up actions.
            return;
        }
        ...
   
        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();// 2
            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;
                    wm.addView(decor, l);
                } else {
                    // The activity will get a callback for this {@link LayoutParams} change
                    // earlier. However, at that time the decor will not be set (this is set
                    // in this method), so no action will be taken. This call ensures the
                    // callback occurs with the decor set.
                    a.onWindowAttributesChanged(l);
                }
            }
        }

        r.nextIdle = mNewActivities;
        mNewActivities = r;
        if (localLOGV) Slog.v(TAG, "Scheduling idle handler for " + r);
        Looper.myQueue().addIdleHandler(new Idler());
    }
Activity
mWindowManager = mWindow.getWindowManager();
Window
mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
WindowManagerImpl
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
    applyDefaultToken(params);
    mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow,
            mContext.getUserId());
}

public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow, int userId) {
        ...
		
        ViewRootImpl root;
        View panelParentView = null;

        synchronized (mLock) {
        
            root = new ViewRootImpl(view.getContext(), display); //1
            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 {
                root.setView(view, wparams, panelParentView, userId); //2
            } catch (RuntimeException e) {
                // BadTokenException or InvalidDisplayException, clean up.
                if (index >= 0) {
                    removeViewLocked(index, true);
                }
                throw e;
            }
        }
    }
ViewRootImpl
public void setView(View view, WindowManager.LayoutParams attrs, 
                                    View panelParentView,int userId) {
        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.
                requestLayout(); //1

                ...
            }
        }
}
@Override
public void requestLayout() {
    if (!mHandlingLayoutInLayoutRequest) {
        checkThread();
        mLayoutRequested = true;
        scheduleTraversals();
    }
}
void checkThread() {
    if (mThread != Thread.currentThread()) {
        throw new CalledFromWrongThreadException(
                "Only the original thread that created a view hierarchy can touch its views.");
    }
}
@UnsupportedAppUsage
void scheduleTraversals() {
    if (!mTraversalScheduled) {
        mTraversalScheduled = true;
        mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
        mChoreographer.postCallback(
                Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); //1
        notifyRendererOfFramePending();
        pokeDrawLockIfNeeded();
    }
}
final class TraversalRunnable implements Runnable {
    @Override
    public void run() {
        doTraversal();
    }
}
void doTraversal() {
    if (mTraversalScheduled) {
        mTraversalScheduled = false;
        mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier); //1

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

        performTraversals(); //2

        if (mProfile) {
            Debug.stopMethodTracing();
            mProfile = false;
        }
    }
}
private void performTraversals() {
	...
	// Ask host how big it wants to be
        performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
	...
	performLayout(lp, mWidth, mHeight);
	...
	performDraw();

}

3、setContentView(R.layout.xxx)

juejin.cn/editor/draf…