Android 內核基本知識


Android基本知識

 

Android基本知識.... 1

1. 各版本系統特性.... 1

2. View繪制流程.... 2

3. 動畫體系.... 2

4. 事件分發機制.... 3

輸入消息獲取.... 3

1. 鍵消息總體派發流程.... 3

視圖內部派發流程.... 4

觸摸消息派發流程.... 4

視圖內部消息派發流程.... 4

ViewGroup內部消息派發過程.... 4

View消息檢測機制.... 4

5. 常見UI 布局優化方式.... 4

6. 常見性能優化方式.... 5

7. 網絡通信機制及訪問策略,TCP/IP、 HTTP協議.... 6

8. Android消息機制, 多線程.... 7

9. 熟悉SQL,常見本地化存儲的優化.... 8

10. JNI 和 NDK.. 8

11. HTML5 和 JS. 8

13. Java Server端.... 8

14. Linux & Kenel 8

15. Framework概述.... 9

1. 服務端.... 9

2. 客戶端.... 9

3. Linux驅動.... 9

Apk的整個運行過程.... 9

客戶端中的線程.... 10

 

 

 

1. 各版本系統特性

Android 5.0十大新特性

1、全新 Material Design 設計風格

2、支持多種設備(智能手機、平板電腦、筆記本電腦、智能電視、汽車、智能手表)

3、全新的通知中心設計 SystemUI Notification

4、支持 64 位 ART 虛擬機(放棄Dalvik虛擬機,改用了ART模式)

5、Project Volta 電池續航改進計划

6、全新的“最近應用程序”

7、改進安全性,防止繞過鎖屏, 開啟系統數據加密

8、不同數據獨立保存 Knox多用戶

9、改進搜索(Google Search 將會更好的意識到用戶正在做什么)

10、新的 API 支持,藍牙 4.1、USB Audio、多人分享等其它特性

 

Android 4.4系統新特性

1. 新的撥號和智能來電顯示

2. 針對RAM的優化

3. 新圖標、鎖屏、啟動動畫和配色方案

4. 加強主動式語音功能

5. 集成Hangouts IM軟件

6. 全屏模式

7. 支持Emoji鍵盤

8. 輕松訪問在線存儲

9. 無線打印

10. 屏幕錄像功能

11. 內置字幕管理功能

12. 計步器應用

13. 低功耗音頻和定位模式

14. 新的接觸式支付系統(通過NFC和智能卡)

15. 新的藍牙配置文件和紅外兼容性

 

2. View繪制流程

1. View的狀態

 

2. 導致View重繪的因素

視圖或子視圖: 大小 有變化,位置有變化, 可見性 有變化

這三種變化 最終都會調用 invalidate()requestLayout()requestFocus()這三個方法

這三個方法 最終調用 ViewRoot的 schueduleTraversals(),該函數發一個異步消息 調用 performTraversals() 開始對整個View重新遍歷

 

 

 

 

 

流程一:mesarue()過程

 

        主要作用:為整個View樹計算實際的大小,即設置實際的高(對應屬性:mMeasuredHeight)和寬(對應屬性:

  mMeasureWidth),每個View的控件的實際寬高都是由父視圖和本身視圖決定的。

 

     具體的調用鏈如下:

          ViewRoot根對象地屬性mView(其類型一般為ViewGroup類型)調用measure()方法去計算View樹的大小,回調

View/ViewGroup對象的onMeasure()方法,該方法實現的功能如下:   

         1、設置本View視圖的最終大小,該功能的實現通過調用setMeasuredDimension()方法去設置實際的高(對應屬性: 

                mMeasuredHeight)和寬(對應屬性:mMeasureWidth)   ;

         2 、如果該View對象是個ViewGroup類型,需要重寫該onMeasure()方法,對其子視圖進行遍歷的measure()過程。

             

               2.1  對每個子視圖的measure()過程,是通過調用父類ViewGroup.java類里的measureChildWithMargins()方法去

          實現,該方法內部只是簡單地調用了View對象的measure()方法。(由於measureChildWithMargins()方法只是一個過渡

          層更簡單的做法是直接調用View對象的measure()方法)。

 

