1. 程式人生 > >[Android系統原理及開發要點詳解

[Android系統原理及開發要點詳解

第1章 Android系統概述 1
1.1 基礎知識 1
1.1.1 Android開發系統的由來 1
1.1.2 行動電話系統開發模式 2
1.1.3 未來行動電話的功能及Android的優勢 4
1.2 Android的開發工作 6
1.2.1 Android移植開發 6
1.2.2 Android應用開發 8
1.2.3 Android系統開發 9
1.3 Android的SDK與原始碼 10
1.3.1 基於SDK的Android開發 10
1.3.2 基於原始碼SDK Android開發 11

第2章 Android系統開發綜述 13
2.1 Android的系統架構 13
2.1.1 軟體結構 13

android程式庫:

系統C庫:一個從BSD繼承來的標準C系統函式庫(libc),它是專門為嵌入式Linux的裝置制定的庫

多媒體庫:基於PacketVideo的OpenCore:該哭支援多種常用的音訊視訊格式回訪和錄製,支援多種媒體格式的編碼解碼格式。

SurfaceManager:顯示子系統的管理器,並且為多個應用程式提供2D和3D圖層的無縫融合。

LibWebCore:一個最新的Web瀏覽器引擎,支援Android瀏覽器以及一個可嵌入的Web檢視。

SGL:Skia圖形庫,底層的2D圖形引擎。

3Dlibraries:基於OpenGL ES1.0 API的實現:該庫可以使用硬體3D加速(如果可用)或者使用高度優化的3D軟加速。

FreeType:點陣圖(bitmap)和向量(vector)字型顯示。

3. Android 執行環境

android 有Dalvik java虛擬機器和基礎Java類庫組成

Dalvik是Android中使用的Java虛擬機器,每一個Android程式都執行在自己的程序中,都有一個Dalvik虛擬機器例項。Dalvik被設計成一個可以同時執行多個虛擬機器例項的虛擬系統

Davik運行鍼對小記憶體做了優化的Dex可執行檔案

同時虛擬機器是基於暫存器的,所有的類都經過Java編譯,然後通過SDK中的dx工具轉化成.dex格式虛擬機器執行

Dalvik虛擬機器依賴於Linux核心一些功能,比如執行緒機制和底層記憶體管理機制。

4、 Android應用程式框架

應用程式重用機制也使使用者可以方便替換程式元件,包括

views:豐富而又可擴充套件的檢視(Views):可以用來構建應用程式,包括列表(lists)、網格(grids)、文字框(text boxes),按鈕(buttons)等基本的UI元素,甚至可嵌入的Web瀏覽器。

content providers :使得應用程式可以訪問另一個應用程式的資料(如聯絡人資料庫),或者共享它們自己的資料。

resource Manager:提供非程式碼資源的訪問,如本地字串,圖形和佈局檔案(layout files).

Notification Manager : 使得應用程式可以在狀態列中顯示自定義的提示資訊。

Activity Manager:用來管理應用程式宣告週期並提供常用的導航回退功能。

WindowManager是Android中一個重要的服務(Service )。WindowManager Service 是全域性的,是唯一的。它將使用者的操作,翻譯成                                                         為指令,傳送給呈現在介面上的各個Window。Activity會將頂級的控制元件註冊到 Window Manager 中,
當用戶點選觸碰螢幕或鍵盤的時候,Window Manager就會通知到,而當控制元件有一些請求產生,也會經由ViewParent送回到Window                                                                Manager中。從而完成整個通訊流程。
整個Android的視窗機制是基於一個叫做 WindowManager,這個介面可以新增view到螢幕,也可以從螢幕刪除view。它面向的物件一端                                                           是螢幕,另一端就是View,通過WindowManager的 addView方法建立View,這樣產生出來的View根據
WindowManager.LayoutParams屬性不同,效果也就不同了。比如建立 系統頂級視窗,實現懸浮視窗效果!WindowManager的方法很                                                            簡單,基本用到的就三addView,removeView,updateViewLayout。介面,而WindowManager.LayoutParams的屬性就多了,非常豐富,                                                       具體請查後面介紹
怎麼樣獲取windowManager例項呢,下面我們就通過一個小的例子來說明以下:
Button bb=new Button(getApplicationContext());  
WindowManager wmManager=(WindowManager) getSystemService(Context.WINDOW_SERVICE);  
WindowManager.LayoutParams wmParams = new WindowManager.LayoutParams();  
/** 
*以下都是WindowManager.LayoutParams的相關屬性 
* 具體用途請參考SDK文件 
*/  
  wmParams.type=2002;   //這裡是關鍵,你也可以試試2003  
  wmParams.format=1;  
/** 
*這裡的flags也很關鍵 
*程式碼實際是wmParams.flags |= FLAG_NOT_FOCUSABLE; 
*40的由來是wmParams的預設屬性(32)+ FLAG_NOT_FOCUSABLE(8) 
*/  
  wmParams.flags=40;  
  wmParams.width=40;  
  wmParams.height=40;  
 
  wmManager.addView(bb, wmParams);  //建立View  
上面就是一個簡單的例子,建立了一個Button物件然後通過WindowManager例項物件的addView新增這個ButtonVIew並根據相應的LayoutParams引數進行顯示。
 
WindowManager物件的一些介面說明:
1)abstract Display  getDefaultDisplay();  //獲取預設顯示的 Display 物件。
2)abstract void removeViewImmediate(View view);//是removeView(View) 的一個特殊擴充套件,在方法返回前能夠立即呼叫該檢視層次的View.onDetachedFromWindow() 方法。
 
下面我們就重點介紹以下WindowManager介面的巢狀內部類LayoutParams。
WindowManager.LayoutParams 是 WindowManager 介面的巢狀類;繼承於 ViewGroup.LayoutParams 。它的內容十分豐富。其實WindowManager.java的主要內容就是由這個類定義構成。下面來分析一下這個類:
 
定義 
public static class WindowManager.LayoutParams 
extends ViewGroup.LayoutParams implements Parcelable
繼承關係 
java.lang.Object
  ↳android.view.ViewGroup.LayoutParams
  ↳android.view.WindowManager.LayoutParams


繼承來的屬性與常量 
從 ViewManager.LayoutParams 繼承來的屬性:
android:layout_height
 Specifies the basic height of the view.


