當前位置:首頁 » 安卓系統 » androidview初始化

androidview初始化

發布時間: 2023-01-24 10:44:27

① Android 重學系列 View的繪制流程(六) 硬體渲染(上)

本文開始聊聊Android中的硬體渲染。如果跟著我的文章順序,從SF進程到App進程的繪制流程一直閱讀,我們到這里已經有了一定的基礎,可以試著進行橫向比對如Chrome瀏覽器渲染流程,看看軟體渲染,硬體渲染,SF合成都做了什麼程度的優化。

先讓我們回顧一下負責硬體渲染的主體對象ThreadedRenderer在整個繪制流程中做了哪幾個步驟。

在硬體渲染的過程中,有一個很核心的對象RenderNode,作為每一個View繪制的節點對象。

當每一次進行准備進行繪制的時候,都會雷打不動執行如下三個步驟:

如果遇到什麼問題歡迎來到 https://www.jianshu.com/p/c84bfa909810 下進行討論

實際上整個硬體渲染的設計還是比較龐大。因此本文先聊聊ThreadedRender整個體系中主要對象的構造以及相關的原理。

首先來認識下面幾個重要的對象有一個大體的印象。

java層中面向Framework中,只有這么多,下面是一一映射的簡圖。

能看到實際上RenderNode也會跟著View 樹的構建同時一起構建整個顯示層級。也是因此ThreadedRender也能以RenderNode為線索構建出一套和軟體渲染一樣的渲染流程。

僅僅這樣?如果只是這么簡單,知道我習慣的都知道,我喜歡把相關總結寫在最後。如果把總攬寫在正文開頭是因為設計比較繁多。因為我們如果以流水線的形式進行剖析容易造成迷失細節的困境。

讓我繼續介紹一下,在硬體渲染中native層的核心對象。

如下是一個思維導圖:

有這么一個大體印象後,就不容易迷失在源碼中。我們先來把這些對象的實例化以及上面列舉的ThreadedRenderer在ViewRootImpl中執行行為的順序和大家來聊聊其原理,先來看看ThreadedRenderer的實例化。

當發現mSurfaceHolder為空的時候會調用如下函數:

而這個方法則調用如下的方法對ThreadedRenderer進行創建:

文件:/ frameworks / base / core / java / android / view / ThreadedRenderer.java

能不能創建的了ThreadedRenderer則決定於全局配置。如果ro.kernel.qemu的配置為0,說明支持OpenGL 則可以直接返回true。如果qemu.gles為-1說明不支持OpenGL es返回false,只能使用軟體渲染。如果設置了qemu.gles並大於0,才能打開硬體渲染。

我們能看到ThreadedRenderer在初始化,做了三件事情:

關鍵是看1-3點中ThreadRenderer都做了什麼。

文件:/ frameworks / base / core / jni / android_view_ThreadedRenderer.cpp

能看到這里是直接實例化一個RootRenderNode對象,並把指針的地址直接返回。

能看到RootRenderNode繼承了RenderNode對象,並且保存一個JavaVM也就是我們所說的Java虛擬機對象,一個java進程全局只有一個。同時通過getForThread方法,獲取ThreadLocal中的Looper對象。這里實際上拿的就是UI線程的Looper。

在這個構造函數有一個mDisplayList十分重要,記住之後會頻繁出現。接著來看看RenderNode的頭文件:
文件:/ frameworks / base / libs / hwui / RenderNode.h

實際上我把幾個重要的對象留下來:

文件:/ frameworks / base / core / java / android / view / RenderNode.java

能看到很簡單,就是包裹一個native層的RenderNode返回一個Java層對應的對象開放Java層的操作API。

能看到這個過程生成了兩個對象:

這個對象實際上讓RenderProxy持有一個創建動畫上下文的工廠。RenderProxy可以通過ContextFactoryImpl為每一個RenderNode創建一個動畫執行對象的上下文AnimationContextBridge。

文件:/ frameworks / base / libs / hwui / renderthread / RenderProxy.cpp

在這里有幾個十分重要的對象被實例化,當然這幾個對象在聊TextureView有聊過( SurfaceView和TextureView 源碼淺析 ):

我們依次看看他們初始化都做了什麼。

文件:/ frameworks / base / libs / hwui / renderthread / RenderThread.cpp