流程二、layout布局過程:

 

     主要作用 :為將整個根據子視圖的大小以及布局參數將View樹放到合適的位置上。

     具體的調用鏈如下:

       host.layout()開始View樹的布局,繼而回調給View/ViewGroup類中的layout()方法。具體流程如下

        1 、layout方法會設置該View視圖位於父視圖的坐標軸,即mLeft,mTop,mLeft,mBottom(調用setFrame()函數去實現) 接下來回調onLayout()方法(如果該View是ViewGroup對象,需要實現該方法,對每個子視圖進行布局) ;

       2、如果該View是個ViewGroup類型,需要遍歷每個子視圖chiildView,調用該子視圖的layout()方法去設置它的坐標值。

          layout函數原型為 ,位於View.java

流程三、draw()繪圖過程

     由ViewRoot對象的performTraversals()方法調用draw()方法發起繪制該View樹,值得注意的是每次發起繪圖時,並不

  會重新繪制每個View樹的視圖,而只會重新繪制那些“需要重繪”的視圖,View類內部變量包含了一個標志位DRAWN,當該視圖需要重繪時,就會為該View添加該標志位。

   調用流程 :

     mView.draw()開始繪制,draw()方法實現的功能如下:

          1 、繪制該View的背景

          2 、為顯示漸變框做一些准備操作(見5,大多數情況下,不需要改漸變框)         

          3、調用onDraw()方法繪制視圖本身   (每個View都需要重載該方法,ViewGroup不需要實現該方法)

          4、調用dispatchDraw()方法繪制子視圖(如果該View類型不為ViewGroup,即不包含子視圖,不需要重載該方法)  值得說明的是,ViewGroup類已經為我們重寫了dispatchDraw ()的功能實現,應用程序一般不需要重寫該方法,但可以重載父類函數實現具體的功能。

            4.1 dispatchDraw()方法內部會遍歷每個子視圖,調用drawChild()去重新回調每個子視圖的draw()方法(注意,這個

地方“需要重繪”的視圖才會調用draw()方法)。值得說明的是,ViewGroup類已經為我們重寫了dispatchDraw()的功能

實現,應用程序一般不需要重寫該方法,但可以重載父類函數實現具體的功能。

 

invalidate()方法

 

   說明:請求重繪View樹,即draw()過程,假如視圖發生大小沒有變化就不會調用layout()過程,並且只繪制那些“需要重繪的”

視圖,即誰(View的話,只繪制該View ;ViewGroup,則繪制整個ViewGroup)請求invalidate()方法,就繪制該視圖。

 

     一般引起invalidate()操作的函數如下:

        1、直接調用invalidate()方法,請求重新draw(),但只會繪制調用者本身。

        2、setSelection()方法 :請求重新draw(),但只會繪制調用者本身。

        3、setVisibility()方法 : 當View可視狀態在INVISIBLE轉換VISIBLE時,會間接調用invalidate()方法,繼而繪制該View。

        4 、setEnabled()方法 : 請求重新draw(),但不會重新繪制任何視圖包括該調用者本身。

 

requestLayout()方法 :會導致調用measure()過程 和 layout()過程 。

 

    說明:只是對View樹重新布局layout過程包括measure()和layout()過程,不會調用draw()過程,但不會重新繪制

任何視圖包括該調用者本身。

    一般引起invalidate()操作的函數如下:

         1、setVisibility()方法: 當View的可視狀態在INVISIBLE/ VISIBLE 轉換為GONE狀態時,會間接調用requestLayout() 和invalidate方法。同時,由於整個個View樹大小發生了變化,會請求measure()過程以及draw()過程,同樣地,只繪制需要“重新繪制”的視圖。

 