android:layout_width
 Specifies the basic width of the view.


從 ViewManager.LayoutParams繼承的常量:
FILL_PARENT
WRAP_CONTENT
MATCH_PARENT
 
兩個變數: 
width
height
 
屬性及可用的常量定義 
1. public int x;
  如果忽略gravity屬性,那麼它表示視窗的絕對X位置。
  什麼是gravity屬性呢?簡單地說,就是視窗如何停靠。
  當設定了 Gravity.LEFT 或 Gravity.RIGHT 之後,x值就表示到特定邊的距離。
 
2. public int y;
  如果忽略gravity屬性,那麼它表示視窗的絕對Y位置。
  當設定了 Gravity.TOP 或 Gravity.BOTTOM 之後,y值就表示到特定邊的距離。
 
3. public float horizontalWeight;
  public float verticalWeight;
  在縱/橫向上,為關聯的view預留了多少擴充套件空間(畫素)。如果是0,那麼此view不能被拉伸。
  其他情況下,擴充套件空間(畫素)將被widget所均分。


4. public int type;
視窗型別。有3種主要型別:
a)Applicationwindows:
取值在 FIRST_APPLICATION_WINDOW 和 LAST_APPLICATION_WINDOW 之間。
是通常的、頂層的應用程式視窗。必須將 token 設定成 activity 的 token 。
b)Sub_windows:
取值在 FIRST_SUB_WINDOW 和 LAST_SUB_WINDOW 之間。
與頂層視窗相關聯,token 必須設定為它所附著的宿主視窗的 token。
c)Systemwindows:
取值在 FIRST_SYSTEM_WINDOW 和 LAST_SYSTEM_WINDOW 之間。
用於特定的系統功能。它不能用於應用程式,使用時需要特殊許可權。
下面定義了 type 的取值:
 應用程式視窗。
 public static final int FIRST_APPLICATION_WINDOW = 1;


 所有程式視窗的“基地”視窗,其他應用程式視窗都顯示在它上面。     
 public static final int TYPE_BASE_APPLICATION   =1;       


 普通應喲功能程式視窗。token必須設定為Activity的token,以指出該視窗屬誰。
 public static final int TYPE_APPLICATION       = 2;


 用於應用程式啟動時所顯示的視窗。應用本身不要使用這種型別。
 它用於讓系統顯示些資訊,直到應用程式可以開啟自己的視窗。   
 public static final int TYPE_APPLICATION_STARTING = 3;


 應用程式視窗結束。
 public static final int LAST_APPLICATION_WINDOW = 99;


 子視窗。子視窗的Z序和座標空間都依賴於他們的宿主視窗。
 public static final int FIRST_SUB_WINDOW       = 1000;


 面板視窗,顯示於宿主視窗上層。
 public static final int TYPE_APPLICATION_PANEL  = FIRST_SUB_WINDOW;


 媒體視窗,例如視訊。顯示於宿主視窗下層。
 public static final int TYPE_APPLICATION_MEDIA  = FIRST_SUB_WINDOW+1;


 應用程式視窗的子面板。顯示於所有面板視窗的上層。(GUI的一般規律,越“子”越靠上)
 public static final int TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW +2;


 對話方塊。類似於面板視窗,繪製類似於頂層視窗,而不是宿主的子視窗。
 public static final int TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW +3;


 媒體資訊。顯示在媒體層和程式視窗之間,需要實現透明(半透明)效果。(例如顯示字幕)
 public static final int TYPE_APPLICATION_MEDIA_OVERLAY  = FIRST_SUB_WINDOW +4;


 子視窗結束。( End of types of sub-windows )
 public static final int LAST_SUB_WINDOW        = 1999;


 系統視窗。非應用程式建立。
 public static final int FIRST_SYSTEM_WINDOW    = 2000;


 狀態列。只能有一個狀態列;它位於螢幕頂端,其他視窗都位於它下方。
 public static final int TYPE_STATUS_BAR        =  FIRST_SYSTEM_WINDOW;


 搜尋欄。只能有一個搜尋欄;它位於螢幕上方。
 public static final int TYPE_SEARCH_BAR        = FIRST_SYSTEM_WINDOW+1;


 電話視窗。它用於電話互動(特別是呼入)。它置於所有應用程式之上,狀態列之下。
 public static final int TYPE_PHONE            = FIRST_SYSTEM_WINDOW+2;


 系統提示。它總是出現在應用程式視窗之上。
 public static final int TYPE_SYSTEM_ALERT      =  FIRST_SYSTEM_WINDOW +3;


 鎖屏視窗。
 public static final int TYPE_KEYGUARD          = FIRST_SYSTEM_WINDOW +4;


 資訊視窗。用於顯示toast。
 public static final int TYPE_TOAST            = FIRST_SYSTEM_WINDOW +5;


 系統頂層視窗。顯示在其他一切內容之上。此視窗不能獲得輸入焦點,否則影響鎖屏。
 public static final int TYPE_SYSTEM_OVERLAY    =  FIRST_SYSTEM_WINDOW +6;


 電話優先,當鎖屏時顯示。此視窗不能獲得輸入焦點,否則影響鎖屏。
 public static final int TYPE_PRIORITY_PHONE    =  FIRST_SYSTEM_WINDOW +7;


 系統對話方塊。(例如音量調節框)。
 public static final int TYPE_SYSTEM_DIALOG     =  FIRST_SYSTEM_WINDOW +8;


 鎖屏時顯示的對話方塊。
 public static final int TYPE_KEYGUARD_DIALOG   =  FIRST_SYSTEM_WINDOW +9;


 系統內部錯誤提示,顯示於所有內容之上。
 public static final int TYPE_SYSTEM_ERROR      =  FIRST_SYSTEM_WINDOW +10;


 內部輸入法視窗,顯示於普通UI之上。應用程式可重新佈局以免被此視窗覆蓋。
 public static final int TYPE_INPUT_METHOD      =  FIRST_SYSTEM_WINDOW +11;


 內部輸入法對話方塊,顯示於當前輸入法視窗之上。
 public static final int TYPE_INPUT_METHOD_DIALOG= FIRST_SYSTEM_WINDOW +12;


 牆紙視窗。
 public static final int TYPE_WALLPAPER         = FIRST_SYSTEM_WINDOW +13;


 狀態列的滑動面板。
 public static final int TYPE_STATUS_BAR_PANEL   = FIRST_SYSTEM_WINDOW +14;


 系統視窗結束。
 public static final int LAST_SYSTEM_WINDOW     = 2999;