能看到其實就是簡單的調用RenderThread的構造函數進行實例化,並且返回對象的指針。

RenderThread是一個線程對象。先來看看其頭文件繼承的對象:
文件:/ frameworks / base / libs / hwui / renderthread / RenderThread.h

其中RenderThread的中進行排隊處理的任務隊列實際上是來自ThreadBase的WorkQueue對象。

文件:/ frameworks / base / libs / hwui / thread / ThreadBase.h

ThreadBase則是繼承於Thread對象。當調用start方法時候其實就是調用Thread的run方法啟動線程。

另一個更加關鍵的對象,就是實例化一個Looper對象到WorkQueue中。而直接實例化Looper實際上就是新建一個Looper。但是這個Looper並沒有獲取當先線程的Looper,這個Looper做什麼的呢?下文就會揭曉。

WorkQueue把一個Looper的方法指針設置到其中,其作用可能是完成了某一件任務後喚醒Looper繼續工作。

而start方法會啟動Thread的run方法。而run方法最終會走到threadLoop方法中,至於是怎麼走進來的,之後有機會會解剖虛擬機的源碼線程篇章進行講解。

在threadloop中關鍵的步驟有如下四個:

在這個過程中創建了幾個核心對象:

另一個核心的方法就是,這個方法為WorkQueue的Looper注冊了監聽:

能看到在這個Looper中注冊了對DisplayEventReceiver的監聽,也就是Vsync信號的監聽,回調方法為displayEventReceiverCallback。

我們暫時先對RenderThread的方法探索到這里,我們稍後繼續看看回調後的邏輯。

文件:/ frameworks / base / libs / hwui / thread / ThreadBase.h

能看到這里的邏輯很簡單實際上就是調用Looper的pollOnce方法,阻塞Looper中的循環,直到Vsync的信號到來才會繼續往下執行。詳細的可以閱讀我寫的 Handler與相關系統調用的剖析 系列文章。

文件:/ frameworks / base / libs / hwui / thread / ThreadBase.h

實際上調用的是WorkQueue的process方法。

文件:/ frameworks / base / libs / hwui / thread / WorkQueue.h

能看到這個過程中很簡單,幾乎和Message的loop的邏輯一致。如果Looper的阻塞打開了,則首先找到預計執行時間比當前時刻都大的WorkItem。並且從mWorkQueue移除,最後添加到toProcess中,並且執行每一個WorkItem的work方法。而每一個WorkItem其實就是通過從某一個壓入方法添加到mWorkQueue中。

到這里,我們就明白了RenderThread中是如何消費渲染任務的。那麼這些渲染任務又是哪裡誕生呢?

上文聊到了在RenderThread中的Looper會監聽Vsync信號,當信號回調後將會執行下面的回調。

能看到這個方法的核心實際上就是調用drainDisplayEventQueue方法,對ui渲染任務隊列進行處理。

能到在這里mVsyncRequested設置為false,且mFrameCallbackTaskPending將會設置為true,並且調用queue的postAt的方法執行ui渲染方法。

還記得queue實際是是指WorkQueue,而WorkQueue的postAt方法實際實現如下:
/ frameworks / base / libs / hwui / thread / WorkQueue.h

情景帶入,當一個Vsync信號達到Looper的監聽者,此時就會通過WorkQueue的drainDisplayEventQueue 壓入一個任務到隊列中。

每一個默認的任務都是執行dispatchFrameCallback方法。這里的判斷mWorkQueue中是否存在比當前時間更遲的時刻,並返回這個WorkItem。如果這個對象在頭部needsWakeup為true,說明可以進行喚醒了。而mWakeFunc這個方法指針就是上面傳下來:

把阻塞的Looper喚醒。當喚醒後就繼續執行WorkQueue的process方法。也就是執行dispatchFrameCallbacks方法。

在這里執行了兩個事情:

先添加到集合中,在上面提到過的threadLoop中,會執行如下邏輯:

如果大小不為0,則的把中的IFrameCallback全部遷移到mFrameCallbacks中。

而這個方法什麼時候調用呢?稍後就會介紹。其實這部分的邏輯在TextureView的解析中提到過。

接下來將會初始化一個重要對象:

這個對象名字叫做畫布的上下文,具體是什麼上下文呢?我們現在就來看看其實例化方法。
文件:/ frameworks / base / libs / hwui / renderthread / CanvasContext.cpp

