基於問題學習安卓系統架構

2020-08-12 10:25:38

基於問題學習安卓系統架構

1 原理

本節要瞭解、弄懂安卓系統架構的原理,首先將安卓系統架構完全分析清楚,再根據下載好的Android8的原始碼,將原始碼目錄中的內容與系統架構聯合在一起,最後總結爲什麼安卓系統架構要這麼設計。

1.1 什麼是安卓系統架構

安卓系統架構,就是從底層硬體到高層軟體的一整套技術體系,使用者使用的行動端裝置如果使用安卓系統搭建,那麼使用者就可以使用安卓平臺上的應用,安卓系統架構圖如下:

在这里插入图片描述

從頂層到底層依次是:APP --> Framework --> Native --> HAL --> Kernal,接下來對這些層進行三個層次的分析:爲什麼要有它、是什麼、在哪裏,一切要從爲什麼入手。

APP層

  1. why

    我今天早上睡過頭遲到了,那麼我就在想,如果有一個鬧鐘就好了,於是就出現了手機鬧鐘APP,就解決了使用者每天起牀需要有人按時提醒上班的需求。所以當應用設計出來後,一些頁面、頁面跳轉、功能都需要在APP層進行實現。

  2. what

    就如同英文直譯過來一樣,是一些應用軟體,比如系統自帶的應用如:時鐘、計算器,和使用者自己安裝的應用如:微信、微博。應用需要給使用者提供好看的外觀和滿足需求的功能,是安卓系統架構中使用者看得見摸得着的第一個東西,直接和使用者的體驗掛鉤。

  3. where

    以apk的形式安裝在使用者的手機上,開啓手機一看全是。

    原始碼根目錄中的packages目錄對應着系統應用層,如下所示。

    packages目錄 描述
    apps 核心應用程式
    experimental 第三方應用程式
    inputmethods 輸入法目錄
    providers 內容提供者目錄
    screensavers 螢幕保護
    services 通訊服務
    wallpapers 牆紙