5. public int memoryType;
  指出視窗所使用的記憶體緩衝型別。預設為 NORMAL 。


  下面定義了 memoryType 的取值:
 視窗緩衝位於主記憶體。
 public static final int MEMORY_TYPE_NORMAL = 0;


 視窗緩衝位於可以被DMA訪問,或者硬體加速的記憶體區域。
 public static final int MEMORY_TYPE_HARDWARE = 1;


 視窗緩衝位於可被圖形加速器訪問的區域。
 public static final int MEMORY_TYPE_GPU = 2;


 視窗緩衝不擁有自己的緩衝區,不能被鎖定。緩衝區由本地方法提供。
 public static final int MEMORY_TYPE_PUSH_BUFFERS = 3;
6.  public int flags;
行為選項/旗標,預設為 none .


下面定義了 flags 的取值:
 視窗之後的內容變暗。
 public static final int FLAG_DIM_BEHIND       = 0x00000002;


 視窗之後的內容變模糊。
 public static final int FLAG_BLUR_BEHIND       = 0x00000004;


 不許獲得焦點。
 public static final int FLAG_NOT_FOCUSABLE     = 0x00000008;


 不接受觸控式螢幕事件。
 public static final int FLAG_NOT_TOUCHABLE     = 0x00000010;


 當視窗可以獲得焦點(沒有設定 FLAG_NOT_FOCUSALBE 選項)時,仍然將視窗範圍之外的點裝置事件(滑鼠、觸控式螢幕)傳送給後面的視窗處理。否則它將獨佔所有的點裝置事件,而不管它們是不是發生在視窗範圍內。
 public static final int FLAG_NOT_TOUCH_MODAL   = 0x00000020;


 如果設定了這個標誌,當裝置休眠時,點選觸控式螢幕,裝置將收到這個第一觸控事件。
 通常第一觸控事件被系統所消耗,使用者不會看到他們點選螢幕有什麼反應。
 public static final int FLAG_TOUCHABLE_WHEN_WAKING = 0x00000040;


 當此視窗為使用者可見時,保持裝置常開,並保持亮度不變。
 public static final int FLAG_KEEP_SCREEN_ON    = 0x00000080;


 窗口占滿整個螢幕,忽略周圍的裝飾邊框(例如狀態列)。此視窗需考慮到裝飾邊框的內容。
 public static final int FLAG_LAYOUT_IN_SCREEN   =0x00000100;


 允許視窗擴充套件到螢幕之外。
 public static final int FLAG_LAYOUT_NO_LIMITS   =0x00000200;


 視窗顯示時,隱藏所有的螢幕裝飾(例如狀態條)。使窗口占用整個顯示區域。
 public static final int FLAG_FULLSCREEN     = 0x00000400;


 此選項將覆蓋FLAG_FULLSCREEN選項,並強制螢幕裝飾(如狀態條)彈出。
 public static final int FLAG_FORCE_NOT_FULLSCREEN   =0x00000800;


 抖動。指 對半透明的顯示方法。又稱“點透”。圖形處理較差的裝置往往用“點透”替代Alpha混合。
 public static final int FLAG_DITHER           = 0x00001000;


 不允許螢幕截圖。
 public static final int FLAG_SECURE           = 0x00002000;


 一種特殊模式,佈局引數用於指示顯示比例。
 public static final int FLAG_SCALED           = 0x00004000;


 當螢幕有可能貼著臉時,這一選項可防止面頰對螢幕造成誤操作。
 public static final int FLAG_IGNORE_CHEEK_PRESSES   = 0x00008000;


 當請求佈局時,你的視窗可能出現在狀態列的上面或下面,從而造成遮擋。當設定這一選項後,視窗管理器將確保視窗內容不會被裝飾條(狀態列)蓋住。
 public static final int FLAG_LAYOUT_INSET_DECOR = 0x00010000;


 反轉FLAG_NOT_FOCUSABLE選項。
 如果同時設定了FLAG_NOT_FOCUSABLE選項和本選項,視窗將能夠與輸入法互動,允許輸入法視窗覆蓋;
 如果FLAG_NOT_FOCUSABLE沒有設定而設定了本選項,視窗不能與輸入法互動,可以覆蓋輸入法視窗。
 public static final int FLAG_ALT_FOCUSABLE_IM = 0x00020000;


 如果你設定了FLAG_NOT_TOUCH_MODAL,那麼當觸屏事件發生在視窗之外事,可以通過設定此標誌接收到一個 MotionEvent.ACTION_OUTSIDE事件。注意,你不會收到完整的down/move/up事件,只有第一次down事件時可以收到 ACTION_OUTSIDE。
 public static final int FLAG_WATCH_OUTSIDE_TOUCH = 0x00040000;


 當螢幕鎖定時,視窗可以被看到。這使得應用程式視窗優先於鎖屏介面。可配合FLAG_KEEP_SCREEN_ON選項點亮螢幕並直接顯示在鎖屏介面之前。可使用FLAG_DISMISS_KEYGUARD選項直接解除非加鎖的鎖屏狀態。此選項只用於最頂層的全螢幕視窗。
 public static final int FLAG_SHOW_WHEN_LOCKED = 0x00080000;


 請求系統牆紙顯示在你的視窗後面。視窗必須是半透明的。
 public static final int FLAG_SHOW_WALLPAPER = 0x00100000;


 視窗一旦顯示出來,系統將點亮螢幕,正如使用者喚醒裝置那樣。
 public static final int FLAG_TURN_SCREEN_ON = 0x00200000;


 解除鎖屏。只有鎖屏介面不是加密的才能解鎖。如果鎖屏介面是加密的,那麼使用者解鎖之後才能看到此視窗,除非設定了FLAG_SHOW_WHEN_LOCKED選項。
 public static final int FLAG_DISMISS_KEYGUARD = 0x00400000;


 鎖屏介面淡出時,繼續執行它的動畫。
 public static final int FLAG_KEEP_SURFACE_WHILE_ANIMATING =0x10000000;


 以原始尺寸顯示視窗。用於在相容模式下執行程式。
 public static final int FLAG_COMPATIBLE_WINDOW = 0x20000000;


 用於系統對話方塊。設定此選項的視窗將無條件獲得焦點。
 public static final int FLAG_SYSTEM_ERROR = 0x40000000;