requestFocus()函數說明

      說明:請求View樹的draw()過程,但只繪制“需要重繪”的視圖。

 

 

 

 

 

3. 動畫體系

 

 

 

 

4. 事件分發機制

輸入消息獲取

 

 

InputReader和InputDispatcher 運行在系統進程system_process中

InputReader 持續調用 輸入設備驅動

InputDispatcher 從消息隊列中獲取消息,

    ① 直接派發到 客戶端窗口

    ② 派發送WmS,WmS經過一定的處理。若WmS沒有處理,再派發到客戶端窗口

 

App創建Window時, 本地創建ViewRoot對象,再通過IPC調用WmS的Session addWindow()。WmS會把Window的相關信息保存在InputMonitor中;再用InputManager把Window信息傳遞給InputDispatcher,同時保存在NativeInputManager中。

 

事件分發機制

窗口內部的默認處理邏輯,叫做 View系統

消息分類: 觸摸消息,按鍵消息

 

1. 鍵消息總體派發流程

ViewRoot中定義了InputHandler對象,handleKey() -> dispatchKey() 發送一個異步DISPATCH_KEY消息,在deliverKeyEvent() 中處理:

(1)調用 mView.dispatchKeyEventPreIme(), 在輸入法窗口之前處理該事件,

       需要重載該函數,若返回true,則先調用finishInputEvent() 報告WmS

(2)若輸入法窗口存在,把消息 派發到 輸入法窗口

(3)調用deliverKeyEventToViewHierarchy(),將消息派發給真正的視圖

① checkForLeavingTouchModeAndConsume(),是否離開觸摸模式

② mView.dispatchKeyEvent()發給根視圖,進入View樹

應用程序,根視圖 即 DecorView

非應用程序,根視圖 ViewGroup的實現

③ 若App沒有處理該消息, 則默認去判斷該消息對View樹焦點的影響

 

視圖內部派發流程

① 回調Activity包含的Window對象

② 將消息派發給 DecorView

③ View內部的 onKeyDown 和 onKeyUp

④ Activity的 onKeyDown 和 onKeyUp

⑤ 按鍵消息沒有被消耗掉,調PhoneWindow的 onKeyDown 和 onKeyUp

 

 

2. 觸摸消息派發流程

按鍵消息 要先經過WmS的處理再發送給客戶端窗口

觸摸消息 則直接派發到客戶端窗口

(1) 物理屏幕像素 到 系統定義的邏輯像素的 轉換

(2) 如果是DOWN,調用ensureTouchMode(true) 進入觸摸模式

(3) 屏幕坐標 轉化為 視圖坐標。對於根視圖X軸沒有滾動,寬度為屏幕寬度

(4) mView.dispatchTouchEvent() 將消息 派發到 根視圖 以及 整個View樹

應用窗口: 根視圖 == DecorView

非應用窗口: 根視圖 == ViewGroup

(5) 視圖 沒有消耗掉消息, 處理屏幕邊界偏移

 

視圖內部消息派發流程

① mView.dispatchTouchEvent() 調用Window對象PhoneWindow的superDispatchTouchEvent() –> DecorView的superDispatchTouchEvent -> DecorView中沒有Callback則:ViewGroup的 dispatchTouchEvent

② 若在PhoneWindow中沒有被消耗,調用onTouchEvent

ViewGroup內部消息派發過程

采用遞歸方式,首先 把消息 派發給View樹中的最后一個子視圖

若子視圖沒有消耗掉消息,才遞歸派發給父視圖

View消息檢測機制

三種檢測: pre-pressed,pressed,長按

原理: 利用Handler發送異步延遲消息。根據觸摸時間判斷

檢測過程在View的onTouchEvent中實現,如果我們重載了該函數,但沒有調用super.onTouchEvent(),三種消息回調就不會執行。 也就是onLongClick不會被執行

 

 

 

 

5. 常見UI 布局優化方式

視圖和布局優化 - 減少視圖層級結構,重用layout代碼,減少不必要的資源占用