Framework層

  1. why

    當產品的定位和設計出來後,安卓軟體工程師就要進行開發,手機上的產品,都是軟體和硬體一起協助開發完成的,那是不是每次開發產品的時候都要將硬體和軟體都寫好呢?當然不是,這樣開發的效率太低、所需要的開發知識的門檻太高了,所以就需要有人幫助安卓應用工程師把一些與系統打交道、通用的東西都封裝好,變成有描述、可以呼叫的API介面,達到提效率、降門檻的效果。

  2. what

    Framework層是一些封裝好的api,主要由Java程式碼實現。當安卓應用工程師在程式設計的時候,就可以呼叫Framework層的api對系統層面比如電源管理、訊息佇列、包管理等做操作。Framework同下層打交到的方式有很多種,參考Android Framework層和Native層通訊的6種方式,同上層打交到的方式就是抽象出各種Java介面。

    往上層看,在AS中開發app的時候,import的各種android.*,就是Framework層的api。

    package com.leo.helloworld;
    
    import android.app.ActivityManager;// 這邊都是
    import android.content.Context;
    import android.os.Bundle;
    
    import androidx.appcompat.app.AppCompatActivity;
    
    public class leo extends AppCompatActivity {
    
        public  void activityManagerDemo(Context context){
            ActivityManager activityManager =
                    (ActivityManager)(context.getSystemService(android.content.Context.ACTIVITY_SERVICE )) ;
            return;
        }
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_leo1);
        }
    }
    

    往下層看,Framework層提供了jni.h的檔案去連線Native中的C或C++程式碼,達到了Java程式碼呼叫C或C++程式碼的效果。

    下面 下麪參照網上對於 ‘Framework是什麼’ 的回答:

    直接翻譯的意思是架構,但這樣說可能不懂,下面 下麪我從兩個方面來給你說吧:

    一是比喻來說,假設你現在要蓋樓房,framework就好比一個建築公司,它裏面有專門採集石料的,專門的租夾板的,專門的磨砂,搬磚的,運輸的等等一系列的,你需要做的就是要通過這家建築公司來蓋樓,比如說你要採集石料,你通過公司採購部門,直接採集來了石料,然後通過他們進行一系列的工作,這要比你自己去河裏採砂,到山上去炸石頭(這些活都是不使用framework的底層嵌入式開發人員做的)要簡單的多,這也是爲什麼farmework要高效的多,所以說,對於開發人員來說,他就像是會爲開發省很多事情,但這種框架下開發的軟體有一定的依賴性,所以,對於不是開發人員的使用者來說,如果你的電腦上有這種框架下開發的軟體,你必須有這個框架軟體纔可以,就好像你如果要用擴充套件名爲EXE的應用程式,你必須先有windows系統一樣

    另外呢,就是通過實際案例來說明framework

    如果我們要完成螢幕列印「hello world」,你如果沒用框架軟體,你或許會先瞭解底層如何實現在螢幕上顯示字元,又如何啓動顯示器,如何控制字元位置等等,然後再考慮如何列印hello world

    但是在framework下,框架下的Console類下有個靜態方法Write直接實現了列印,你只需要呼叫這個方法,然後告訴他你要列印的資訊就可以了,Console.Write(「hello world」)

    不管你是不是開發人員,解釋的覺得還是透徹的,不懂的話可以再追問,希望能得到最佳~

    參照來源:XJZWX

    Framework層主要提供的元件如下:

    名稱 功能描述
    Activity Manager(活動管理器) 管理各個應用程式生命週期以及通常的導航回退功能
    Location Manager(位置管理器) 提供地理位置以及定位功能服務
    Package Manager(包管理器) 管理所有安裝在Android系統中的應用程式
    Notification Manager(通知管理器) 使得應用程式可以在狀態列中顯示自定義的提示資訊
    Resource Manager(資源管理器) 提供應用程式使用的各種非程式碼資源,如在地化字串、圖片、佈局檔案、顏色檔案等
    Telephony Manager(電話管理器) 管理所有的移動裝置功能
    Window Manager(視窗管理器) 管理所有開啓的視窗程式
    Content Providers(內容提供器) 使得不同應用程式之間可以共用數據
    View System(檢視系統) 構建應用程式的基本元件
  3. where

    應用框架層的主要實現程式碼在/frameworks/base和/frameworks/av目錄下,其中/frameworks/base目錄結構如下所示:

    /frameworks/base目錄 描述 /frameworks/base目錄 描述
    api 定義API cmds 重要命令:am、app_proce等
    core 核心庫 data 字型和聲音等數據檔案
    docs 文件 graphics 圖形影象相關
    include 標頭檔案 keystore 和數據簽名證書相關
    libs location 地理位置相關庫
    media 多媒體相關庫 native 本地庫
    nfc-extras NFC相關 obex 藍牙傳輸
    opengl 2D/3D 圖形API packages 設定、TTS、VPN程式
    sax XML解析器 services 系統服務
    telephony 電話通訊管理 test-runner 測試工具相關
    tests 測試相關 tools 工具
    wifi wifi無線網路

    參考鏈接:Android系統架構與系統原始碼目錄

Native/Art