7. public int softInputMode;
  軟輸入法模式選項:


  以下選項與 softInputMode 有關:
 軟輸入區域是否可見。
 public static final int SOFT_INPUT_MASK_STATE = 0x0f;


 未指定狀態。
 public static final int SOFT_INPUT_STATE_UNSPECIFIED = 0;


 不要修改軟輸入法區域的狀態。
 public static final int SOFT_INPUT_STATE_UNCHANGED = 1;


 隱藏輸入法區域(當用戶進入視窗時)。
 public static final int SOFT_INPUT_STATE_HIDDEN = 2;


 當視窗獲得焦點時,隱藏輸入法區域。
 public static final int SOFT_INPUT_STATE_ALWAYS_HIDDEN = 3;


 顯示輸入法區域(當用戶進入視窗時)。
 public static final int SOFT_INPUT_STATE_VISIBLE = 4;


 當視窗獲得焦點時,顯示輸入法區域。
 public static final int SOFT_INPUT_STATE_ALWAYS_VISIBLE = 5;


 視窗應當主動調整,以適應軟輸入視窗。
 public static final int SOFT_INPUT_MASK_ADJUST = 0xf0;


 未指定狀態,系統將根據視窗內容嘗試選擇一個輸入法樣式。
 public static final int SOFT_INPUT_ADJUST_UNSPECIFIED = 0x00;


 當輸入法顯示時,允許視窗重新計算尺寸,使內容不被輸入法所覆蓋。
 不可與SOFT_INPUT_ADJUSP_PAN混合使用,如果兩個都沒有設定,系統將根據視窗內容自動設定一個選項。
 public static final int SOFT_INPUT_ADJUST_RESIZE = 0x10;


 輸入法顯示時平移視窗。它不需要處理尺寸變化,框架能夠移動視窗以確保輸入焦點可見。
 不可與SOFT_INPUT_ADJUST_RESIZE混合使用;如果兩個都沒設定,系統將根據視窗內容自動設定一個選項。
 public static final int SOFT_INPUT_ADJUST_PAN = 0x20;


 當用戶轉至此視窗時,由系統自動設定,所以你不要設定它。
 當視窗顯示之後該標誌自動清除。
 public static final int SOFT_INPUT_IS_FORWARD_NAVIGATION = 0x100;
8. public int gravity;
  gravity 屬性。什麼是gravity屬性呢?簡單地說,就是視窗如何停靠。


9. public float horizontalMargin;
  水平邊距,容器與widget之間的距離,佔容器寬度的百分率。


10. public float verticalMargin;
縱向邊距。


11. public int format;
期望的點陣圖格式。預設為不透明。參考android.graphics.PixelFormat。


12. public int windowAnimations;
視窗所使用的動畫設定。它必須是一個系統資源而不是應用程式資源,因為視窗管理器不能訪問應用程式。


13. public float alpha = 1.0f;
整個視窗的半透明值,1.0表示不透明,0.0表示全透明。


14. public float dimAmount = 1.0f;
當FLAG_DIM_BEHIND設定後生效。該變數指示後面的視窗變暗的程度。1.0表示完全不透明,0.0表示沒有變暗。


15. public float screenBrightness = -1.0f;
用來覆蓋使用者設定的螢幕亮度。表示應用使用者設定的螢幕亮度。從0到1調整亮度從暗到最亮發生變化。
 
16. public IBinder token = null;
視窗的標示符。( Identifier for this window. This will usually be filled in for you. )


17. public String packageName = null;
此視窗所在的包名。


18. public int screenOrientation =ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
螢幕方向,參見android.content.pm.ActivityInfo#screenOrientation。


19. 在相容模式下,備份/恢復引數所使用的內部緩衝區。
public int[] mCompatibilityParamsBackup = null;
 
常用方法 
1. public final int copyFrom (WindowManager.LayoutParams o);
  下面定義了各種“CHANGE”資訊,為copyFrom函式所使用。
public staticfinal int LAYOUT_CHANGED =1<<0;
public staticfinal int TYPE_CHANGED =1<<1;
public staticfinal int FLAGS_CHANGED =1<<2;
public staticfinal int FORMAT_CHANGED =1<<3;
public staticfinal int ANIMATION_CHANGED =1<<4;
public staticfinal int DIM_AMOUNT_CHANGED =1<<5;
public staticfinal int TITLE_CHANGED =1<<6;
public staticfinal int ALPHA_CHANGED =1<<7;
public staticfinal int MEMORY_TYPE_CHANGED =1<<8;
public staticfinal int SOFT_INPUT_MODE_CHANGED =1<<9;
public staticfinal int SCREEN_ORIENTATION_CHANGED =1<<10;
public staticfinal int SCREEN_BRIGHTNESS_CHANGED =1<<11;

package manager

           1. 用來獲取當前裝置上應用包的相關資訊。

PackageManager框架

Activity manager

            ActivityManager講解

5. Android應用程式

        android所有的應用程式都是使用Java語言編寫的。Android系統的核心應用程式和系統一起釋出,包括:E-mail客戶端,SMS短訊息程式、日曆、地圖、瀏覽器、聯絡人管理程式等。

        由使用者開發的Android應用程式和Android的核心應用程式是同一層次的,它們都是基於Android的系統API構建的。
2.1.2 Android的工具 16

aapt

adb

android工具

AIDL

AVDS

DDMS

dx

Draw9-patch

Emulator

Hierarchy Viewer

mksdcard

Monkey

sqlite3

Traceview


2.2 Android原始碼的開發環境 18

基於Linux的開發環境,推薦使用Ubuntu

2.2.1 Android原始碼的獲取和提交 18

主機環境包括以下需求:git工具,repo工具,java的JDK 主機編譯工具

Ubuntu主機需要安裝以下包:

1. 獲取Android原始碼

2. 提交android原始碼

2.2.2 Android原始碼結構 21

工程名稱

工程描述

bionic

C執行時支援:libc,libm,libdl,動態linker

bootloader/legacy

Bootloader參考程式碼