http://www.open-open.com/lib/view/open1398175135593.html

 

1、RelativeLayout 優於 LinearLayout

針對大量Items時,通過Hierarchy Viewer查看兩種布局方案的View層級圖,RelativeLayout明顯優於LinearLayout

2、UI資源的復用

<viewStub/>,<merge/>和<include/>

 

(1) <viewstub> 延遲加載。

viewstub是一個不可見的,大小為0的View,最佳用途就是實現View的延遲加載

被<viewStub/>標簽所包裹的Views在默認狀態下不會占用任何內存空間(visibility=GONE),通過方法inflate()來召喚系統加載其內部的Views。

(2) <merge/> :該標簽在優化UI結構時起到很重要的作用,目的是通過刪減多余或者額外的層級

<merge>也有一些使用限制: 只能用於xml layout文件的根元素

(3) <include> 重用layout代碼

如果在某個布局里面需要用到另一個相同的布局設計,可以通過<include> 標簽來重用layout代碼:

(4) layoutopt (Layout Optimization工具)

這工具可以分析所提供的Layout,並提供優化意見。在tools文件夾里面可以找到layoutopt.bat。

 

性能優化- 背景和圖片優化,延遲加載

Drawing(繪制的刷新率)

Startup Time (啟動Activities的時間)

getWindow().setBackgroundDrawable()方法來設置DecorView的background drawable

通過Theme定義Window’s background的圖片,從而提高Activity的啟動速度

 

Adapter優化:

對ListView優化就是對Adapter中的getView方法進行優化

留意getView里面的代碼,要判斷convertView是否為null,以便重用,減少對象的創建,減少內存占用

◆內存分配優化

 

6. 常見性能優化方式

1.采用硬件加速,在androidmanifest.xml中application添加  android:hardwareAccelerated="true"。不過這個需要在android 3.0才可以使用。

2. View 中設置緩存屬性. setDrawingCachetrue.

3. 優化你的布局。通過Android sdk中tools目錄下的layoutopt 命令查看你的布局是否需要優化。

4. 動態加載View. 采用ViewStub 避免一些不經常的視圖長期握住引用.

5.將Acitivity 中的Window 的背景圖設置為空。getWindow().setBackgroundDrawable(null); android的默認背景是不是為空。

6. 采用<merge> 優化布局層數。 采用<include >來共享布局。

7. 查看Heap 的大小

8. 利用TraceView查看跟蹤函數調用。有的放矢的優化。

9. cursor 的使用。不過要注意管理好cursor,不要每次打開關閉cursor.因為打開關閉Cursor非常耗時。Cursor.require用於刷新cursor.

10.采用環形Buffer(可以采用鏈表數據結構實現)。可以設置一個鏈表長度的上限,根據手勢的變化來不斷地更新環形Buffer的內容。

11.采用SurfaceView在子線程刷新UI, 避免手勢的處理和繪制在同一UI線程(普通View都這樣做)。

12.采用JNI,將耗時間的處理放到c/c++層來處理。

13.有些能用文件操作的,盡量采用文件操作,文件操作的速度比數據庫的操作要快10倍左右

14. 懶加載和緩存機制。訪問網絡的耗時操作啟動一個新線程來做,而不要再UI線程來做。

 

Android性能優化典范 http://www.open-open.com/lib/view/open1421723359718.html

 

主要從三個方面展開: Android的渲染機制,內存與GC,電量優化

 

1. Android的渲染機制

大多數用戶感知到的卡頓等性能問題的最主要根源都是因為渲染性能

 

大多數操作都必須在16ms內完成, 以便達到流暢的畫面所需要的60fps

用HierarchyViewer來查找Activity中的布局是否過於復雜,

也可以使用手機設置里 面的開發者選項,打開Show GPU Overdraw等選項進行觀察。

還可以使用TraceView來觀察CPU的執行情況,更加快捷的找到性能瓶頸。

 

2. 過度繪制

