<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                ViewRootImpl創建于WindowManagerGlobal的addView()方法中,而調用addView()方法的線程即是此ViewRootImpl所掌控的控件樹的UI線程。ViewRootImpl的構造主要是初始化了一些重要的成員,事先對這些重要的成員有個初步的認識對隨后探討ViewRootImpl的工作原理有很大的幫助。其構造函數代碼如下: **ViewRootImpl.java-->ViewRootImpl.ViewRootImpl()** ``` public ViewRootImpl(Context context, Displaydisplay) { /* ① 從WindowManagerGlobal中獲取一個IWindowSession的實例。它是ViewRootImpl和 WMS進行通信的代理 */ mWindowSession= WindowManagerGlobal.getWindowSession(context.getMainLooper()); // **②保存參數display**,在后面setView()調用中將會把窗口添加到這個Display上 mDisplay= display; CompatibilityInfoHolder cih = display.getCompatibilityInfo(); mCompatibilityInfo = cih != null ? cih : new CompatibilityInfoHolder(); /* **③ 保存當前線程到mThread。**這個賦值操作體現了創建ViewRootImpl的線程如何成為UI主線程。 在ViewRootImpl處理來自控件樹的請求時(如請求重新布局,請求重繪,改變焦點等),會檢 查發起請求的thread與這個mThread是否相同。倘若不同則會拒絕這個請求并拋出一個異常*/ mThread= Thread.currentThread(); ...... /* **④ mDirty用于收集窗口中的無效區域。**所謂無效區域是指由于數據或狀態發生改變時而需要進行重繪 的區域。舉例說明,當應用程序修改了一個TextView的文字時,TextView會將自己的區域標記為無效 區域,并通過invalidate()方法將這塊區域收集到這里的mDirty中。當下次繪制時,TextView便 可以將新的文字繪制在這塊區域上 */ mDirty =new Rect(); mTempRect = new Rect(); mVisRect= new Rect(); /* **⑤ mWinFrame,描述了當前窗口的位置和尺寸。**與WMS中WindowState.mFrame保持著一致 */ mWinFrame = new Rect(); /* ⑥ 創建一個W類型的實例,W是IWindow.Stub的子類。即它將在WMS中作為新窗口的ID,以及接 收來自WMS的回調*/ mWindow= new W(this); ...... /* **⑦ 創建mAttachInfo。**mAttachInfo是控件系統中很重要的對象。它存儲了此當前控件樹所以貼附 的窗口的各種有用的信息,并且會派發給控件樹中的每一個控件。這些控件會將這個對象保存在自己的 mAttachInfo變量中。mAttachInfo中所保存的信息有WindowSession,窗口的實例(即mWindow), ViewRootImpl實例,窗口所屬的Display,窗口的Surface以及窗口在屏幕上的位置等等。所以,當 要需在一個View中查詢與當前窗口相關的信息時,非常值得在mAttachInfo中搜索一下 */ mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display,this, mHandler, this); /* **⑧ 創建FallbackEventHandler。**這個類如同PhoneWindowManger一樣定義在android.policy 包中,其實現為PhoneFallbackEventHandler。FallbackEventHandler是一個處理未經任何人 消費的輸入事件的場所。在6.5.4節中將會介紹它 */ mFallbackEventHandler =PolicyManager.makeNewFallbackEventHandler(context); ...... /* ⑨ 創建一個依附于當前線程,即主線程的Choreographer,用于通過VSYNC特性安排重繪行為 */ mChoreographer= Choreographer.getInstance(); ...... } ``` 在構造函數之外,還有另外兩個重要的成員被直接初始化: - mHandler,類型為ViewRootHandler,一個依附于創建ViewRootImpl的線程,即主線程上的,用于將某些必須主線程進行的操作安排在主線程中執行。mHandler與mChoreographer的同時存在看似有些重復,其實它們擁有明確不同的分工與意義。由于mChoreographer處理消息時具有VSYNC特性,因此它主要用于處理與重繪相關的操作。但是由于mChoreographer需要等待VSYNC的垂直同步事件來觸發對下一條消息的處理,因此它處理消息的及時性稍遜于mHandler。而mHandler的作用,則是為了將發生在其他線程中的事件安排在主線程上執行。所謂發生在其他線程中的事件是指來自于WMS,由繼承自IWindow.Stub的mWindow引發的回調。由于mWindow是一個Binder對象的Bn端,因此這些回調發生在Binder的線程池中。而這些回調會影響到控件系統的重新測量、布局與繪制,因此需要此Handler將回調安排到主線程中。 說明 mHandler與mThread兩個成員都是為了單線程模型而存在的。Android的UI操作不是線程安全的,而且很多操作也是建立在單線程的假設之上(如scheduleTraversals())。采用單線程模型的目的是降低系統的復雜度,并且降低鎖的開銷。 - mSurface,類型為Surface。采用無參構造函數創建的一個Surface實例。mSurface此時是一個沒有任何內容的空殼子,在 WMS通過relayoutWindow()為其分配一塊Surface之前尚不能實用。 - mWinFrame、mPendingContentInset、mPendingVisibleInset以及mWidth,mHeight。這幾個成員存儲了窗口布局相關的信息。其中mWinFrame、mPendingConentInsets、mPendingVisibleInsets與窗口在WMS中的Frame、ContentInsets、VisibleInsets是保持同步的。這是因為這3個成員不僅會作為 relayoutWindow()的傳出參數,而且ViewRootImpl在收到來自WMS的回調IWindow.Stub.resize()時,立即更新這3個成員的取值。因此這3個成員體現了窗口在WMS中的最新狀態。與mWinFrame中的記錄窗口在WMS中的尺寸不同的是,mWidth/mHeight記錄了窗口在ViewRootImpl中的尺寸,二者在絕大多數情況下是相同的。當窗口在WMS中被重新布局而導致尺寸發生變化時,mWinFrame會首先被IWindow.Stub.resize()回調更新,此時mWinFrame便會與mWidth/mHeight產生差異。此時ViewRootImpl即可得知需要對控件樹進行重新布局以適應新的窗口變化。在布局完成后,mWidth/mHeight會被賦值為mWinFrame中所保存的寬和高,二者重新統一。在隨后分析performTraversals()方法時,讀者將會看到這一處理。另外,與mWidth/mHeight類似,ViewRootImpl也保存了窗口的位置信息Left/Top以及ContentInsets/VisibleInsets供控件樹查詢,不過這四項信息被保存在了mAttachInfo中。 ViewRootImpl的在其構造函數中初始化了一系列的成員變量,然而其創建過程仍未完成。僅在為其指定了一個控件樹進行管理,并向WMS添加了一個新的窗口之后,ViewRootImpl承上啟下的角色才算完全確立下來。因此需要進一步分析ViewRootImpl.setView()方法。 **ViewRootImp.java-->ViewRootImpl.setView()** ``` public void setView(View view,WindowManager.LayoutParams attrs, View panelParentView) { synchronized (this) { if (mView == null) { // **① mView保存了控件樹的根** mView = view; ...... // ②mWindowAttributes保存了窗口所對應的LayoutParams mWindowAttributes.copyFrom(attrs); ...... /* 在添加窗口之前,先通過requestLayout()方法在主線程上安排一次“遍歷”。所謂 “遍歷”是指ViewRootImpl中的核心方法performTraversals()。這個方法實現了對 控件樹進行測量、布局、向WMS申請修改窗口屬性以及重繪的所有工作。由于此“遍歷” 操作對于初次遍歷做了一些特殊處理,而來自WMS通過mWindow發生的回調會導致一些屬性 發生變化,如窗口的尺寸、Insets以及窗口焦點等,從而有可能使得初次“遍歷”的現場遭 到破壞。因此,需要在添加窗口之前,先發送一個“遍歷”消息到主線程。 在主線程中向主線程的Handler發送消息如果使用得當,可以產生很精妙的效果。例如本例 中可以實現如下的執行順序:添加窗口->初次遍歷->處理來自WMS的回調 */ requestLayout(); /***③ 初始化mInputChannel。**參考第五章,InputChannel是窗口接受來自InputDispatcher 的輸入事件的管道。 注意,僅當窗口的屬性inputFeatures不含有 INPUT_FEATURE_NO_INPUT_CHANNEL時才會創建InputChannel,否則mInputChannel 為空,從而導致此窗口無法接受任何輸入事件 */ if ((mWindowAttributes.inputFeatures & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) { mInputChannel = new InputChannel(); } try { ...... /* 將窗口添加到WMS中。完成這個操作之后,mWindow已經被添加到指定的Display中去 而且mInputChannel(如果不為空)已經準備好接受事件了。只是由于這個窗口沒有進行 過relayout(),因此它還沒有有效的Surface可以進行繪制 */ res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes, getHostVisibility(), mDisplay.getDisplayId(), mAttachInfo.mContentInsets, mInputChannel); } catch (RemoteException e) {......} finally { ...... } ...... if (res < WindowManagerGlobal.ADD_OKAY) { // 錯誤處理。窗口添加失敗的原因通常是權限問題,重復添加,或者tokeen無效 } ...... /*④ 如果mInputChannel不為空,則創建mInputEventReceiver,用于接受輸入事件。 注意第二個參數傳遞的是Looper.myLooper(),即mInputEventReceiver將在主線程上 觸發輸入事件的讀取與onInputEvent()。這是應用程序可以在onTouch()等事件響應中 直接進行UI操作等根本原因。 */ if (mInputChannel != null) { ...... mInputEventReceiver = new WindowInputEventReceiver(mInputChannel, Looper.myLooper()); } /* ViewRootImpl將作為參數view的parent。所以,ViewRootImpl可以從控件樹中任何一個 控件開始,通過回溯getParent()的方法得到 */ view.assignParent(this); ...... } } } ``` 至此,ViewRootImpl所有重要的成員都已經初始化完畢,新的窗口也已經添加到WMS中。ViewRootImpl的創建過程是由構造函數和setView()方法兩個環節構成的。其中構造函數主要進行成員的初始化,setView()則是創建窗口、建立輸入事件接收機制的場所。同時,觸發第一次“遍歷”操作的消息已經發送給主線程,在隨后的第一次“遍歷”完成后,ViewRootImpl將會完成對控件樹的第一次測量、布局,并從WMS獲取窗口的Surface以進行控件樹的初次繪制工作。 在本節的最后,通過圖 6 – 4對ViewRootImpl中的重要成員進行了分類整理。 :-: ![](http://img.blog.csdn.net/20150814133547769?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center) 圖 6 - 4 ViewRootImpl中的主要成員
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看