Native: C/C++
  1. why

    首先,安卓的應用和framework層主要使用Java語言開發,但是Java是高階語言的一種,雖然在程式設計時比較容易學習,但是如果要完成一些複雜運算和偏向底層的操作,Java非常難實現,而使用C/C++會很簡單,所以一般會使用虛擬機器:JVM等,將Java程式碼編譯後再解釋成機器語言,來驅動硬體。那麼除開使用虛擬機器將.class檔案解釋後變成機器可識別的檔案,我們可以通過給Java程式碼加上native關鍵字、Java程式碼利用jni.h檔案呼叫C/C++程式碼的方式,把複雜的Java難以實現的功能,交給C/C++程式碼來實現。

  2. what

    常見一些本地服務和一些鏈接庫等。這一層的一個特點就是通過C和C++語言實現。

  3. where

    C/C++程式庫並不完全在一個目錄中,主要的C/C++程式庫如:

    名稱 功能描述
    OpenGL ES 3D繪圖函數庫
    Libc 從BSD繼承來的標準C系統函數庫,專門爲基於嵌入式Linux的裝置定製
    Media Framework 多媒體庫,支援多種常用的音訊、視訊格式錄製和回放。
    SQLite 輕型的關係型數據庫引擎
    SGL 底層的2D圖形渲染引擎
    SSL 安全通訊協定,是爲網路通訊提供安全及數據完整性的一種安全協定
    FreeType 可移植的字型引擎,它提供統一的介面來存取多種字型格式檔案

    要檢視framework層對應的native層原始碼,參考:如何查詢native方法所對應的底層檔案

Art
  1. why

    在安卓系統中,APP開發和Framework層開發都是Java程式碼,Java程式碼執行在虛擬機器上,如:JVM。Art也是虛擬機器的一種,所以其存在的理由就是爲了支援Java程式碼編譯、解釋。

  2. what

    Art,Android Runtime,是安卓手機上執行的虛擬機器,Android4.4及以前使用的都是Dalvik虛擬機器,Android5.0纔開始使用Art。

    爲什麼不使用JVM做爲虛擬機器?

    Java VM是以基於棧的虛擬機器(Stack-based),而Dalvik是基於暫存器的虛擬機器(Register-based)。 顯然,後者最大的好處在於可以根據硬體實現更大的優化,這更適合移動裝置的特點。DVM非常適合在行動終端上使用,與PC相比,它不需要很快的CPU和大量的記憶體空間。

    爲什麼要從Dalvik變爲Art?

    • Dalvik
      Android4.4及以前使用的都是Dalvik虛擬機器,我們知道Apk在打包的過程中會先將Java等原始碼通過javac編譯成.class檔案,但是我們的Dalvik虛擬機器只會執行.dex檔案,這個時候dx會將.class檔案轉換成Dalvik虛擬機器執行的.dex檔案。Dalvik虛擬機器在啓動的時候會先將.dex檔案轉換成快速執行的機器碼,又因爲65535這個問題,導致我們在應用冷啓動的時候有一個合包的過程,最後導致的一個結果就是我們的app啓動慢,這就是Dalvik虛擬機器的JIT特性(Just In Time)。
    • Art
      ART虛擬機器是在Android5.0纔開始使用的Android虛擬機器,ART虛擬機器必須要相容Dalvik虛擬機器的特性,但是ART有一個很好的特性AOT(ahead of time),這個特性就是我們在安裝APK的時候就將dex直接處理成可直接供ART虛擬機器使用的機器碼,ART虛擬機器將.dex檔案轉換成可直接執行的.oat檔案,ART虛擬機器天生支援多dex,所以也不會有一個合包的過程,所以ART虛擬機器會很大的提升APP冷啓動速度。

    簡而言之,就是Dalvik是邊執行邊解釋.dex檔案,Art是啓動時就預解釋.dex檔案爲.oat檔案,Dalvik啓動時更快,Art執行時更快。

    參考鏈接

    Android Dalvik虛擬機器和ART虛擬機器對比

    一篇文章帶你瞭解 Android的 JIT 、AOT、Dalvik、ART ,不再傻傻分不清

    JVM:.java檔案->.class檔案->.jar檔案

    DVM:.java檔案->.class檔案->.dex檔案->.apk檔案

    ART:.java檔案->.class檔案->.dex檔案->.oat檔案->.apk檔案

  3. where

    • .oat檔案;
    • art目錄。