build

Build系統

dalvik

Dalvik虛擬機器

development

高層的開發和除錯工具

frameworks/base

Android核心的框架庫

frameworks/policies/base

框架配置策略

Hardware/libhardware

硬體抽象層

hardware/ril

無線介面層(radio Inteface  Layer)

kernel

Linux核心

Prebuilt

對Linux和Mac OS編譯的二進位制支援

System/core

最小化可啟動的環境

System/extras

底層除錯和檢查工具

         bootloader中包含的是核心載入器的內容,在核心執行之前。其中kernel中包含的室內和的內容,bionic和build是編譯系統,prbuilt是預編譯的核心,

2. Android的擴充套件工程

     android的擴充套件工程包含在external資料夾中,這是一個經過修改後適應Android系統的開源工程,這些工程中的內容有些在主機是哪個執行,有些在目標及上執行。

工程名稱

工程描述

Aes

高階加密標準(Advanced  Encrypted Standard)

Apache-http

Http伺服器

Bison

(主機)自動生成語法分析器程式

Bluez

藍芽庫

Bsdiff

(主機)用於為二進位制檔案生成補丁

Bzip2

(主機/目標機)壓縮檔案工具

Clearsilver

(主機)模板語言,包括Python、java、perl、C的庫

Dbus

freedesktop下開源的Linux  IPC通訊機制

Dhcpcd

動態主機配置協議的工具

Dropbear

ssh2伺服器和客戶端

E2fsprogs

(主機)Ext2/3/4 檔案系統的工具

Elfcopy

(主機)ELF工具

Elfutils

(主機)ELF工具

Embunit

嵌入式C系統的測試框架

Emma

(Java)Java程式碼覆蓋檢查工具

Esd

(僅標頭檔案)

Expat

(主機/目標機)XML  Parser

Fdlibm

精確實現IEEE754浮點數

Freetype

C語言實現的字型光柵化引擎製作的一個軟體庫

Gdata

(Java)用於資料操作

Genext2fs

(主機)Ext2檔案系統生成工具

Giflib

GIF工具

Googleclient

(Java)Google客戶端

Grub

多重炒作系統啟動管理器

Icu4c

IBM支援軟體國際滑的開源專案

Iptables

建構在Xtables的架構下,定義“表(tables)”、“鍵(chain)”、“規則(rules)”來處理風暴的運送

Jdiff

(主機Java庫)比較工具

Jhead

Jpeg檔案頭(Exif)編輯修改軟體

Jpeg

Jpeg工具庫

Libffi

可移植的

Libpcap

網路資料包補貨函式包

Libpng

PNG工具庫

Libxml2

(主機/目標機)C語言的XML解析庫

Netcat

用來對網路連線TCP或者UDP進行讀/寫

Netperf

網路效能的測量工具

Neven

人臉識別的一套庫

Opencore

多媒體框架

Openssl

C語言的SSL(Secure  Sockets Layer)工具

Oprofile

Linux核心支援的一種效能分析機制

Ping

Ping工具

Ppp

Ppp(點對點)工具

Protobuf

Google工具,利用.proto檔案自動生成程式碼

qemu

(主機)模擬環境

Safe-iop

跨平臺的整數運算

Skia

一個圖形庫

Sonivox

Sonic嵌入式的音樂合成器

Sqlite

輕量級的SQL嵌入式資料庫

Srec

(主機、目標機)motorols  S-records十六進位制檔案格式工具

Strace

監視系統呼叫的工具

Tagsoup

(Java)HTML解析工具

Tcpdump

為網路中傳送的資料包的頭被完全接貨下來提供分析的工具

Tinyxml

(主機、目標機)XML工具

Tremor

Ogg Vorbis的播放器

Webkit

開源的瀏覽器引擎

Wpa_supplicant

無線區域網Wifi的工具

Xdelta3

二進位制檔案比較工具

Yaffs2

YAFFS檔案系統

3. Android中的Java程式包

2.2.3 編譯 24

1. 編譯方法

在其根目錄下直接執行make即可(MakeFile)

$make

      make過程將遞迴找到各個目錄中的Android.mk檔案進行編譯。Android編譯將搜尋所有的目錄,編譯本身和目錄的名稱及位置沒有關係

2. 編譯結果

    Android編譯結果包含以下內容

主機工具

目標程式

目標映像檔案

目標機Linux核心(需要單獨編譯)

Android系統編譯完成的結果全部在其根目錄out目錄中,原始的各個工程不會改動。

out目錄的結構如下

out/

|---- host                                    //主機內容

|          |----common                   //主機的通用內容

|          |         |-- obj                     編譯所生成的主機linux上執行的工具

|          |----linux-x86

|                    |---  bin

|                    |-----framework

|                    |----lib

|                     ----obj

|----------target                             //目標機內容

 |-----common             //目標機的通用內容

                 |              |---R

 |              |---docs

 |              -----obj

 |-----------product           //目標機的產品目錄

|------generic

其中out/target/product目錄是目標產品的目錄,在預設的情況下使用generic作為目標產品的名稱,目錄結構如下所示:

out/target/product/generic

|-- android-info.txt

|-- clean_steps.mk

|-- data                                                      //資料目錄

|-- installed-files.txt

|-- obj                                                         中間目標檔案目錄

|       |-- APPS                                            Java應用程式包

|       |-- ETC                                              執行時配置檔案

|       |-- EXECUTABLES                         可執行程式

|       |-- KEYCHARS

|       |-- NOTICE.html

|       |-- NOTICE.html.gz

|       |--NOTICE_FILES

|       |--PACKAGING

|       |--SHEARED_LIBRARIES           動態庫(共享庫)

|       |--STATIC_LIBRARIES                靜態庫

|       |--include

| ---lib

| --- previous_build_config.mk

|--- ramdisk.img                                     根檔案系統映像

|---root                                                      根檔案系統目錄  

|---symbols                                             符號的目錄

|---system                                               主檔案系統目錄

|---system.img                                       主檔案系統映像

|---userdata-qemu.img                        QEMU的資料映像

----userdata.img                                   資料映像

其中root,system,data三個目錄分別是目標根檔案系統、主檔案系統和資料檔案系統的目錄,字尾名為.img的檔案分別是他們所對應的映像檔案。

2.2.4 系統的執行 25

