Android Framework原理

App啟動過程

App啟動過程
  1. 點擊桌面App圖標,Launcher進程采用Binder IPC向system_server進程發起startActivity請求
  2. system_server進程接收到請求后,向zygote進程發送創建進程的請求
  3. Zygote進程fork出新的子進程,即App進程
  4. App進程,通過Binder IPC向sytem_server進程發起attachApplication請求
  5. system_server進程在收到請求后,進行一系列準備工作后,再通過binder IPC向App進程發送scheduleLaunchActivity請求
  6. App進程的binder線程(ApplicationThread)在收到請求后,通過handler向主線程發送LAUNCH_ACTIVITY消息
  7. 主線程在收到Message后,通過發射機制創建目標Activity,并回調Activity.onCreate()等方法
  8. App便正式啟動,開始進入Activity生命周期,執行完onCreate/onStart/onResume方法,UI渲染結束后便可以看到App的主界面

Android開機流程

Android開機流程

BootLoader引導

當按開機鍵的時候,引導芯片開始從固化在ROM的預設代碼開始執行,然后加載引導程序到RAM
BootLoader,又稱為引導程序。它是在操作系統運行之前運行的一段程序
BootLoader負責初始化軟件運行所需要的最小硬件環境,最后加載內核到內存

啟動Kernel

這個入口的函數是start_kernel函數
start_kernel函數執行到最后調用了reset_init函數進行后續的初始化
start_kernel最終啟動用戶空間的init程序

啟動Android

當初始化內核之后,init進程負責解析init.rc配置文件, 就會啟動一個相當重要的祖先進程,也就是init進程,在Linux中所有的進程都是由init進程直接或間接fork出來的

* /system/bin/app_process_Zygote服務啟動的進程名
* --start-system-server 表明Zygote啟動完成后,需要啟動System_Server進程
* socket zygote stream 666在Zygote啟動時,創建一個權限為666的socket。此socket用來請求zygote創建新進程
* socket的fd保存在名稱為"ANDROID_SOCKET_zygote"的環境變量中

init進程負責創建系統中最關鍵的幾個核心daemon(守護)進程,尤其是zygote和System_Server進程

1. zygote進程 android啟動的第一個Dalvik 虛擬機,它將負責啟動Java世界的進程
2. zygote虛擬機啟動子進程system_server,同時也可以看出zygote中定義了一個Socket,綁定666端口,用于接收ActivityManagerService啟動應用程序的請求
3. System_Server進程  Binder通信的基礎,它還提供了property service(屬性服務),類似于windows系統的注冊表服務
4. 系統里面重要的服務都是在這個進程里面開啟的,例如AMS, WindowsManager, PackageManagerService等等都是由這個System_Server fork出來的
5. 在System_Server進程開啟的時候,就會初始化ActivityManagerService 。同時,會加載本地系統的服務庫,調用createSystemContext()創建系統上下文,創建ActivityThread及開啟各種服務等等
6. system_server中開啟了核心系統服務,并將系統服務添加到ServiceManager中,然后系統進入SystemReady狀態

啟動Home Activity

  1. 在systemReady狀態,ActivityManagerService會與zygote的Socket通信,請求啟動Home
  2. zygote收到AMS的連接請求后,執行runSelectLoopMode處理請求
  3. zygote處理請求會通過forkAndSpecialize啟動新的應用進程,并最終啟動Home
概況
1. 系統加電,執行bootloader。Bootloader負責初始化軟件運行的最小硬件環境,最后加載內核到內存
2. 內核加載到內存后,進入內核引導階段,在內核引導的最后,調用start_kernel進入內核啟動階段。start_kernel最終啟動用戶空間的init程序
3. init負責解析init.rc配置文件,開啟系統守護進程。2個最重要的守護進程是zygote進程和serverManager進程。zygote是android啟動的第一個Dalvik虛擬機,ServiceManager服務是Binder通信的基礎
4. zygote虛擬機啟動子進程system_server,在system_server中啟動了核心系統服務,并將系統服務添加到ServiceManager中,然后系統進入SystemReady狀態
5. 在SystemReady狀態,ActivityManagerService與zygote中的socket通信,通過zygote啟動home應用,進入系統界面

* 從步驟3開始, init啟動后,上層的實現
1. init啟動的核心Daemon服務包括Android的第一個Dalvik虛擬機Zygote
2. zygote定義一個socket,用于接受ActivityManangerService啟動應用的請求
3. zygote通過fork系統調用創建system_server進程
4. 在system_server進程中,將會啟動系統核心服務以及其他服務
5. 系統服務啟動后會注冊到ServiceManager中,用于Binder通信
6. ActivityManagerService進入systemReady狀態
7. 在systemReady狀態,ActivityManangerService會與zygote的Socket通信,請求啟動Home
8. zygote收到AMS的連接請求后,執行runSelectLoopMode處理請求
9. zygote處理請求會通過forkAndSpecialize啟動新的應用進程,并最終啟動Home