HAL層

  1. why

    設想一下使用Java開發後端的情況,似乎在JVM後,就直接是操作系統,利用操作系統內提供的對機器的指令完成軟體對硬體的操作,但是爲啥安卓系統架構中Art之下還需要有一個硬體抽象層?下面 下麪參照一下羅老師對於HAL層的介紹:

    Android的硬體抽象層,簡單來說,就是對Linux內核驅動程式的封裝,向上提供介面,遮蔽低層的實現細節。也就是說,把對硬體的支援分成了兩層,一層放在使用者空間(User Space),一層放在內核空間(Kernel Space),其中,硬體抽象層執行在使用者空間,而Linux內核驅動程式執行在內核空間。爲什麼要這樣安排呢?把硬體抽象層和內核驅動整合在一起放在內核空間不可行嗎?從技術實現的角度來看,是可以的,然而從商業的角度來看,把對硬體的支援邏輯都放在內核空間,可能會損害廠家的利益。我們知道,Linux內核原始碼版權遵循GNU License,而Android原始碼版權遵循Apache License,前者在發佈產品時,必須公佈原始碼,而後者無須發佈原始碼。如果把對硬體支援的所有程式碼都放在Linux驅動層,那就意味着發佈時要公開驅動程式的原始碼,而公開原始碼就意味着把硬體的相關參數和實現都公開了,在手機市場競爭激烈的今天,這對廠家來說,損害是非常大的。因此,Android纔會想到把對硬體的支援分成硬體抽象層和內核驅動層,內核驅動層只提供簡單的存取硬體邏輯,例如讀寫硬體暫存器的通道,至於從硬體中讀到了什麼值或者寫了什麼值到硬體中的邏輯,都放在硬體抽象層中去了,這樣就可以把商業祕密隱藏起來了。也正是由於這個分層的原因,Android被踢出了Linux內核主線程式碼樹中。大家想想,Android放在內核空間的驅動程式對硬體的支援是不完整的,把Linux內核移植到別的機器上去時,由於缺乏硬體抽象層的支援,硬體就完全不能用了,這也是爲什麼說Android是開放系統而不是開源系統的原因。

    參考鏈接Android硬體抽象層(HAL)概要介紹和學習計劃

    我的理解是,其實安卓系統架構中的HAL層和Kernal層組合起來,起到的是操作系統的作用,但是爲了將關鍵原始碼不開源,所以將一些不能公佈的原始碼放在HAL層,一些能開源的程式碼放在Kernal層。

  2. what

    硬體抽象層是位於操作系統內核與硬體電路之間的介面層,其目的在於將硬體抽象化,爲了保護硬體廠商的智慧財產權,它隱藏了特定平臺的硬體介面細節,爲操作系統提供虛擬硬體平臺,使其具有硬體無關性,可在多種平臺上進行移植。

    在HAL層,程式碼也是儲存在jni.h和.c型別的檔案中,與Framework層呼叫Native層不一樣的是,HAL層的程式碼需要遵循固定的格式、命名進行編寫。

    參考鏈接:Android中HAL如何向上層提供介面總結-hw_device_t

  3. where

    • hardware/libhardware/
    • hardware/libhardware_legacy/
    • hardware/xxxx/

