Android 的显示原理
文章目錄
- 相關(guān)問(wèn)題
- setContentView 它的原理是什么?
- Activity 在 onResume() 之后才會(huì)顯示的原因是什么?
相關(guān)問(wèn)題
Activity 的顯示原理 (Window、DecorView、ViewRoot)Activity 的 UI 刷新機(jī)制(Vsync、Choreographer)UI 的繪制原理(Measure、Layout、Draw)Surface 原理(Surface、SurfaceFlinger)- Activity 的顯示原理 (Window、DecorView、ViewRoot)
- setContentView() 原理是什么?
- Activity 在 onResume() 之后才會(huì)顯示的原因是什么?
- ViewRoot 是干嘛的,是 View Tree 的 rootView 么?
setContentView 它的原理是什么?
MainActivity繼承Activity
public class MainActivity extends Activity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);} }點(diǎn)擊查看setContentView跳轉(zhuǎn)到Activity源碼
public void setContentView(@LayoutRes int layoutResID) {getWindow().setContentView(layoutResID);initWindowDecorActionBar();}getWindow();就是window對(duì)象 mWindow 就是Window
private Window mWindow; public Window getWindow() {return mWindow;}說(shuō)明getWindow().setContentView(layoutResID);調(diào)用的是window對(duì)象的setContentView對(duì)象,接下來(lái)去Window唯一實(shí)現(xiàn)類(lèi)PhoneWindow對(duì)象查看
ViewGroup mContentParent; @Overridepublic void setContentView(int layoutResID) {//mContentParent 就是viewGroupif (mContentParent == null) {//當(dāng)mContentParent 是null 然后看看installDecor方法做了什么installDecor();} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {mContentParent.removeAllViews();}}- installDecor()方法的主要代碼
- protected ViewGroup generateLayout(DecorView decor)方法
所以requestWindowFeature()一定要在setContentView之前設(shè)置,因?yàn)閟etContentView的時(shí)候需要讀取設(shè)置的值
繼續(xù)看generateLayout方法
看一下默認(rèn)的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" /><FrameLayoutandroid: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>//里面主要是有一個(gè)id為content的FrameLayout和一個(gè)action_mode_bar_stub
接下來(lái)繼續(xù)分析generateLayout()方法
所以 上面installDecor()里面mContentParent就是id為content的FrameLayout
到這里可以初步確認(rèn)Activity的布局層級(jí)結(jié)構(gòu)
- installDecor()之后回到setContentView方法繼續(xù)分析
接下來(lái)看一下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 賦值給resultView result = root;//.......if (TAG_MERGE.equals(name)) {//如果是merge標(biāo)簽 說(shuō)明merge是能作為根節(jié)點(diǎn)if (root == null || !attachToRoot) {throw new InflateException("<merge /> can be used only with a valid "+ "ViewGroup root and attachToRoot=true");}//.......}else{//如果不是merge標(biāo)簽//創(chuàng)建對(duì)應(yīng)name標(biāo)簽的viewfinal View temp = createViewFromTag(root, name, inflaterContext, attrs);//......rInflateChildren(parser, temp, attrs, true);//......} }- LayoutInflater 的 rInflateChildren方法
調(diào)用rInflate方法
-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,直到結(jié)束inflate
然后繼續(xù)分析inflate方法
- 接下來(lái)看一下上段代碼 root.addView做了什么
- view的requestLayout();方法
在requestLayout中獲得了ViewRootImpl對(duì)象
ViewRootImpl viewRoot = getViewRootImpl();//.....if (mParent != null && !mParent.isLayoutRequested()) {mParent.requestLayout();}- 然后調(diào)用到了 ViewRootImpl的requestLayout方法
- 接下來(lái)調(diào)用到了 scheduleTraversals();方法
- 然后經(jīng)過(guò)很復(fù)雜的調(diào)用調(diào)用了mTraversalRunnable的(Runnable)action).run();
- 接下來(lái)看一下mTraversalRunnable
- ViewRootImpl 的 doTraversal();方法
- performTraversals();
接下來(lái)在 performTraversals();的方法里面依次調(diào)用了
performMeasur();
performLayout();
performDraw(); - 接下來(lái)分析一下這三個(gè)方法 performMeasur();
-
//performMeasure方法中調(diào)用了 mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);方法
-
performLayou() 調(diào)用了host.layout
- performDraw方法最后也調(diào)用了 mView.draw();
- 接下來(lái)就是該分析view的measure layout draw 方法了
Activity 在 onResume() 之后才會(huì)顯示的原因是什么?
- 在 onCreate() 中調(diào)用 setContextView() 只是去加載各種設(shè)置,解析 view tree ,設(shè)置主題等等…此時(shí)并沒(méi)有把頁(yè)面顯示出來(lái)。
- 直接看 onResume() 調(diào)用之前,在 Activity 的 handleResumeActivity() 中
在上面的方法中,wm.addView(decor, l); 調(diào)用的是 WindowManagerImpl 的 addView() 方法如下:
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();@Overridepublic 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 調(diào)用res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,getHostVisibility(), mDisplay.getDisplayId(), mWinFrame,mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel);}}}- requestLayout();
- scheduleTraversals();
- relayoutWindow()
relayoutWindow 函數(shù)內(nèi) relayout binder調(diào)用執(zhí)行結(jié)束 mSurface 就可以用了,然后我們surface才有 Buffer ,在 Buffer 上繪制之后再提交到 surfaceflinger ,surfaceflinger 寫(xiě)到屏幕緩沖區(qū)就可以顯示了
- mWindowSession.addToDisplay()
mWindowSession 就是通過(guò) wms 獲取到的一個(gè) Session 對(duì)象。
IWindowManager windowManager = getWindowManagerService(); sWindowSession = windowManager.openSession(new IWindowSessionCallback.Stub() {@Overridepublic void onAnimatorScaleChanged(float scale) {ValueAnimator.setDurationScale(scale);}},imm.getClient(), imm.getInputContext());- IPC 調(diào)用從 WindowManagerService 獲取 Session;Session就是用來(lái)給應(yīng)用和 WMS 通信的。
那么 session 的 addToDisplay() 的作用是啥
// 傳入的 IWindow window 是一個(gè)binder對(duì)象 方便 WindowManagerService 調(diào)用應(yīng)用層的 window。相當(dāng)于互相引用了 方便互相調(diào)用。@Overridepublic 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 端創(chuàng)建一個(gè)window對(duì),統(tǒng)一有 wms 進(jìn)行管理(層級(jí) 顯示位置大小 等)
wms 主要作用:
- 分配 surface ,管理 surface 的順序位置尺寸等。
- 控制窗口動(dòng)畫(huà)
- 輸入事件分發(fā)
總結(jié)
以上是生活随笔為你收集整理的Android 的显示原理的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 马光远:春运买票难根源在于部门利益
- 下一篇: 《那些年啊,那些事——一个程序员的奋斗史