2010年4月26日 星期一

Andriod 如何添加自定義C++庫

Andriod 如何添加自定義C++庫
1. 底層添加模式,即要在編譯出的 sdk 包含要添加的 c++ 庫
目前使用複制模式,把 Webkit 目錄複製一份,取名為 Tunnel ,在其目錄下,分別修改 Javascripecore 和 webcore 下的 Android.mk

l 修改 LOCAL_MODULE 變量,這個變量的作用是為一個庫取一個獨立的名字,在編譯過程中腳本會判斷會不會有重複的庫,所以修改一下,分別修改為 libtunnelwebcore 和 libtunnelkjs ,這樣就和原有的庫不會有名字衝突。

l 修改 LOCAL_COPY_HEADERS_TO 變量,這個變量用於腳本 COPY 庫中的頭文件的時候用的,修改一下名字,腳本會在 out/target/product/generc/obj/include/ 目錄下建立一個“名字”的目錄,把指定的頭文件 Copy 進去。

l 修改 webcore 中的 LOCAL_STATIC_LIBRARIES 變量,此變量用戶指定需要包含的靜態庫,這裡改成前面的 lib tunnelkjs

l 動態庫需要指定 Map 信息,需要修改 build/core/prelink-linux-arm.map 中的信息。

這個 map 文件好像是製定動態庫的地址的,在前面註釋上面有一些地址範圍的信息,注意庫與庫之間的間隔數,如果指定不好的話編譯的時候會提示說地址空間衝突的問題。另外,注意排序,這裡要把數大的放到前面去,按照大小降序排序。

重新編譯即可,可以看到在 out 目錄下面的相對位置會產生剛才添加的靜動態庫。



2. 應用層添加模式,即在發布的 SDK 基礎上添加自定義的 C++ 庫。

此操作的目的就是對於一個第三方而言,要在自己寫的程序中調用自己的 C++ 庫,並且隨著安裝部署到 Android 的環境中的解決方案。

l 在工程的根目錄下,必須有一個 lib 目錄,在 lib 目錄下創建一個名字為 ” armeabi” 子目錄,並將所有用到的 so 庫文件放到該子目錄下。

l Java 編碼方式

在 Java 中裝載 so 庫時需要按以下方法進行:

a). 使用方法 System.load() ,該方法需要使用絕對的路徑。

b). 絕對路徑為 /data/data/”packageName”/lib/libXXX.so

packageName: 是調用方法 System.load() 的 java 類所在包的名字。

libXXX.so: 是所要裝載的 so 庫的名字。

l APK 打包

在對應用進行打包時,需要將前提條件中的 lib 目錄打進最終的 apk 包中,打包後的 apk 包結構為:





注:在這個過程中使用的是 ant 進行的工程構建。

3. 過程分析

這部分對分析的過程進行簡單的記錄,如果只需要知道如何將 so 動態庫隨 apk 一起部署,該部分對你沒有任何幫助。

過程涉及兩個方面:一方面是 apk 的打包,這方面涉及到的工具是 apkbuilder( 這個工具實際上是一個腳本文件,它最終調用了程序 apkbuilder.jar) 。另一方面是 android 對 apk 包的解析部分。

下面描述一個安裝 apk 包的過程。

4. 在 HostOS 中通過命令安裝一個 APK 包: adb install xxx.apk

ADB 的概述:

在 hostOS 的工具集中有一個工具 adb 。在第一次運行 adb 命令時, adb 會 fork 出一個子進程作為運行在 hostOS 中的一個服務,我們將它稱為 ADB-Server ,將運行命令的 adb 稱為 adb-commandline-tool 。每次我們在命令提示符中運行 adb 時都是運行 adb-commandline-tool ,而後 adb-commandline-tool 通過本地套接口鏈接到 ADB-Server ,並將命令行中的參數傳遞給 ADB-Server 。



在 targetOS 啟動時,會啟動一個守護進程 ADBD 。 ADB-Server 啟動時會鏈接到 targetOS 中的 ADBD 。在 ADB-Server 接收到 adb-commandline-tool 傳遞來的參數後,通過套接口將參數傳遞給 ADBD 。然後 ADBD 根據參數在 targetOS 執行不同的命令。



ADBD 接收到 ADB-Server 發送來的數據後,最終調用函數 handle_packet() 。



針對 ”adb install xxx.apk” 命令 handle_packet() 的調用過程如下:






handle_packet()

create_local_service_socket(name) // name: pm xxx.apk

service_to_fd(name) // name: pm xxx.apk

create_subprocess(SHELL_COMMAND, "-c", name + 6);

execl(cmd, cmd, arg0, arg1, NULL);

//cmd: "/system/bin/sh" // arg0: -c // arg1: install xxx.apk

// 注:此處的 xxx.apk 是 targetOS 上的路徑和文件名。

由 1的分析得知最終調用了 pm 工具。 pm 就是 package mamanger ,它是一腳本文件

內容如下:

base=/system

export CLASSPATH=$base/framework/pm.jar

exec app_process $base/bin com.android.commands.pm.Pm "$@"

給腳本實際上調用了工具 app_process ,並傳遞了四個參數:

參數 1: /system/bin

參數 2: com.android.commands.pm.Pm

參數 3: -c

參數 4: install xxx.apk



下面來考量 app_process 的調用流程,只是粗略的記錄了分析的過程。 App_process 源文件在 frameworks\base\cmds\app_process\app_main.cpp 中。

reference from: http://tony-life2000.spaces.live.com/blog/cns!7DA77E607F2B16BF!429.entry?wa=wsignin1.0&sa=78101085

1 則留言:

  1. Hi JavaTai,
    請問能否在Android.mk裡面定義自己的變數,然後在C code裡面引用它?
    Thanks,
    Eric

    回覆刪除