Kernal層

  1. why

    如果說上面的所有層,都是決定邏輯上去驅動那個硬體,那麼需要有一層只負責對硬體做操作,不是說上面的所有層沒辦法對硬體做操作,畢竟大家都是圖靈完備的Java/C/C++程式碼,而是要把相同類似的事物放在一起,這樣可以解耦,所以Kernal層,主要負責對硬體做操作,也叫驅動層。

    爲什麼安卓要採用linux作爲操作系統呢?

    Android系統是基於Linux內核的,這一層爲Android裝置的各種硬體提供了底層的驅動(如顯示,音訊,照相機,藍牙,WI-FI,電源管理等等),那麼Android爲什麼會選擇採用linux呢?原因與Linux的特性有關,內核作爲一個抽象層存在硬體和軟體之間,強大的記憶體管理和進程管理,基於許可權的安全模式,支援共用庫,經過認證的驅動模式,Linux本身就是開源專案等等。

  2. what

    Android 的核心繫統服務基於Linux 內核,在此基礎上新增了部分Android專用的驅動。系統的安全性、記憶體管理、進程管理、網路協定棧和驅動模型等都依賴於該內核。

    主要驅動:增強顯示驅動、鍵盤驅動、Flash記憶體驅動、照相機驅動、音訊驅動、藍牙驅動、WiFi驅動、Binder IPC驅動、Power Management(電源管理),包括硬體時鐘,記憶體分配和共用,低記憶體管理,kernel偵錯,日誌裝置,android IPC機制 機製,電源管理等。

    如果要寫一個驅動應該怎樣寫呢?Android 從上層到底層-----kernel層

  3. where

    主要在Driver目錄下。

    1、Binder IPC驅動:基於OpenBinder框架的一個驅動,用於提供 Android平臺的進程間通訊功能。原始碼位於drivers/staging/android/binder.c。

    2、電源管理(PM) :一個基於標準Linux電源管理系統的輕量級Android電源管理驅動,針對嵌入式裝置做了很多優化,比如電池電量。原始碼位於:kernel/power/earlysuspend.c、kernel/power/consoleearlysuspend.c、kernel/power/fbearlysuspend.c、kernel/power/wakelock.c、kernel/power/userwakelock.c

    3、低記憶體管理器:比Linux的標準的OOM機制 機製更加靈活,它可以根據需要殺死進程以釋放需要的記憶體。原始碼位於 drivers/staging/ android/lowmemorykiller.c。

    4、匿名共用記憶體: 爲進程間提供大塊共用記憶體,同時爲內核提供回收和管理這個記憶體的機制 機製。原始碼位於mm/ashmem.c。

    5、 PMEM :用於向使用者空間提供連續的實體記憶體區域,DSP和某些裝置只能工作在連續的實體記憶體上。原始碼位於drivers/misc/pmem.c。

    6、 Logger :一個輕量級的日誌裝置,用於抓取Android系統的各種日誌。原始碼位於drivers/staging/android/logger.c。

    7、 Alarm :提供了一個定時器,用於把裝置從睡眠狀態喚醒,同時它還提供了一個即使在裝置睡眠時也會 執行的時鐘基準。原始碼位於drivers/rtc/alarm.c。

    8、USB Gadget:驅動 一個基於標準 Linux USB gadget驅動框架的裝置驅動,Android的USB驅動是基於gaeget框 架的。原始碼位於drivers/usb/gadget/。

    9、Ram Console: 爲了提供偵錯功能,Android允許將偵錯日誌資訊寫入一個被稱爲RAM Console的裝置裡,它是一個基於RAM的Buffer。原始碼位於drivers/staging/android / ram_console.c。

    10、timed device: 提供了對裝置進行定時控制的功能,目前支援vibrator和LED裝置。原始碼位於drivers/staging/android /timed_output.c(timed_gpio.c)。

    11、Yaffs2 :是檔案系統 Android採用Yaffs2作爲MTD nand flash檔案系統,原始碼位於fs/yaffs2/目錄下。Yaffs2是一個快速穩定的應用於NAND和NOR Flash的跨平臺的嵌入式裝置檔案系統,同其他Flash檔案系統相比,Yaffs2能使用更小的記憶體來儲存其執行狀態,因此它佔用記憶體小。Yaffs2的垃圾回收非常簡單而且快速,因此能表現出更好的效能。Yaffs2在大容量的NAND Flash上的效能表現尤爲突出,非常適合大容量的Flash儲存。

1.2 Android8原始碼與系統架構的關係

從安卓8的原始碼目錄入手,分析安卓系統架構的每一層,大概在原始碼中的什麼位置。

原始碼目錄結構

目錄圖如下:

