Android 的显示原理

相关问题

 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 的顺序位置尺寸等。
  • 控制窗口动画
  • 输入事件分发

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值