屏幕上的某個像素在同一幀的時間內被繪制了多次

打開Show GPU Overdraw的選項

    Overdraw有時候是因為你的UI布局存在大量重疊的部分

 

3. 內存與GC

Android系統里面有一個Generational Heap Memory的模型,系統會根據內存中不同 的內存數據類型分別執行不同的GC操作。

例如,最近剛分配的對象會放在Young Generation區域,這個區域的對象通常都是會快速被創建並且很快被銷毀回收的,

同時這個區域的GC操作速度也是比Old Generation區域的GC操作速度更快的。

 

執行GC操作的時候,任何線程的任何操作都會需要暫停,等待GC操作完成之后,其他操作才能夠繼續運行。

單個的GC並不會占用太多時間,但是大量不停的GC操作則會顯著占用幀間隔時間(16ms)。

 

VM的回收機制給開發人員帶來很大的好處,不用時刻處理對象的分配與回收,可以更加專注於更加高級的代碼實現

但是在一個龐大的系統當中,還是免不了經常發生部分對象忘記回收的情況,這就是內存泄 漏。

 

內存泄漏指的是那些程序不再使用的對象無法被GC識別,這樣就導致這個對象一直留在內存當中,占用了寶貴的內存空間。

顯然,這還使得每級Generation的內存區域可用空間變小,GC就會更容易被觸發,從而引起性能問題。

 

 

為了尋找內存的性能問題,Android Studio提供了工具來幫助開發者。

 

Memory Monitor:查看整個app所占用的內存,以及發生GC的時刻,短時間內發生大量的GC操作是一個危險的信號。

Allocation Tracker:使用此工具來追蹤內存的分配,前面有提到過。

Heap Tool:查看當前內存快照,便於對比分析哪些對象有可能是泄漏了的,請參考前面的Case。

 

4. 電量優化

千萬不能讓你的應用成為消耗電量的大戶。

我們應該盡量減少喚醒屏幕的次數與持續的時間,使用WakeLock來處理喚醒的問題,能夠正確執行喚醒操作並根據設定及時關閉操作進入睡眠狀態。

某些非必須馬上執行的操作,例如上傳歌曲,圖片處理等,可以等到設備處於充電狀態或者電量充足的時候才進行。

觸發網絡請求的操作,每次都會保持無線信號持續一段時間,我們可以把零散的網絡請求打包進行一次操作,避免過多的無線信號引起的電量消耗

 

Battery History Tool,它可以查看程序被喚醒的頻率,又誰喚醒的,持續了多長的時間,這些信息都可以獲取到。

 

7. 網絡通信機制及訪問策略,TCP/IP、 HTTP協議

TCP/IP 

      Transmission Control Protocol 傳輸控制協議 

      Internet Protocol 互聯網協議 

UDP 

      User Datagram Protocol 用戶數據協議 

Socket 是程序與網絡間的一種接口,大部分網絡應用程序都是點對點的,所謂點就是服務器端和客戶端所執行的程序。 Socket 是用來接收和傳送分組的一個端點。

 

超文本傳輸協議(HTTP,HyperText Transfer Protocol)是互聯網上應用最為廣泛的一種網絡協議。所有的WWW文件都必須遵守這個標准。設計HTTP最初的目的是為了提供一種發布和接收HTML頁面的方法。

 

 

 

 

 

 

8. Android消息機制, 多線程

Android消息機制

Handler, Looper,MessageQueue

 

Looper – 一個線程只能有一個Looper

Looper.prepare()創建消息隊列MessageQueue,

然后調用Looper.loop()進入消息循環.

Looper.prepare() 只能執行一次

 

MessageQueue – 一個線程只能有一個MessageQueue

采用排隊的方式對消息進行處理

 

Handler – 只能添加到有MessageQueue的線程中,一個線程可以有多個handler

Handler的sendMessage() 函數 發送消息

Handler的handleMessage() 函數對消息進行處理

 

 

 

 