Android Emulator基於QEMU,這個模擬器支援Android Virtual Device(Android 虛擬裝置)以及很多的除錯效能。

1. 使用模擬器執行系統

2. 在shell中檢視資訊

3. 模擬器的相關內容

2.3 Android SDK的開發環境 32

Android的SDK開發環境使用預編譯的核心和檔案系統,遮蔽了Android軟體架構第三層及以下的內容,開發者可以基於Android的系統API配合執行應用層次的開發。在SDK、的開發環境中,還可以使用Eclipse等作為IDE開發環境。

2.3.1 SDK的結構 32

android的SDK命名規則為:

android-sdk-{主機系統}_{體系結構}_{版本}


2.3.2 Windows環境SDK開發 33


2.3.3 Linux環境SDK開發 42



第3章 Android的Linux核心與驅動程式 44


3.1 Linux核心與驅動 44

android在標準的Linux核心中主要增加的內容是一些驅動程式,這些驅動主要分成兩種型別:

Android專用驅動:

Android使用的裝置驅動:

3.2 Android專用驅動 45


3.2.1 Ashmem 45                              //匿名共享驅動

Ashmem的含義是匿名共享記憶體,通過這種機制,可以為使用者空間程式提供分配記憶體的機制


3.2.2 Binder 45                                  //基於OpenBinder系統的驅動,為Android平臺提供IPC支援:

Android的Binder驅動程式為使用者層程式提供了IPC(程序間通訊)支援,Android整個系統的執行以來Binder驅動。


3.2.3 Logger 46                                 //輕量級的log驅動

Android的Logger驅動程式為使用者程式提供log支援,這個驅動作為一個工程來使用


3.3 Android使用的裝置驅動 46        

作為主要為智慧手機定製的作業系統,Android通常可以使用Linux中一些標準的裝置驅動程式。

3.3.1 Framebuffer顯示驅動 46

在Linux中,Framebuffer驅動是標準的顯示裝置驅動:對於PC系統,Framebuffer驅動是顯示卡的驅動:對於嵌入式系統的SOC處理器,Framebuffer
3.3.2 Event輸入裝置驅動 48

   Input驅動程式是linux輸入裝置的驅動程式,分成遊戲杆(joystick)、滑鼠(mouse和mice)和事件裝置(Event queue)3種驅動程式。其中事件驅動程式是目前通用的驅動程式,可支援鍵盤、滑鼠、觸控式螢幕等多種輸入裝置。


3.3.3 v4l2攝像頭——視訊驅動 50

V4L的全稱是Video4Linux(Video for Linux),是Linux核心中標準的關於視訊驅動程式。
3.3.4 OSS音訊驅動 53


3.3.5 ALSA音訊驅動 54


3.3.6 MTD驅動 56


3.3.7 藍芽驅動 57


3.3.8 Wlan驅動 58



第4章 Android的底層庫和程式 60
4.1 底層庫和程式的結構 60
4.1.1 本地實現的基本結構 60

Android的本地實現層次具有基本的庫和程式,這些苦和程式是Android基本系統執行的基礎
4.1.2 增加本地程式和庫的方法 61

Android中增加被底層徐或者庫,這些程式和庫與其所在的路徑沒有關係,只和他們的Android.mk檔案有關係。

Android.mk和普通的Makefile略有不同,Android.mk具有統一的寫法,主要包含一些系統公共的巨集。

Android.mk中選項參考一下全路徑:

build/core/config.mk

各個選項的預設值在以下檔案中定義:

build/core/base_rules.mk

在一個Android.mk中也可以生成多個可執行程式、動態庫或者靜態庫。

1. 編譯程式

2. 安裝程式

4.2 標準C/C++庫bionic 64

bionic提供C/C++標準庫的功能,它是一個專門我嵌入式系統設計的輕量級標準庫實現。

bionic 的原始碼和標頭檔案在一下目錄中:

相對於傳統的標準庫實現,如glibc,bionic的體積和記憶體佔用更小。bionic支援表尊C、C++庫絕大部分功能,支援數學庫及NPTL執行緒庫。它還實現了自己的Linker及Loader用於動態庫的建立和載入。

bionic加入了Android獨有的一些功能,比如log的底層支援。另外它還實現了一套property系統,這是整個Android全域性變數的儲存區域,bionic使用共享記憶體的方式來實現property系統。
4.3 C語言工具庫libcutils 65

C語言工具庫提供了C語言中最基本的工具功能。這是Android本地種最基礎的庫,基本上Android中所有的本地庫和程式都連線了這個庫。


4.4 init可執行程式 66

init京城是android 啟動後,系統執行的第一個名稱為init的可執行程式。這個程式可以一個守程序的方式執行,它提供了一下功能:

裝置管理:

解析啟動指令碼 init.rc:

執行啟動指令碼中的基本功能:

執行啟動指令碼中的各種服務:

init程序的程式碼路徑為:system/core/init.