Handler機制與底層實現原理

概念

Message - Message代表一個行為what或者一串動作Runnable, 每一個消息在加入消息隊列時,都有明確的目標Handler
ThreadLocal - 線程本地存儲區(Thread Local Storage,簡稱為TLS),每個線程都有自己的私有的本地存儲區域,不同線程之間彼此不能訪問對方的TLS區域。ThreadLocal的作用是提供線程內的局部變量TLS,這種變量在線程的生命周期內起作用,每一個線程有他自己所屬的值(線程隔離)
MessageQueue (C層與Java層都有實現) - 以隊列的形式對外提供插入和刪除的工作, 其內部結構是以雙向鏈表的形式存儲消息的
Looper (C層與Java層都有實現) - Looper是循環的意思,它負責從消息隊列中循環的取出消息然后把消息交給Handler處理
Handler - 消息的真正處理者, 具備獲取消息、發送消息、處理消息、移除消息等功能

普通的線程是沒有looper的,如果需要looper對象,那么必須要先調用Looper.prepare方法,而且一個線程只能有一個looper
Handler是如何完成跨線程通信的

* Android中采用的是Linux中的管道通信
* 關于管道,簡單來說,管道就是一個文件
* 在管道的兩端,分別是兩個打開文件文件描述符,這兩個打開文件描述符都是對應同一個文件,其中一個是用來讀的,別一個是用來寫的
* 消息隊列創建時,會調用JNI函數,初始化NativeMessageQueue對象。NativeMessageQueue則會初始化Looper對象
* Looper的作用就是,當Java層的消息隊列中沒有消息時,就使Android應用程序主線程進入等待狀態,而當Java層的消息隊列中來了新的消息后,就喚醒Android應用程序的主線程來處理這個消息

整個消息機制流程


image
Handler通過sendMessage()發送Message到MessageQueue隊列
Looper通過loop(),不斷提取出達到觸發條件的Message,并將Message交給target來處理
經過dispatchMessage()后,交回給Handler的handleMessage()來進行相應地處理
將Message加入MessageQueue時,處往管道寫入字符,可以會喚醒loop線程;如果MessageQueue中沒有Message,并處于Idle狀態,則會執行IdelHandler接口中的方法,往往用于做一些清理性地工作

ContentProvider原理

Android繪制原理

Activity的window組成,Activity內部有個Window成員,它的實例為PhoneWindow,PhoneWindow有個內部類是DecorView,這個DecorView就是存放布局文件的,里面有TitleActionBar和我們setContentView傳入進去的layout布局文件


image
  1. Window類時一個抽象類,提供繪制窗口的API
  2. PhoneWindow是繼承Window的一個具體的類,該類內部包含了一個DecorView對象,該DectorView對象是所有應用窗口(Activity界面)的根View
  3. DecorView繼承FrameLayout,里面id=content的就是我們傳入的布局視圖
  4. ContentView必須是一個ViewGroup
  5. ViewGroup 開始遞歸執行以下邏輯進行繪制
+ measure, 遞歸測量view的大小。有3種測量模式
    - MeasureSpec.EXACTLY表示確定大小
    - MeasureSpec.AT_MOST表示最大大小
    - MeasureSpec.UNSPECIFIED不確定
+ layout,遞歸布局view的位置
+ draw,遞歸繪制view
    - ViewRootImpl中的代碼會創建一個Canvas對象,然后調用View的draw()方法來執行具體的繪制

AsyncTask源碼分析

Binder機制及底層實現

進程空間分配

  1. 進程間,用戶空間的數據不可共享,所以用戶空間 = 不可共享空間
  2. 進程間,內核空間的數據可共享,所以內核空間 = 可共享空間
  3. 進程內用戶與內核進行交互稱為系統調用

Binder跨進程通信(IPC)的原理


image
  1. 先通過進程間的內核空間進行數據交互
  2. 再通過進程內的用戶空間&內核空間進行數據交互,從而實現進程間的用戶空間的數據交互
  3. 而Binder,就是充當連接兩個進程(內核空間)的通道

使用步驟:
注冊服務

* Server進程向Binder驅動發起服務注冊請求
* Binder驅動將注冊請求轉發給ServiceManager進程
* ServiceManager進程添加該服務
* 此時ServiceManager進程擁有該服務信息

獲取服務

* Client向Binder驅動發起獲取服務的請求,傳遞要獲取的服務名稱(service name)
* Binder驅動將該請求轉發給ServiceManager進程
* ServiceManager查找到Client需要的Server對應的服務信息
* 通過Binder驅動將上述服務信息返回給Client進程
* 此時client進程與server進程已經建立了連接

使用服務

* Client進程將參數數據發到Server進程
    1. client 進程將需要的傳送的數據放到client進程的共享內存;(當前線程被掛起)
    2. Binder驅動從client的共享內存中讀取數據,并根據ServiceManager進程里面的Server信息找到對應的Server進程
    3. Binder驅動將數據copy到Server進程的共享內存里,并通知Server進程解包
