Android框架理解之USB
目的是看一下android下的usb和linux下的usb有什麼區別?如果現在就能回答,就不用往下看了。
我的理解是android下沒有usb。
一 Android誕生的背景
乍一看這個話題與android框架似乎毫無關係,但是每一個新生事物的誕生必然取決於當時的環境,環境造就了這個新生事物,同時這個新生事物也應該承載著歷史所賦予他的責任,他也將因此擁有某種能力,來完成他的使命。
最近幾年手機的快速增長大家是有目共睹,手機已經擺脫了他最初的角色:打電話。他開始發簡訊、上網、打遊戲、看電影、聊天等等。基於手機的增值服務對手機軟體環境的需求越來越大。Android誕生前的手機作業系統是什麼樣子的?symbian
通過以上的總結,可以看出android最重要的是給java開發人員提供了應用軟體的開發環境。至此,我們終於引出了下一個話題:android是怎麼實現這個機制的?讓java和linux(一個C環境)融合在了一起。
瞭解一個新的事物,不一定要從應用者的角度、也不一定要從研發人員的角度,我們還可以從設計者的角度來考慮、從他誕生的環境來切入等等,並沒有固定的,只是在不同的階段可以考慮從不同的角度來切入。
二是JNI
上面我們只是說android讓java開發人員能夠方便的進行手機應用軟體開發了,更具體的說應該是更方便的在linux作業系統上以java語言進行應用軟體開發了。所以說android不是作業系統,是linux作業系統下的一個框架。一個可以使用java語言進行開發的框架,一個實現了通過java呼叫C或者C++(.so)進而linux作業系統的框架。這個框架是執行在linux系統上的一個程式,沒有這個框架,下面的linux系統已經完全能夠實現我們所需要的應用(和使用android達到一樣的效果),但是他不夠人性化。Linux強大的可移植性和java的平臺無關性,兩者的結合簡直就是perfect。
上層應用和UI有java來完成,底層的硬體有C/C++來完成。如果底層的硬體有改動,只需要改動lib層(.so)和kernel層就可以了(我覺得叫做linux系統層更好)。層是一個很好的概念,層代表著獨立、自由。每一層的首要任務是負責自己層的事情,但是層與層之間需要溝通。Java層在實現某種功能時,如果需要呼叫底層的C元件,那麼就會使用JNI介面。JNI是Java Native Interface的縮寫,譯為Java本地介面。它允許Java程式碼和其他語言編寫的程式碼進行互動。在android中提供JNI的方式,讓Java程式可以呼叫C語言程式。android中很多Java類都具有native介面,這些介面由本地實現,然後註冊到系統中。
JNI程式碼放在以下的路徑中:frameworks/base/core/jni/,這個路徑中的內容被編譯成庫 libandroid_runtime.so
Java轉化為位元組碼之後就需要虛擬機器來執行了。Dalvik virtual machine。在java執行的過程中,如果java類需要使用C++函式,那麼虛擬機器就會載入這個C函式。虛擬機器可以讓java和C之間通過標準的JNI相互呼叫。System.loadLibrary(*.so)這個動作就是java程式要求虛擬機器載入C函式,載入之後java類和.so庫就一起運行了。
下面是一個jni 例子。
關於jni如何使用,這裡就沒有必要介紹了,他只是用來連線Java和C++的一個簡單的機制,結合著java虛擬機器共同完成了java和C++的互動。
到了這裡我們可以考慮一下,JNI這個機制在android作用,他的作用也間接的反映出android的框架。作用就是連線java類和lib庫(.so C++),那麼也就是說android就是兩層:一層是java,另外一層是C++,通過JNI將兩層連線起來。當我們還不瞭解一件事物的時候,最好把他先想的簡單一點,之後再慢慢細分。
那麼為什麼android的框架模型中卻是4層呢?google在這點上我覺得有點不厚道。
圖1
底層的kernel層不屬於google的android,當然屬於linus torvalds還是google,已經不重要了,重要的是誤導了我對android的理解,以為android是一個作業系統,其實他不是。Kernel層的叫法我也不認同,我覺得應該叫做linux系統層。Android為什麼這麼叫?
三挾天子以令諸侯
linux被android綁架了!android本來是執行在linux系統下的一個應用開發框架,但是他的光環已經掩蓋了linux系統,更可怕的是android正在逐步的修改linux系統,作業系統成了傀儡。Google嫁了女兒帶回來一個兒子(linux),這個被linus torvalds養了20年的兒子,給人做了上門女婿,並且在不斷的被洗腦、改造著。而linus torvalds和廣大的linux愛好者還要不斷的給linux提供生活補助。並且這個女兒好像也不是google的,好像是sun的。收養了一個看似普通的女兒,經過打扮,找了一個上門女婿(一個並未施展才華的小夥),組成了一個新的家庭:google稱其為android。1+1 遠遠打大於了 2。
Google正在不遺餘力的包裝著這個新的組合,包裝都是有欺騙性的。但是做為即將在android環境下開發的人員來說,不能被他的宣傳廣告所迷惑,掌握了他的真實脈絡,才能進行開發、移植。
經過上面的感性認識,我們要回歸理性的分析。最底層kernel層(大家都這麼叫),入鄉隨俗,不僅包括核心本身,各種驅動、檔案系統也盡在其中。對硬體的所有基本操作都在這一層,這一層有誰來維護,總之不是google,但是google卻可以在裡邊加入自己的元素:android元素,其實沒有說google這樣做不好的意思,相反很贊成,Google根據這套框架所執行的環境對linux 核心進行不斷的改造,可謂是為嵌入式量身打造。其上一層是lib庫層,為什麼會誕生這一層?我覺得他是為java層而生。Java層的應用程式在試圖操作kernel底層的裝置時,發現很費勁,所以設計者可能考慮需要在java應用層和kernel層之間插入一層:lib層。這一層將裝置驅動層和java應用層聯絡了起來。所以如果我們要讓我們的某個裝置在android的環境下使用起來(如果android當前的框架中無法支援這個裝置),就需要在開發完驅動後,再在lib層用C++寫一組JNI介面函式,供java層呼叫。這個機制並不複雜,這個開發工作是否複雜取決於你要將這個裝置呈現給java開發者的功能,如果只想做個helloworld,能複雜的了麼?所以寫到這我有一個想法(等待以後的驗證):android開發的難易取決於我們以後要實現的某種功能,而這個功能與android環境、linux系統沒有多大關係(可能有點關係,畢竟在人家的地盤)。Lib層之上是所謂的framework層,我覺得從我們公司的性質來看,更傾向於將framework層和app層統稱為java層。這兩層都是java程式,一層是jar包,就是一些封裝好的API函式,一層是使用這些API,對於我們做晶片、做驅動、做方案來說,這兩層就是一層。我們只要理解一個宗旨就可以了:android誕生的目的就是讓java開發人員能夠方便的使用java語言操作各種硬體資源,進行應用程式的開發,所以在framework這一層的jar包僅僅是為app層使用的,僅僅是一個純軟體及別的層面,這一層和lib層相呼應。或者這樣來劃分:jni之上的framework和app叫做軟體層(或應用層),之下的lib和kernel叫做硬體層(或系統層)。軟體層的framework間接的反應著硬體的操作方法和功能,稱其為軟體層中的硬體反映層。Lib層就成為硬體層中的軟體供給層。
軟體中的軟體、軟體中的硬體、硬體中的軟體、硬體中的硬體,簡單理解就兩層;軟體層和硬體層。每一層的開發都基於下一層提供的功能介面。
記得當時有同事討論在android這個環境下能否直接使用C語言開發。中華人民共和國是有共產黨領導的,既然選擇了在這裡生活,你就應該堅持四項基本原則、擁護黨的領導,不要老是想著國外那些情況,動不動就罷工、遊行什麼的。在這裡就要按照我們國家的制度辦事,遊行可以,不過要按照中國的遊行法來做。享受著android帶來的開發便捷,還想著以前的linux,用什麼C語言來開發應用程式,枉費google的一片心血。所以我覺得不是不可以,但要看android的臉色和氣度。我覺得應該是可能的,android雖然強大,綁架了linux系統,但是linux畢竟是一個作業系統,是可以直接執行程式的。這個基於linux系統執行的程式和android是同級別的[m1]。
圖2
http://www.you01.com/dzly/dzswf/2010072932.swf
四從系統裝載過程再認識android
android和kernel是單獨編譯,單獨載入的。可以說是這個真相,觸發了這篇文章。
在編譯android 之後,會生成幾個image 檔案,這些檔案是:
1 ramdisk.img : 一個分割槽影像檔案,它會在kernel 啟動的時候,以只讀的方式被 mount,這個檔案中只是包含了 /init 以及一些配置檔案,這個ramdisk 被用來呼叫init,以及把真正的root file system mount 起來
2 system.img:是包含了整個系統,android 的framework,application 等等,會被掛接到 "/" 上,包含了系統中所有的二進位制檔案
3 userdata.img:將會被掛接到 /data 下,包含了所有應用相關的配置檔案,以及使用者相關的資料
ramdisk.img is a small partition image that is mounted read-only by the kernel at boot time. It only contains /init and a few config files. It is used to start init which will mount the rest of the system images properly and run the init procedure. A Ramdisk is a standard Linux feature.
system.img is a partition image that will be mounted as / and thus contains all system binaries
userdata.img is a partition image that can be mounted as /data and thus contains all application-specific and user-specific data.
Ramdisk是kernel和android的橋樑,linux啟動後掛在ramdisk,然後再ramdisk裡邊有啟動android的指令碼。
Linux核心啟動完成後,會啟動第一個程序:init程序,它是一個由核心啟動的使用者級程序。核心啟動後,就通過啟動一個使用者級程式init的方式,完成引導程序。init始終是第一個程序。
Init程序一起來就根據init.rc指令碼檔案建立了幾個基本的服務:
- servicemanamger
- zygote
………………
init.rc這個腳本里的內容就是android需要維護的,裡邊應當有啟動android的過程。這方面的內容將有zhicheng來講解。
從這個linux-----àandroid的啟動過程,是不是再一次感覺到了層的重要性,層與層之間相互獨立,層與層之間又提供了某種開發的介面,允許對方的侵入。同時從這個啟動過程也能夠知道我們的工作需要落實在哪一層上(根據我們的需求)。
五以特定應用為主線縱向貫穿各層
經過上面的理解,對android應該是有了一定的理解的。理解的目的是要用,如何把這個框架使用起來。我們是做SOC的,還得給他們做解決方案,以前只做驅動就可以了,但是既然在andoird下混了,就要按照android的規則出牌,我們得給人提供jar包、lib庫。我們得有服務精神,讓廣大的java應用開發人員在我們的解決方案上,遊刃有餘的開發應用程式,我們的獎金得依靠他們。
那麼針對某種應用我們都需要幹什麼呢?需求驅動著一切(底層反應著一切),軟體開發人員需要什麼,我們就提供什麼,所以我們從app層的某個特定的功能應用開始。
以向sd卡中寫入一個檔案為例:
下面這段程式碼是從網上找的,用於向sd卡中寫入一個檔案的片段。
if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){ File sdCardDir = Environment.getExternalStorageDirectory();//獲取SDCard目錄,2.2的時候為:/mnt/sdcart 2.1的時候為:/sdcard,所以使用靜態方法得到路徑會好一點。
File saveFile = new File(sdCardDir, "abc.txt");
FileOutputStream outStream = new FileOutputStream(saveFile);
outStream.write("你好".getBytes());
outStream.close();
}
對於一個應用程式來說,必須要知道自己要向哪個目錄中寫入檔案。如果想往sd卡中寫,就需要知道sd卡對應的目錄:Environment.getExternalStorageDirectory();這句話是用來獲取sd卡的儲存目錄,將目錄存放到sdCardDir,比如是/sdcard。
那麼些這個檔案的過程是什麼?outStream.write("你好".getBytes());到底幹了什麼?
進入FileOutputStream.java,可以看到write的具體實現:
public void write(byte[] buffer, int offset, int count) throws IOException {
if (buffer == null) {
throw new NullPointerException(Msg.getString("K0047")); //$NON-NLS-1$
}
if ((count | offset) < 0 || count > buffer.length - offset) {
throw new IndexOutOfBoundsException(Msg.getString("K002f"));
}
if (count == 0) {
return;
}
openCheck();
fileSystem.write(fd.descriptor, buffer, offset, count);
}
然後呼叫到了fileSystem.write,fd.descriptor中含有路徑資訊。
這個write會像一個linux中所謂的scsi裝置中寫入資料,經過一層層傳到sd的驅動層。這個sd裝置應該會在/dev目錄下有一個裝置節點,最終會通過這個裝置節點,呼叫到這個裝置的write驅動函式,實現最終的寫入動作。
當然從fileSystem這個物件的write到裝置驅動的write,中間走過了2層,一個是framework、一個是lib層。(這兩個write已經不是同一個write了,第一個write是fileSystem這個物件呈現給app層的,而第二個write的功能也許沒那麼豐富,這都取決於設計者)。
在kernel層之上,應該能夠找到open這個裝置的地方。如open(“/dev/sd”)。
六你看到題目中的USB了麼?
初衷是看看android中的usb是什麼樣子的,但是都快講完了usb也沒有出現。通過上面的sd的檔案操作,在android的框架中看到sd卡的驅動了麼?我沒有看到,希望永遠不要看到,因為他不應該在android的框架裡出現,應該出現在kernel層(linux系統)。USB
和sd的角色是一樣的。所以也不應該出現在android的框架中、程式碼中。
七未濟的話題
通篇都是在說android只是一個框架、如何撿了便宜,其實這是不客觀的,這麼多人將android認為是一個作業系統,是有它的道理的。Android做了很多東西,並不是簡單的一堆jar包。他在linux系統之上衍生出了一個新的準系統,實現著程式的管理、通訊、儲存等功能。如activity、intent、service、content provider等,這些工作不是一個簡單的程式所能做的,java人員享受的開發之快捷是建立在android所提供的強大功能之上,稱其為android系統並不為過。Android不僅是一個系統,也是一種新的商業模式,一種企業文化的體現。
這些理解並不一定正確,不過是一個開始,還有很多工作需要我們去做。
所以這裡一共涉及到兩個大環境:一個是我們以前的linux環境,一個是android環境。
如果我們不提供解決方案,只提供驅動,只要關注linux環境中的驅動部分就可以。如果提供解決方案,假設已經有了針對硬體的驅動(linux層的驅動),那麼還需要在lib層加入一層C++的程式碼,向上提供操作驅動層的介面。同時還要在framework層針對我們裝置的功能封裝出相應的類和接口出來,並實現該類或介面的方法,提供給app層應用。App層我們不會用到,但是需要了解或讓客戶提需求,因為只有瞭解客戶要怎麼用這個裝置,才能封裝出好的類和介面。不過android系統的東西只要會用、有個瞭解就差不多了。