相关问题
Activity 的显示原理 (Window、DecorView、ViewRoot)
Activity 的 UI 刷新机制(Vsync、Choreographer)
UI 的绘制原理(Measure、Layout、Draw)
Surface 原理(Surface、SurfaceFlinger)
- Activity 的显示原理 (Window、DecorView、ViewRoot)
- setContentView() 原理是什么?
- Activity 在 onResume() 之后才会显示的原因是什么?
- ViewRoot 是干嘛的,是 View Tree 的 rootView 么?
setContentView 它的原理是什么?
MainActivity继承Activity
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
点击查看setContentView跳转到Activity源码
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
getWindow();就是window对象 mWindow 就是Window
private Window mWindow;
public Window getWindow() {
return mWindow;
}
说明getWindow().setContentView(layoutResID);调用的是window对象的setContentView对象,接下来去Window唯一实现类PhoneWindow对象查看
ViewGroup mContentParent;
@Override
public void setContentView(int layoutResID) {
//mContentParent 就是viewGroup
if (mContentParent == null) {
//当mContentParent 是null 然后看看installDecor方法做了什么
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
}
- installDecor()方法的主要代码
private void installDecor() {
// 如果mDecor是null mDecor就是DecorView(FrameLayout)
if (mDecor == null) {
//generateDecor 主要就是new 一个DecorView 然后setWindow传入window对象赋值给mWindow对象
mDecor = generateDecor(-1);
}else{
mDecor.setWindow(this);
}
if (mContentParent == null) {
//接下来为mContentParent 赋值 下面分析一下generateLayout都做了什么
mContentParent = generateLayout(mDecor);
//....
}
}
- protected ViewGroup generateLayout(DecorView decor)方法
protected ViewGroup generateLayout(DecorView decor) {
//.......
//接下来很大一段都是判断requestFeature的值
if (a.getBoolean(R.styleable.Window_windowNoTitle, false)) {
requestFeature(FEATURE_NO_TITLE);
}
//......
//获取xml中设置的属性
WindowManager.LayoutParams params = getAttributes();
//接下来根据不同的features设置不同的layoutResource
}
所以requestWindowFeature()一定要在setContentView之前设置,因为setContentView的时候需要读取设置的值
继续看generateLayout方法
// 如果不设置requestWindowFeature属性默认走到下面的方法
} else {
layoutResource = R.layout.screen_simple;
}
看一下默认的R.layout.screen_simple是什么
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:orientation="vertical">
<ViewStub android:id="@+id/action_mode_bar_stub"
android:inflatedId="@+id/action_mode_bar"
android:layout="@layout/action_mode_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="?attr/actionBarTheme" />
<FrameLayout
android:id="@android:id/content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:foregroundInsidePadding="false"
android:foregroundGravity="fill_horizontal|top"
android:foreground="?android:attr/windowContentOverlay" />
</LinearLayout>
//里面主要是有一个id为content的FrameLayout和一个action_mode_bar_stub
接下来继续分析generateLayout()方法
//.....
//获取到了layoutResource 之后调用了下面这个方法 将此布局添加到mDecor里
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
//紧接着后去contentParent ID_ANDROID_CONTENT就是id为Content 上面提到的FrameLayout
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
//最后返回contentParent
return contentParent;
所以 上面installDecor()里面mContentParent就是id为content的FrameLayout
到这里可以初步确认Activity的布局层级结构
- installDecor()之后回到setContentView方法继续分析
@Override
public void setContentView(int layoutResID) {
if (mContentParent == null) {
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
getContext());
transitionTo(newScene);
} else {
mLayoutInflater.inflate(layoutResID, mContentParent);
}
//........
}
接下来看一下mLayoutInflater.inflate(layoutResID, mContentParent);都做了什么
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
final Resources res = getContext().getResources();
if (DEBUG) {
Log.d(TAG, "INFLATING from resource: \"" + res.getResourceName(resource) + "\" ("
+ Integer.toHexString(resource) + ")");
}
//解析xml传入inflate方法
final XmlResourceParser parser = res.getLayout(resource);
try {
return inflate(parser, root, attachToRoot);
} finally {
parser.close();
}
}
-LayoutInflater的 inflate()方法
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
//......
//将root 赋值给result
View result = root;
//.......
if (TAG_MERGE.equals(name)) {
//如果是merge标签 说明merge是能作为根节点
if (root == null || !attachToRoot) {
throw new InflateException("<merge /> can be used only with a valid "
+ "ViewGroup root and attachToRoot=true");
}
//.......
}else{
//如果不是merge标签
//创建对应name标签的view
final View temp = createViewFromTag(root, name, inflaterContext, attrs);
//......
rInflateChildren(parser, temp, attrs, true);
//......
}
}
- LayoutInflater 的 rInflateChildren方法
调用rInflate方法
final void rInflateChildren(XmlPullParser parser, View parent, AttributeSet attrs,
boolean finishInflate) throws XmlPullParserException, IOException {
rInflate(parser, parent, parent.getContext(), attrs, finishInflate);
}
-LayoutInflater 的 rInflate方法
void rInflate(XmlPullParser parser, View parent, Context context,
AttributeSet attrs, boolean finishInflate) throws XmlPullParserException, IOException {
final int depth = parser.getDepth();
int type;
boolean pendingRequestFocus = false;
while (((type = parser.next()) != XmlPullParser.END_TAG ||
parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
if (type != XmlPullParser.START_TAG) {
continue;
}
final String name = parser.getName();
if (TAG_REQUEST_FOCUS.equals(name)) {
pendingRequestFocus = true;
consumeChildElements(parser);
} else if (TAG_TAG.equals(name)) {
parseViewTag(parser, parent, attrs);
} else if (TAG_INCLUDE.equals(name)) {
if (parser.getDepth() == 0) {
throw new InflateException("<include /> cannot be the root element");
}
parseInclude(parser, context, parent, attrs);
} else if (TAG_MERGE.equals(name)) {
throw new InflateException("<merge /> must be the root element");
} else {
final View view = createViewFromTag(parent, name, context, attrs);
final ViewGroup viewGroup = (ViewGroup) parent;
final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
rInflateChildren(parser, view, attrs, true);
viewGroup.addView(view, params);
}
}
if (pendingRequestFocus) {
parent.restoreDefaultFocus();
}
if (finishInflate) {
parent.onFinishInflate();
}
}
然后就是不断地递归将view添加到parent,直到结束inflate
然后继续分析inflate方法
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
//......
//接下来rInflateChildren之后将temp添加到root中
if (root != null && attachToRoot) {
root.addView(temp, params);
}
//然后返回result
return result;
//结束inflate过程
}
- 接下来看一下上段代码 root.addView做了什么
@Override
public void addView(View child, LayoutParams params) {
addView(child, -1, params);
}
public void addView(View child, int index, LayoutParams params) {
if (DBG) {
System.out.println(this + " addView");
}
if (child == null) {
throw new IllegalArgumentException("Cannot add a null child view to a ViewGroup");
}
// addViewInner() will call child.requestLayout() when setting the new LayoutParams
// therefore, we call requestLayout() on ourselves before, so that the child's request
// will be blocked at our level
//这里调用了requestLayout
requestLayout();
invalidate(true);
addViewInner(child, index, params, false);
}
- view的requestLayout();方法
@CallSuper
public void requestLayout() {
if (mMeasureCache != null) mMeasureCache.clear();
if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == null) {
// Only trigger request-during-layout logic if this is the view requesting it,
// not the views in its parent hierarchy
ViewRootImpl viewRoot = getViewRootImpl();
if (viewRoot != null && viewRoot.isInLayout()) {
if (!viewRoot.requestLayoutDuringLayout(this)) {
return;
}
}
mAttachInfo.mViewRequestingLayout = this;
}
mPrivateFlags |= PFLAG_FORCE_LAYOUT;
mPrivateFlags |= PFLAG_INVALIDATED;
if (mParent != null && !mParent.isLayoutRequested()) {
mParent.requestLayout();
}
if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == this) {
mAttachInfo.mViewRequestingLayout = null;
}
}
在requestLayout中获得了ViewRootImpl对象
ViewRootImpl viewRoot = getViewRootImpl();
//.....
if (mParent != null && !mParent.isLayoutRequested()) {
mParent.requestLayout();
}
- 然后调用到了 ViewRootImpl的requestLayout方法
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
- 接下来调用到了 scheduleTraversals();方法
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
- 然后经过很复杂的调用调用了mTraversalRunnable的(Runnable)action).run();
- 接下来看一下mTraversalRunnable
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
- ViewRootImpl 的 doTraversal();方法
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
if (mProfile) {
Debug.startMethodTracing("ViewAncestor");
}
// 重点
performTraversals();
if (mProfile) {
Debug.stopMethodTracing();
mProfile = false;
}
}
}
- performTraversals();
接下来在 performTraversals();的方法里面依次调用了
performMeasur();
performLayout();
performDraw(); - 接下来分析一下这三个方法 performMeasur();
private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
if (mView == null) {
return;
}
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
try {
mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
-
//performMeasure方法中调用了 mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);方法
-
performLayou() 调用了host.layout
final View host = mView;
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
- performDraw方法最后也调用了 mView.draw();
- 接下来就是该分析view的measure layout draw 方法了
Activity 在 onResume() 之后才会显示的原因是什么?
- 在 onCreate() 中调用 setContextView() 只是去加载各种设置,解析 view tree ,设置主题等等…此时并没有把页面显示出来。
- 直接看 onResume() 调用之前,在 Activity 的 handleResumeActivity() 中
public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward, String reason) {
// 这里面调用了 onResume() 方法
final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
final Activity a = r.activity;
if (r.window == null && !a.mFinished && willBeVisible) {
r.window = r.activity.getWindow();
View decor = r.window.getDecorView();
// 默认 INVISIBLE
decor.setVisibility(View.INVISIBLE);
// 获取 WindowManager
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
// 此时才将 decorView 添加到了 window 中
wm.addView(decor, l);
}
// 刷新让它展示 将上面设置 decor.setVisibility 修改为 mDecor.setVisibility(View.VISIBLE); 触发一次重绘
r.activity.makeVisible();
Looper.myQueue().addIdleHandler(new Idler());
}
在上面的方法中,wm.addView(decor, l); 调用的是 WindowManagerImpl 的 addView() 方法如下:
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
public void addView(View view, ViewGroup.LayoutParams params,Display display, Window parentWindow) {
ViewRootImpl root;
// ...
root = new ViewRootImpl(view.getContext(), display);
root.setView(view, wparams, panelParentView);
}
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
if (mView == null) {
mView = view;
requestLayout();
// binder 调用
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(), mWinFrame,
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel);
}
}
}
- requestLayout();
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
- scheduleTraversals();
void scheduleTraversals() {
if (!mTraversalScheduled) {
// ...
// 往 mChoreographer 中添加一个 callback ,这个会在下一个 Vsync 来的时候触发这个 callback,
// mTraversalRunnable 是一个runnable run方法中调用了 doTraversal(); --> performTraversals(); 内部是真正执行绘制的 先调用 relayoutWindow() 像 wms 申请 surface
// performMeasure() performLayout() performDraw()
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
// ...
}
}
- relayoutWindow()
private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
boolean insetsPending) throws RemoteException {
// relayout binder调用执行结束 mSurface 就可以用了,然后我们surface才有 Buffer ,在 Buffer 上绘制之后再提交到 surfaceflinger ,surfaceflinger 写到屏幕缓冲区就可以显示了
int relayoutResult = mWindowSession.relayout(mWindow, mSeq,...,mSurface)
}
relayoutWindow 函数内 relayout binder调用执行结束 mSurface 就可以用了,然后我们surface才有 Buffer ,在 Buffer 上绘制之后再提交到 surfaceflinger ,surfaceflinger 写到屏幕缓冲区就可以显示了
- mWindowSession.addToDisplay()
mWindowSession 就是通过 wms 获取到的一个 Session 对象。
IWindowManager windowManager = getWindowManagerService();
sWindowSession = windowManager.openSession(
new IWindowSessionCallback.Stub() {
@Override
public void onAnimatorScaleChanged(float scale) {
ValueAnimator.setDurationScale(scale);
}
},
imm.getClient(), imm.getInputContext());
- IPC 调用从 WindowManagerService 获取 Session;Session就是用来给应用和 WMS 通信的。
@Override
public IWindowSession openSession(IWindowSessionCallback callback, IInputMethodClient client,
IInputContext inputContext) {
if (client == null) throw new IllegalArgumentException("null client");
if (inputContext == null) throw new IllegalArgumentException("null inputContext");
Session session = new Session(this, callback, client, inputContext);
return session;
}
那么 session 的 addToDisplay() 的作用是啥
// 传入的 IWindow window 是一个binder对象 方便 WindowManagerService 调用应用层的 window。相当于互相引用了 方便互相调用。
@Override
public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, Rect outFrame, Rect outContentInsets,
Rect outStableInsets, Rect outOutsets,
DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel) {
return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outFrame,
outContentInsets, outStableInsets, outOutsets, outDisplayCutout, outInputChannel);
}
mService.addWindow 就是在 wms 端创建一个window对,统一有 wms 进行管理(层级 显示位置大小 等)
wms 主要作用:
- 分配 surface ,管理 surface 的顺序位置尺寸等。
- 控制窗口动画
- 输入事件分发