文件:/ device / generic / goldfish / init.ranchu.rc

在init.rc中默認是opengl,那麼我們就來看看下面的邏輯:

首先實例化一個OpenGLPipeline管道,接著OpenGLPipeline作為參數實例化CanvasContext。

文件:/ frameworks / base / libs / hwui / renderthread / OpenGLPipeline.cpp

能看到在OpenGLPipeline中,實際上就是存儲了RenderThread對象,以及RenderThread中的mEglManager。透過OpenGLPipeline來控制mEglManager進而進一步操作OpenGL。

做了如下操作:

文件:/ frameworks / base / libs / hwui / renderstate / RenderState.cpp

文件:/ frameworks / base / libs / hwui / renderthread / DrawFrameTask.cpp

實際上就是保存這三對象RenderThread;CanvasContext;RenderNode。

文件:/ frameworks / base / core / jni / android_view_ThreadedRenderer.cpp

能看到實際上就是調用RenderProxy的setName方法給當前硬體渲染對象設置名字。

文件:/ frameworks / base / libs / hwui / renderthread / RenderProxy.cpp

能看到在setName方法中,實際上就是調用RenderThread的WorkQueue,把一個任務隊列設置進去,並且調用runSync執行。

能看到這個方法實際上也是調用post執行排隊執行任務,不同的是,這里使用了線程的Future方式,阻塞了執行,等待CanvasContext的setName工作完畢。

② Android中View的創建過程

我們知道在onCreate裡面View還是沒有測繪完成的。那麼什麼時候測繪完成了?答案是onResume。
通過查看源碼 我們可以看到在onCreate方法裡面調用了getWindow()方法然後在將我們的頁面塞到這個window裡面。這個window也就是PhonwWindow.

那PhoneWindow是什麼時候被創建的?
這就引出了Activity的創建流程。
那Activity是怎麼被創建的呢?
由於Activity是一個組件他是由系統使用 ActivityThread 方法去創建的。
現在我來分析下:
先來到ActivityThread類的handleLaunchActivity方法。

可以看到他去調用了Activity的performCreate方法。

現在我們終於看到onCreate方法被調用了。

這里還有個重點,在performLaunchActivity裡面去調用Activity的onCreate方法之前還去做了一件很重要的事情,這個事情在第3224行:調用了Activity的attach方法。

現在跟到Activity的attach方法:找到了我們一直找的PhoneWindow的創建。

③ android的textview怎麼初始化

你現在是把它創建出來了,但是沒有加到activity上,讓它顯示到哪兒呢。
最簡單的操作是調用setContentView(textView), 這樣,這一整個Acitivty就只顯示這個TextView了,但實際開發中肯定不這么干。

一般是把一個View加到一個Layout上。每一個Layout比如Linerlayout什麼的,都是一個GroupView,都有一個addView(View)的方式。
如果你一整個Activity都不想用find。。。那就初始一個Layout 加到 Ac上,加給layout 加view

public void onCreate(Buddle c) {
super.onCreate(c)

LinearLyaout layout = new LinearLayout(this);

setContentView(layout);

TextView tv = new TextView(this);

.......你的那堆代碼

layout.addView(tv);

}

④ Android View知識

1, View是除了Android四大組件外,最常用的東西
2,什麼是View:
View是android中所有控制項的父類,比如TextView,LinearLayout等等
其中LinearLayout繼承自控制項組ViewGroup,當然ViewGroup也是繼承自View

3,View的位置
top:左上角縱坐標
left:左上角橫坐標
right:右下角橫坐標
bottom:右下角縱坐標
如下圖:

4,view的MotionEvent和TouchSlop
4.1MotionEvent:
ACTION_DOWN:手指接觸屏幕
ACTION_MOVE:手指在屏幕上滑動
ACTION_UP:手指離開屏幕。

4.2TouchSlop
處理滑動時的過濾條件,簡單來說就是,手指在屏幕上的一次操作算不算滑動。
系統默認值:ViewConfiguration.get(context).getScaledTouchSlop()

5,getX()getY()和getRawX()和getRawY()
前兩者相對於父控制項View 後兩者相對於手機屏幕