init可執行檔案是系統執行的第一個使用者空間的程式,它以守護程序的方式執行。因此這個程式的init.c檔案包含main函式的入口。
int main(int argc, char **argv)
{
    int device_fd = -1;
    int property_set_fd = -1;
    int signal_recv_fd = -1;
    int keychord_fd = -1;
    int fd_count;
    int s[2];
    int fd;
    struct sigaction act;
    char tmp[PROP_VALUE_MAX];
    struct pollfd ufds[4];
    char *tmpdev;
    char* debuggable;

    act.sa_handler = sigchld_handler;
    act.sa_flags = SA_NOCLDSTOP;
    act.sa_mask = 0;
    act.sa_restorer = NULL;
    sigaction(SIGCHLD, &act, 0);

    /* clear the umask */
    umask(0);

        /* Get the basic filesystem setup we need put
         * together in the initramdisk on / and then we'll
         * let the rc file figure out the rest.
         */
    mkdir("/dev", 0755);
    mkdir("/proc", 0755);
    mkdir("/sys", 0755);

    mount("tmpfs", "/dev", "tmpfs", 0, "mode=0755");
    mkdir("/dev/pts", 0755);
    mkdir("/dev/socket", 0755);
    mount("devpts", "/dev/pts", "devpts", 0, NULL);
    mount("proc", "/proc", "proc", 0, NULL);
    mount("sysfs", "/sys", "sysfs", 0, NULL);

        /* We must have some place other than / to create the
         * device nodes for kmsg and null, otherwise we won't
         * be able to remount / read-only later on.
         * Now that tmpfs is mounted on /dev, we can actually
         * talk to the outside world.
         */
    open_devnull_stdio();
    log_init();
    
    INFO("reading config file\n");
    parse_config_file("/init.rc");

    /* pull the kernel commandline and ramdisk properties file in */
    qemu_init();
    import_kernel_cmdline(0);

    get_hardware_name();
    snprintf(tmp, sizeof(tmp), "/init.%s.rc", hardware);
    parse_config_file(tmp);

    action_for_each_trigger("early-init", action_add_queue_tail);
    drain_action_queue();

    INFO("device init\n");
    device_fd = device_init();

    property_init();
    
    // only listen for keychords if ro.debuggable is true
    keychord_fd = open_keychord();

    if (console[0]) {
        snprintf(tmp, sizeof(tmp), "/dev/%s", console);
        console_name = strdup(tmp);
    }

    fd = open(console_name, O_RDWR);
    if (fd >= 0)
        have_console = 1;
    close(fd);

    if( load_565rle_image(INIT_IMAGE_FILE) ) {
    fd = open("/dev/tty0", O_WRONLY);
    if (fd >= 0) {
        const char *msg;
            msg = "\n"
        "\n"
        "\n"
        "\n"
        "\n"
        "\n"
        "\n"  // console is 40 cols x 30 lines
        "\n"
        "\n"
        "\n"
        "\n"
        "\n"
        "\n"
        "\n"
        "             A N D R O I D ";
        write(fd, msg, strlen(msg));
        close(fd);
    }
    }

    if (qemu[0])
        import_kernel_cmdline(1); 

    if (!strcmp(bootmode,"factory"))
        property_set("ro.factorytest", "1");
    else if (!strcmp(bootmode,"factory2"))
        property_set("ro.factorytest", "2");
    else
        property_set("ro.factorytest", "0");

    property_set("ro.serialno", serialno[0] ? serialno : "");
    property_set("ro.bootmode", bootmode[0] ? bootmode : "unknown");
    property_set("ro.baseband", baseband[0] ? baseband : "unknown");
    property_set("ro.carrier", carrier[0] ? carrier : "unknown");
    property_set("ro.bootloader", bootloader[0] ? bootloader : "unknown");

    property_set("ro.hardware", hardware);
    snprintf(tmp, PROP_VALUE_MAX, "%d", revision);
    property_set("ro.revision", tmp);

        /* execute all the boot actions to get us started */
    action_for_each_trigger("init", action_add_queue_tail);
    drain_action_queue();

        /* read any property files on system or data and
         * fire up the property service.  This must happen
         * after the ro.foo properties are set above so
         * that /data/local.prop cannot interfere with them.
         */
    property_set_fd = start_property_service();

    /* create a signalling mechanism for the sigchld handler */
    if (socketpair(AF_UNIX, SOCK_STREAM, 0, s) == 0) {
        signal_fd = s[0];
        signal_recv_fd = s[1];
        fcntl(s[0], F_SETFD, FD_CLOEXEC);
        fcntl(s[0], F_SETFL, O_NONBLOCK);
        fcntl(s[1], F_SETFD, FD_CLOEXEC);
        fcntl(s[1], F_SETFL, O_NONBLOCK);
    }

    /* make sure we actually have all the pieces we need */
    if ((device_fd < 0) ||
        (property_set_fd < 0) ||
        (signal_recv_fd < 0)) {
        ERROR("init startup failure\n");
        return 1;
    }

    /* execute all the boot actions to get us started */
    action_for_each_trigger("early-boot", action_add_queue_tail);
    action_for_each_trigger("boot", action_add_queue_tail);
    drain_action_queue();

        /* run all property triggers based on current state of the properties */
    queue_all_property_triggers();
    drain_action_queue();

        /* enable property triggers */   
    property_triggers_enabled = 1;     

    ufds[0].fd = device_fd;
    ufds[0].events = POLLIN;
    ufds[1].fd = property_set_fd;
    ufds[1].events = POLLIN;
    ufds[2].fd = signal_recv_fd;
    ufds[2].events = POLLIN;
    fd_count = 3;

    if (keychord_fd > 0) {
        ufds[3].fd = keychord_fd;
        ufds[3].events = POLLIN;
        fd_count++;
    } else {
        ufds[3].events = 0;
        ufds[3].revents = 0;
    }

#if BOOTCHART
    bootchart_count = bootchart_init();
    if (bootchart_count < 0) {
        ERROR("bootcharting init failure\n");
    } else if (bootchart_count > 0) {
        NOTICE("bootcharting started (period=%d ms)\n", bootchart_count*BOOTCHART_POLLING_MS);
    } else {
        NOTICE("bootcharting ignored\n");
    }
#endif

    for(;;) {
        int nr, i, timeout = -1;

        for (i = 0; i < fd_count; i++)
            ufds[i].revents = 0;

        drain_action_queue();
        restart_processes();


        if (process_needs_restart) {
            timeout = (process_needs_restart - gettime()) * 1000;
            if (timeout < 0)
                timeout = 0;
        }


#if BOOTCHART
        if (bootchart_count > 0) {
            if (timeout < 0 || timeout > BOOTCHART_POLLING_MS)
                timeout = BOOTCHART_POLLING_MS;
            if (bootchart_step() < 0 || --bootchart_count == 0) {
                bootchart_finish();
                bootchart_count = 0;
            }
        }
#endif
        nr = poll(ufds, fd_count, timeout);
        if (nr <= 0)
            continue;

        if (ufds[2].revents == POLLIN) {
            /* we got a SIGCHLD - reap and restart as needed */
            read(signal_recv_fd, tmp, sizeof(tmp));
            while (!wait_for_one_process(0))
                ;
            continue;
        }
        if (ufds[0].revents == POLLIN)
            handle_device_fd(device_fd);
        if (ufds[1].revents == POLLIN)
            handle_property_set_fd(property_set_fd);
        if (ufds[3].revents == POLLIN)
            handle_keychord(keychord_fd);
    }
    return 0;
}

2. 啟動指令碼init.rc