在这里插入图片描述

原始碼目錄描述

Android原始碼根目錄 描述
art 全新的ART執行環境
bionic 系統C庫
bootable 啓動引導相關程式碼
build 存放系統編譯規則及generic等基礎開發包設定
cts Android相容性測試套件標準
dalvik dalvik虛擬機器
developers 開發者目錄
development 應用程式開發相關
device 裝置相關設定
docs 參考文件目錄
external 開源模組相關檔案
frameworks 應用程式框架,Android系統核心部分,由Java和C++編寫
hardware 主要是硬體抽象層的程式碼
kernel Linux Kernal,需要單獨去下載
libcore 核心庫相關檔案
libnativehelper 動態庫,實現JNI庫的基礎
packages 應用程式包
pdk Plug Development Kit 的縮寫,本地開發套件
platform_testing 平臺測試
prebuilts x86和arm架構下預編譯的一些資源
sdk sdk和模擬器
system 底層檔案系統庫、應用和元件
toolchain 工具鏈檔案
tools 工具檔案
Makefile 全域性Makefile檔案,用來定義編譯規則

App層:packages

Framework層:frameworks

Native/ART層

​ NativeC/C++:bionic

​ Art:art

HAL層:hardware

Kernal層:kernel

1.3 爲什麼安卓系統架構要這麼設計

思路:從整體架構分析,再從大到小,到每層分工來分析。

從0開始,什麼都沒有,我們收到需求:做一款能在手機上執行的應用,分析本質,就是軟體和硬體的互動,完成一種滿足使用者需求的產品。那麼現在開始開發了,寫了很多程式碼,配合上硬體,能完成功能,那麼這些程式碼裏面,肯定必須得有軟體能直接驅動硬體的程式碼,而這些程式碼肯定是最後被呼叫,又因爲要和硬體打交道,C語言很合適,於是選擇C語言編寫,這時候把這些驅動硬體的程式碼拉出來放在一個資料夾裏面,把這個資料夾叫做操作系統程式碼。由於在軟體選語言的時候,覺得Java的可移植性好,所以選擇用Java進行開發,接下來再看看寫的程式碼,除去驅動硬體的程式碼後,發現有一些程式碼是用來寫APP的頁面、頁面跳轉之類的,那也把這些程式碼整合一下,放在一個資料夾裏面,就叫業務程式碼吧。而Java開發,那必須得有虛擬機器才能 纔能變成機器識別的程式碼,所以又將虛擬機器的程式碼放在一個資料夾裡,就叫虛擬機器程式碼吧。

開發的某一天,我們忽然碰到一個很複雜的功能,Java實現起來太難了,這時候想到一個辦法–使用C/C++來實現,然後用Java呼叫這些C/C++程式碼來實現功能,那也把這些C/C++程式碼也放在一個資料夾裡,就叫呼叫的C/C++程式碼吧。

開發工作結束了,現在的專案結構是:業務程式碼、虛擬機器程式碼、呼叫的C/C++程式碼、操作系統程式碼最後連線到硬體。

在休息兩天後,又接到新的需求,要開發另外一款新的產品,這下根據上一款專案的程式碼,忽然發現,好像業務程式碼中,有一部分是可以重複使用的,比如一些位置、電話之類的通用模組,那新建一個資料夾,把這部分重複的程式碼也放在一個資料夾裏面,就叫通用程式碼吧。這時候忽然收到通知,操作系統的程式碼要求要開源了,但是裏面有一部分程式碼涉及商業機密不允許開源啊,於是把這部分無法開源的程式碼放在一個資料夾裡,就叫不開原始碼吧。

第二款APP開發結束了,現在的專案結構是:業務程式碼、通用程式碼、虛擬機器程式碼、呼叫的C/C++程式碼、不開原始碼、操作系統程式碼,對應了安卓系統架構的:APP層、Framework層、Art、Native、HAL層、Kernal層