6,VelocityTracker,GestureDetector,Scroller
6.1VelocityTracker:滑動速度,在view的ontouch事件中,查看速度
6.2 GestureDetector:手勢判斷,比如長按,點擊,雙擊等,很少用,可以用 ontouch事件來代替
6.3Scroller:彈性滑動對象,實現view的位置改變等
7,原始滑動方式
7.1:ScrollerTo和Scroller By()
實現簡單 但是只能滑動view裡面的子元素

7.2:改變view參數
實現復雜,但是如果view有交互,這種方式比較好

7.3:動畫
適用於沒有交互的,或者動畫復雜的view的滑動

8View的事件分發:
8.1:Activity-window-View
8.2:view中是從父到子,也就是從外到內,都不處理,返回給最頂級
8.3:ViewGroup默認不攔截任何事件,默認返回false
8.4:分發方法:dispatchTouchEvent,OnInterceptTouchEvent,OnTouchEvent
dispatchTouchEvent:分發
OnInterceptTouchEvent:攔截
OnTouchEvent:處理點擊事件

⑤ android:X5WebView首次初始化X5內核耗時,會產生卡頓現象的解決辦法

      集成騰訊的X5,一般都是在application中進行初始化,不過有一個現象就是第一次啟動都睡有一小會產生了UI卡頓,一開始利用IntentService進行後台線程進行初始化,但還是會產生卡頓現象,不過官方在X5 SDK的v3.6版本後添加了一個多進程的service= 設置開啟優化方案。

        如下做法:

第一種 多進程方案: 設置開啟優化方案// 在調用TBS初始化、創建WebView之前進行如下配置,以開啟優化方案HashMapmap = new HashMap();map.put(TbsCoreSettings.TBS_SETTINGS_USE_SPEEDY_CLASSLOADER, true);QbSdk.initTbsSettings(map);b) 

增加Service聲明 :在AndroidManifest.xml中增加內核首次載入時優化Service聲明; 該Service僅在TBS內核首次Dex載入時觸發並執行dex2oat任務,任務完成後自動結束;

<service  android:name="com.tencent.smtt.export.external.DexClassLoaderProviderService"

android:label="dexopt"

android:process=":dexopt"/>

第二種 多進程方案:僅Android 5.1+生效)

1、// 在調用TBS初始化、創建WebView之前進行如下配置,以開啟優化方案

HashMapmap = new HashMap();

map.put(TbsCoreSettings.TBS_SETTINGS_USE_SPEEDY_CLASSLOADER,true);

QbSdk.initTbsSettings(map);

2、) 

多線程方案策略配置// 在調用TBS初始化、創建WebView之前進行如下配置,以開啟優化方案

HashMapmap = new HashMap();

// 配置不使用多進程策略,即該方案僅在Android 5.1+系統上生效。

map.put(TbsCoreSettings.TBS_SETTINGS_USE_DEXLOADER_SERVICE, false);

QbSdk.initTbsSettings(map);

⑥ 【Android】Window/DecorView/ViewRootImpl

根據 Activity啟動流程 ,當流程進行到 ActivityThread.performLaunchActivity 的時候,會創建Activity實例,並調用其 attach 方法。在 attach 方法中,會創建Window的實例。

也就是說,在Activity實例創建之初,Window就已經創建好了。

DecorView在第一次調用 Window.getDecorView 的時候被創建。PhoneWindow類的 getDecorView 方法實現如下:

一般情況下,在 onCreate 回調中調用了 setContentView 方法,DecorView就被初始化了。
調用鏈為:
AppcompatActivity.setContentView ->
AppCompatDelegateImpl.setContentView ->
AppCompatDelegateImpl.ensureSubDecor ->
AppCompatDelegateImpl.createSubDecor ->
PhoneWindow.getDecorView ->
PhoneWindow.installDecor

最終,在 PhoneWindow.installDecor 方法中,DecorView被初始化:

另外,如果不調用 setContentView ,DecorView同樣也會在 Activity.performCreate 方法中較後的地方創建。

ViewRootImpl是在WindowManagerGlobal的 addView 方法中被初始化的,並且也是在這里與DecorView進行綁定,成為DecorView的parent。調用鏈可以追溯到 ActivityThread.handleResumeActivity 中,在 performResumeActivity 調用之後,ViewRootImpl被創建。