在android 中使用啟動指令碼init.rc,可以在系統的初始化過程中進行一些簡單的初始化操作。

init啟動指令碼的路徑:system/core/rootdir/init.rc

這個指令碼被直接安裝到目標系統的根檔案系統中,被init可執行程式解析。

init.rc是在init啟動後被執行的啟動指令碼,其語法主要包含了以下的內容:

Commands:命令

Actions:動作

Triggers:觸發條件

Services:服務

Options:選項

Properties:屬性


4.5 Shell工具 72


4.6 C++工具庫libutils 75

libutils是android的底層庫,這個庫以C++實現,它提供的API也是C++的。android層次的本地C/C++程式和庫,大都基於libutils開發

4.6.1 libutils的基本內容 75
4.6.2 Binder 76

Binder用於程序間的通訊(IPC),它是Android平臺的最核心機制之一,也是支撐這個系統執行的基礎。

1. Binder簡介

Android中的Binder最早來源於OpenBinder,Google為了擺脫OpenBinder的許可問題,重新開發了自己的一套Binder實現,基於寬鬆的Apache協議釋出,架構OpenBinder類似。

相對於socket,Pipe等傳統的IPC機制,Binder有著更系統化的架構,他可以實現遠端函式呼叫(RPC),傳遞結構化的引數和返回值。而且在使用者看來,遠端函式呼叫與本地函式呼叫無異。Binder使用同步的函式呼叫模式,就如同執行緒前移一般,將使用者京城的引數帶到服務程序進行呼叫,再帶著結果返回。

Binder的效能相對於本地函式來說,雖然有一定的差距,但對於智慧手機這類外設眾多、架構越發複雜、設計週期往往比較短的裝置來說,採用Binder這樣的IPC方式組織眾多功能模組,看了一更有效地分離模組設計,降低模組兒間耦合,也能更好地利用底層作業系統的程序管理特性來輔助管理服務排程。目前大部分智慧平臺都很好利用了Binder或D-BUS等IPC機制,作為平臺提供系統服務的基礎。

關於Binder,需要注意的是,本節主要針對native部分的Binder實現:er針對Java部分的是實現見第五章

2. Binder基本架構

Binder可以分為多個層次,主要的機制在本地實現,在Java層也可以使用。


1. 驅動層

組織Binder服務節點,排程Binder相關處理執行緒,完成實際的BInder傳輸等。

2. 驅動適配層

主要是IPCThreadState.cpp與ProcessState.cpp , 還包括Parcel.cpp中的一些函式。絕大部分binder相關功能,都有他們完成與Binder驅動的溝通。這是Binder重要組成部分。

3.  Binder核心框架層

Binder核心框架主要是IBinder及它的兩個雷,即BBinder和BpBinder,分別代表最基本的伺服器端及客戶端。

4. Binder框架層

       Binder框架在native(CPP)和Java部分分別有一套實現,單模式都是類似的。 

5. Binder、服務和客戶端實現層

2. Binder基本架構

3. Binder服務端和客戶端實現

在native層中,要定義新的Binder伺服器和客戶端,其實也就是實現第4層和第五層。主要使用的標頭檔案是IInterface.h,其中定義了主要有外部使用的幾個類,IInterface的定義如下所示:

             class IInterface : public virtual RefBase{

                    public:sp<IBinder> asBinder();

                              sp<const IBinder>  asBinder() const;

                    protected:virtual  IBinder *       onAsBinder() = 0;

                 };


4.6.3 libutils中的其他內容 82

其中比價重要的類是在IMemory.h中定義的兩個和記憶體相關的類。ImemoryHeap類定義如下:

class IMemoryHeap : public IInterface

{

         public:DECLARE_META_INTERFACE(MemoryHeap);

            enum{

                   READ_ONLY = 0x00000001;

                   MAP_ONCE  = 0x00000002

            };

            virtual int    getHeapID()  const  = 0;

           virtual  void*   getBase()  const = 0;

            virtual   size_t   getSize()   const  = 0;

           virtual uint32_t   getFlags()   const  = 0;

           int32_t   heapID  const

}

Imermory類的定義如下:

class IMemory :  public IInterface{

   public:

}

IMemoryHeap介面表示一塊堆上的記憶體,IMemory介面表示一塊可以任意使用的記憶體。IMemory的建構函式中有一個引數IMemoryHeap,一個IMemory可以看做使用IMemoryHeap這個堆上記憶體的一部分。

    MemoryHeapBase是IMemoryHeap介面的一個實現,MemoryBase是IMemory介面的一個實現。

    MemoryHeapBase的定義如下所示:

    class MemoryHeapBase  : public virtual BnMemoryHeap{

     public :

   }


4.7 Android的系統程序 85

幾個重要系統程序為/init,/system/bin/servicemanager ,/system/bin/mediaserver,system_server及zygote

前面的init分析提到init通過解析init.rc,啟動對應服務程式。servicemanager , zygote 和mediaserver都通過這種方式啟動:system_server則通過zygote孵化出來。這幾個程序是Android系統執行的基礎,通過了解他們,可以展現一副Android執行是的全景圖。

4.7.1 servicemanager

      servicemanager是Binder的服務管理守護程序,也是Binder機制的核心,所有Binder服務都會通過他進行註冊,客戶端在通過他獲取服務介面。

      servicemanager是一個本地應用,位於:frameworks/base/cmds/servicemanager/,生成名為servicemanager的可執行檔案。

       servicemanager程式碼邏輯非常簡潔

         int  main(int args,char **argv){

           struct  binder_state *bs;

             void *svcmgr  = BINDER_SERVICE_MANAGER;

             ba = binder_open(128*1024);

               if(binder_become_context_manager(bs)){

                     LOGE("cannot become context manger(%s)\n",strerror(errno));

                    return  -1;

                }

                svcmgr_handle = svcmgr;

                binder_loop(bs,svcmgr_handler);

                 return 0;

              }

          servicemanager的執行流程為:開啟Binder驅動-->成為服務管理金曾--->進入binder_loop等待訪問請求。

         binder_loop 通過傳送BINDER_WRITE_READ的ioctl命令給底層Binder驅動,一度去服務註冊和獲取服務介面等請求。再經由binder_parse,最終由svcmgr_handler完成處理。

          支援的請求有:

                  enum{

                     }

             新增服務的實質是在Bind