多線程

1. 普通線程

執行完run()方法內的代碼后線程就結束

 

2. 異步線程

線程啟動后 進入一個 死循環, 每循環一次,處理MessageQueue中的一個消息

如果MessageQueue為空,線程會暫停

使用條件: 任務要常駐,要根據消息不同執行不同操作

 

 

 

 

9. 熟悉SQL,常見本地化存儲的優化

 

10. JNI 和 NDK

 

11. HTML5 和 JS

 

13. Java Server端

 

14. Linux & Kenel

 

15. Framework概述

1. 服務端

WmS:管理個窗口(實際是View或ViewGroup)的疊放次序, 隱藏或顯示

    KeyQ類:WmS內部類,不斷讀取用戶UI操作消息,放入消息隊列QueueEvent中

AmS:管理所有應用程序的Activity

 

2. 客戶端

ActivityThread: App主線程類,所有apk有且僅有一個該類,該類的static main()是App入口。 ActivityThread所在的線程即為UI線程。

Activity: ActivityThread會根據用戶操作 動態選擇加載那個Activity對象

 

PhoneWindow: 包含一個DecorView(父類FrameLayout),並提供一組窗口操作API

Window: 提供一組窗口操作API, 並不是WmS管理的窗口

DecorView: FrameLayout子類,相對普通的FrameLayout做了一定的修飾

 

ViewRoot:繼承Handler,將WmS的IPC調用 轉化為本地的異步調用

ViewRoot.W:繼承於Binder。WmS通知客戶端窗口時,是通過IPC調用,也就是調用該Binder。

              然后,該Binder內部處理函數會給 所在的ViewRoot發一個Handler消息,以便異步處理

 

WindowManager: 客戶端 創建窗口時向它申請,由它與WmS交互

 

3. Linux驅動

與Framework相關的:

① SurfaceFlingger:把各個Surface顯示在同一個屏幕上

② Binder:提供跨進程消息傳遞

 

Apk的整個運行過程

1. ActivityThread.main() -> prepareMainLooper(): UI線程創建MessageQueue 和 ActivityThread對象

ActivityThread對象初始化 -> 創建愛H(Handler) 和 ApplicationThread(Binder)對象。 Binder接收AmS中的IPC調用后,發送handler消息到MessageQueue, UI線程異步讀取消息並進行相應操作(start,stop,pause)

2. UI線程 執行Looper.loop() 循環不斷的讀取、處理消息

 

AmS發送start Activity消息 –> ActivityThread 接收, 創建PhoneWindow類 -> DecorView類 -> 相應的View或ViewGroup。 創建完成后 –>

調用WindowManager -> 創建ViewRoot對象(包含W類)-> 調用WmS提供的遠程接口添加窗口並顯示到屏幕

 

用戶在界面上操作 -> KeyQ把用戶消息 存在QueueEvent中 -> InputDispatcherThread 逐個取出消息 並調用WmS相應函數處理

 

WmS發現消息屬於哪個窗口時,調用相應的ViewRoot.W類,-> W 將消息傳給ViewRoot -> ViewRoot把消息傳給UI線程ActivityThread –> ActivityThread做相應處理

 

客戶端中, 消息處理順序 DecorView -> 內部View 或 ViewGroup -> PhoneWindow –> Activity

 

 

 

客戶端中的線程

每個Binder對應一個線程

Activity啟動后 ①創建一個ViewRoot.W 對象, ②ActivityThread會創建一個ApplicationThread對象,這兩Binder負責接收Linux Binder驅動和發送IPC調用

程序本身所在的線程即UI線程,所有處理用戶消息和繪制界面 都由它完成。

一個APK只有一個UI線程

 

UI線程: 從ActivityThread運行,在該類的main()方法中 使用Looper.prepareMainLooper()為該線程添加Looper對象(包含MessageQueue)。所以我們可以直接定義Handler

普通線程:裸線程,沒有Loop,不能直接定義Handler

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM