日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > Android >内容正文

Android

Android 的显示原理

發(fā)布時(shí)間:2024/1/18 Android 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Android 的显示原理 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

文章目錄

    • 相關(guān)問題
    • setContentView 它的原理是什么?
    • Activity 在 onResume() 之后才會(huì)顯示的原因是什么?

相關(guā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;}

說明getWindow().setContentView(layoutResID);調(diào)用的是window對(duì)象的setContentView對(duì)象,接下來去Window唯一實(shí)現(xiàn)類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()方法的主要代碼
private void installDecor() {// 如果mDecor是null mDecor就是DecorView(FrameLayout)if (mDecor == null) {//generateDecor 主要就是new 一個(gè)DecorView 然后setWindow傳入window對(duì)象賦值給mWindow對(duì)象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中設(shè)置的屬性WindowManager.LayoutParams params = getAttributes();//接下來根據(jù)不同的features設(shè)置不同的layoutResource}

所以requestWindowFeature()一定要在setContentView之前設(shè)置,因?yàn)閟etContentView的時(shí)候需要讀取設(shè)置的值
繼續(xù)看generateLayout方法

// 如果不設(shè)置requestWindowFeature屬性默認(rèn)走到下面的方法 } else {layoutResource = R.layout.screen_simple; }

看一下默認(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
接下來繼續(xù)分析generateLayout()方法

//..... //獲取到了layoutResource 之后調(diào)用了下面這個(gè)方法 將此布局添加到mDecor里 mDecor.onResourcesLoaded(mLayoutInflater, layoutResource); //緊接著后去contentParent ID_ANDROID_CONTENT就是id為Content 上面提到的FrameLayout ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);//最后返回contentParentreturn contentParent;

所以 上面installDecor()里面mContentParent就是id為content的FrameLayout
到這里可以初步確認(rèn)Activity的布局層級(jí)結(jié)構(gòu)

  • installDecor()之后回到setContentView方法繼續(xù)分析
@Overridepublic 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 賦值給resultView result = root;//.......if (TAG_MERGE.equals(name)) {//如果是merge標(biāo)簽 說明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方法
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,直到結(jié)束inflate
然后繼續(xù)分析inflate方法

public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {//......//接下來rInflateChildren之后將temp添加到root中if (root != null && attachToRoot) {root.addView(temp, params);}//然后返回resultreturn result;//結(jié)束inflate過程}
  • 接下來看一下上段代碼 root.addView做了什么
@Overridepublic 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//這里調(diào)用了requestLayout requestLayout();invalidate(true);addViewInner(child, index, params, false);}
  • view的requestLayout();方法
@CallSuperpublic 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 hierarchyViewRootImpl 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對(duì)象

ViewRootImpl viewRoot = getViewRootImpl();//.....if (mParent != null && !mParent.isLayoutRequested()) {mParent.requestLayout();}
  • 然后調(diào)用到了 ViewRootImpl的requestLayout方法
@Overridepublic void requestLayout() {if (!mHandlingLayoutInLayoutRequest) {checkThread();mLayoutRequested = true;scheduleTraversals();}}
  • 接下來調(diào)用到了 scheduleTraversals();方法
void scheduleTraversals() {if (!mTraversalScheduled) {mTraversalScheduled = true;mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);if (!mUnbufferedInputDispatch) {scheduleConsumeBatchedInput();}notifyRendererOfFramePending();pokeDrawLockIfNeeded();}}
  • 然后經(jīng)過很復(fù)雜的調(diào)用調(diào)用了mTraversalRunnable的(Runnable)action).run();
  • 接下來看一下mTraversalRunnable
final class TraversalRunnable implements Runnable {@Overridepublic void run() {doTraversal();}}
  • ViewRootImpl 的 doTraversal();方法
void doTraversal() {if (mTraversalScheduled) {mTraversalScheduled = false;mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);if (mProfile) {Debug.startMethodTracing("ViewAncestor");}// 重點(diǎn)performTraversals();if (mProfile) {Debug.stopMethodTracing();mProfile = false;}}}
  • performTraversals();
    接下來在 performTraversals();的方法里面依次調(diào)用了
    performMeasur();
    performLayout();
    performDraw();
  • 接下來分析一下這三個(gè)方法 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方法中調(diào)用了 mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);方法

  • performLayou() 調(diào)用了host.layout

final View host = mView;host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
  • performDraw方法最后也調(diào)用了 mView.draw();
  • 接下來就是該分析view的measure layout draw 方法了

Activity 在 onResume() 之后才會(huì)顯示的原因是什么?

  • 在 onCreate() 中調(diào)用 setContextView() 只是去加載各種設(shè)置,解析 view tree ,設(shè)置主題等等…此時(shí)并沒有把頁面顯示出來。
  • 直接看 onResume() 調(diào)用之前,在 Activity 的 handleResumeActivity() 中
public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward, String reason) {// 這里面調(diào)用了 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();// 默認(rèn) INVISIBLEdecor.setVisibility(View.INVISIBLE);// 獲取 WindowManager ViewManager wm = a.getWindowManager();WindowManager.LayoutParams l = r.window.getAttributes();a.mDecor = decor;// 此時(shí)才將 decorView 添加到了 window 中wm.addView(decor, l);}// 刷新讓它展示 將上面設(shè)置 decor.setVisibility 修改為 mDecor.setVisibility(View.VISIBLE); 觸發(fā)一次重繪r.activity.makeVisible();Looper.myQueue().addIdleHandler(new Idler()); }

在上面的方法中,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();
@Overridepublic void requestLayout() {if (!mHandlingLayoutInLayoutRequest) {checkThread();mLayoutRequested = true;scheduleTraversals();}}
  • scheduleTraversals();
void scheduleTraversals() {if (!mTraversalScheduled) {// ...// 往 mChoreographer 中添加一個(gè) callback ,這個(gè)會(huì)在下一個(gè) Vsync 來的時(shí)候觸發(fā)這個(gè) callback,// mTraversalRunnable 是一個(gè)runnable run方法中調(diào)用了 doTraversal(); --> performTraversals(); 內(nèi)部是真正執(zhí)行繪制的 先調(diào)用 relayoutWindow() 像 wms 申請(qǐng) 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調(diào)用執(zhí)行結(jié)束 mSurface 就可以用了,然后我們surface才有 Buffer ,在 Buffer 上繪制之后再提交到 surfaceflinger ,surfaceflinger 寫到屏幕緩沖區(qū)就可以顯示了int relayoutResult = mWindowSession.relayout(mWindow, mSeq,...,mSurface)}

relayoutWindow 函數(shù)內(nèi) relayout binder調(diào)用執(zhí)行結(jié)束 mSurface 就可以用了,然后我們surface才有 Buffer ,在 Buffer 上繪制之后再提交到 surfaceflinger ,surfaceflinger 寫到屏幕緩沖區(qū)就可以顯示了

  • mWindowSession.addToDisplay()

mWindowSession 就是通過 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就是用來給應(yīng)用和 WMS 通信的。
@Overridepublic 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 是一個(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)畫
  • 輸入事件分發(fā)

總結(jié)

以上是生活随笔為你收集整理的Android 的显示原理的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。