* Server進程根據Client進程要求,調用目標方法
    1. 接到Binder驅動通知后,Server進程從線程池中取出線程,進行數據解包和調用目標方法
    2. 將最終方法結果寫到自己的共享內存
* Server進程將目標方法的結果,返回給Client進程
    1. Binder驅動程序將Server進程的共享內存里面的數據(方法執行結果) copy 到client進程的共享內存
    2. 通知client進程獲得返回結果(此時client進程之前被掛起的線程被重新喚醒)
image

Client進程、Server進程 & Service Manager 進程之間的交互 都必須通過Binder驅動(使用 open 和 ioctl文件操作函數),而非直接交互
Client進程、Server進程 & Service Manager進程屬于進程空間的用戶空間,不可進行進程間交互
Binder驅動 屬于 進程空間的 內核空間,可進行進程間 & 進程內交互


image

Binder驅動 & Service Manager進程 屬于 Android基礎架構(即系統已經實現好了);而Client 進程 和 Server 進程 屬于Android應用層(需要開發者自己實現)


image

ActivityThread工作原理

Window 、WMS的工作原理

ThreadLocal原理,實現及如何保證Local屬性

每個Thread維護一個ThreadLocalMap映射表,這個映射表的key是ThreadLocal實例本身,value是真正需要存儲的Object

Android線程有沒有上限

android本身就是linux系統 所以查看命令和linux一樣

Android內存限制

dalvik.vm.heapstartsize 表示 初始內存大小是8m
dalvik.vm.heapgrowthlimit 表示標準內存大小是96m 一般應用都是這么大
dalvik.vm.heapsize 表示 在manifest配置文件中application標簽下配置 android:largeHeap="true"時的內存大小

Android apk大小限制

apk安裝包大小理論上沒有限制。但是各個應用商店為了有大小限制google play 要求小于50M , 擴展包可以擴展到2g
壓測: 800M的apk是沒問題的只是安裝的時間比較長。1.6G的apk包則把手機弄死機

線程池有沒有上限

ThreadPoolExecutor構造函數的maximumPoolSize決定

AndroidToast原理分析

Art和Dalvik對比

ART 的機制與 Dalvik 不同。在Dalvik下,應用每次運行的時候,字節碼都需要通過即時編譯器(just in time ,JIT)轉換為機器碼,這會拖慢應用的運行效率,而在ART 環境中,應用在第一次安裝的時候,字節碼就會預先編譯成機器碼,使其成為真正的本地應用。這個過程叫做預編譯(AOT,Ahead-Of-Time)。這樣的話,應用的啟動(首次)和執行都會變得更加快速。

BlockCanaryEx原理

即整個應用的主線程,只有這一個looper,不管有多少handler,最后都會回到這里

public static void loop() {
    ...

    for (;;) {
        ...

        // This must be in a local variable, in case a UI event sets the logger
        Printer logging = me.mLogging;
        if (logging != null) {
            logging.println(">>>>> Dispatching to " + msg.target + " " +
                    msg.callback + ": " + msg.what);
        }

        msg.target.dispatchMessage(msg);

        if (logging != null) {
            logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
        }

        ...
    }
}

設置Printer對象,判斷是否超過預期時間,notifyLog

Android GC探究

虛擬機原理,如何自己設計一個虛擬機(內存管理,類加載,雙親委派)

對于Activity 的 onCreate 等生命周期的函數為什么不會因為 Looper.loop()里的死循環卡死而永無機會執行

View 的繪制到底是怎樣完成的,它又為什么不會因為 Looper.loop()里的死循環卡死而永無機會刷新

子線程真的不能刷新UI

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 作為一個在三里屯上班的普通青年,雖然坐擁各地美食,可是中午有限的時間,想要用來吃一頓豐盛的午宴,也是不太夠的。 理...
    帕克兒閱讀 439評論 0 0
  • 大學需要制定一些目標來證明自己是一個有夢想,有奔頭,有上進心的好青年。 目標一:深入學習Java,在進一步學習Ja...
    李霖神谷閱讀 138評論 0 1
  • 無論是在工作和生活中,我們每個人都要與他人交往,無論是親密的伴侶,熟悉的親戚還是友好的朋友,同事,亦或是只有一面或...
    DQJY閱讀 432評論 2 0
  • 一 無所事事時教小可認字,就從最簡單的“一二三四五”開始,這幾個字斷斷續續教了一個多月,他熟悉七七八八了。 我以為...
    莫摘花的詩詞情懷閱讀 415評論 6 3
  • 太陽逐漸向遠方的山頂靠攏,我慢悠悠的走在這條先輩們用石頭鋪的通往田間的小路上。小路的兩邊是一塊塊不規則的田地,田...
    戲之冰旋律閱讀 536評論 4 2