也就是說,ViewRootImpl是在 onResume 回調之後才進行初始化的。這可以在 onResume 中列印 getWindow().getDecorView().getParent() 證實。

在 二 中知道了, handleResumeActivity 方法最終會調用 WindowManagerGlobal.addView 方法,在這里 ViewRootImpl 被創建,並且通過 setView 方法綁定DecorView,這些都發生在onResume之後。

在ViewRootImpl的 setView 方法中,又會調用 requestLayout ,在這里就會進行這個View樹的第一次測繪。具體的方式是通過 scheleTraversals 方法向 Choreographer 發送一個預定的消息,並在下一次屏幕刷新的時候調用 doTraversal → performTraversals 方法進行ViewTree的測量、布局和繪制。

從這一點我們可以知道,在Activity的 onResume 或之前的生命流程中調用View的 getMesuredWidth 或者 getWidth 都會返回0,因為這個時候還沒有開始測量。

當View樹還沒有被測繪的時候 ,View.post會將這個Runnable發送給內部的一個消息隊列(不是系統的消息隊列)。這個消息隊列中保存的Runnable會在下一次 performTraversals 的時候被執行;調用鏈為:
ViewRootImpl.performTraversals →
DecorView.dispatchAttachedToWindow →
... →
View.dispatchAttachedToWindow →
executeActions
最終在 executeActions 方法中,Runnable對象被發送給 ViewRootImpl的內部Handler 執行。也就是說,當這個Runnable被執行的時候,已經至少經過一次測繪了,所以可以正確的獲取到View的寬高;也說明了為什麼通過 View.post 發送的Runnable會在主線中執行。

當View樹已經被測繪過了 , View.post 就會直接通過內部的 mHandler 發送消息,這個Handler是在attach的過程中跟著 AttachInfo 一起傳遞給View的,實質上就是ViewRootImpl的內部Handler。

⑦ android viewpager怎麼初始化數據

使用ViewPager的setCurrentItem (int item) 方法設置其初始顯示的頁面,
不是在其數據適配器中,而是在完成數據適配後設置。
如viewPager.setAdapter(adapter);
viewPager.setCurrentItem(3);

⑧ Android-View的創建從xml到View

基於android 29API

android中的UI主要是通過xml文件編寫,從xml文件到View是通過LayoutInflater。

LayoutInflater通過inflater方法從xml文件轉換成View,通過createViewFromTag創建View,在createViewFromTag方法中會調用tryCreateView方法進行三次攔截最終調用系統方法生成View。

其中開發者可以通過mFactory2(通過setFactory2進行賦值  )和mFactory(通過setFactory進行賦值  )  2個對象對View創建進行攔截,通過Activity的

onCreateView方法對mPrivateFactory對象進行攔截 

攔截順序為:mFactory2--mFactory--mPrivateFactory--系統生成View

當tryCreateView方法沒有返回一個View,那麼就會由系統生成View

⑨ android 在adapter類裡面怎麼初始化控制項

在getView方法內載入動態布局view(就是你想顯示的layout),然後獲取動態布局view中的組件即可。以下舉個例子:
12View view = LayoutInflater.from(context).inflate(R.layout.testlayout , null);TextView text = (TextView)view.findViewById(R.id.testTextView);
如上就可以初始化布局testlayout中id為testTextView的(TextView)組件。

⑩ android 如何初始化LayoutInflater的View裡面的文本框的值

settext(「xxx」);

熱點內容
話嘮安卓哪裡下載 發布:2025-05-19 20:27:04 瀏覽:164
瘋狂android講義光碟 發布:2025-05-19 20:12:31 瀏覽:152
安卓手機怎麼下載圈點 發布:2025-05-19 20:08:11 瀏覽:473
文件夾粉碎不了 發布:2025-05-19 20:05:41 瀏覽:244
安卓怎麼把軟體放進全局 發布:2025-05-19 20:03:55 瀏覽:688
安卓手機如何看最真實的型號 發布:2025-05-19 19:58:59 瀏覽:12
U盤超級加密2008 發布:2025-05-19 19:44:32 瀏覽:457
燈帶編程軟體 發布:2025-05-19 19:32:30 瀏覽:288
如何判斷伺服器被多少人訪問 發布:2025-05-19 19:27:45 瀏覽:126
編程stata 發布:2025-05-19 19:12:18 瀏覽:517