在源碼根目錄下有個 vendor (供應(yīng)商) 目錄,專門用于存放各個供應(yīng)商的會有代碼。其中有一個個 sample 目錄,這是 Google 用于示范如何編寫自定義共享庫的示例,它展示了自定義共享庫、JNI 調(diào)用、對庫的使用方法及皮膚定制等功能。下面我們通過對該示例進(jìn)行分析,讓大家熟悉這個輕量級的框架。
1、首先看一下 sample 目錄的結(jié)構(gòu):
sample
├── Android.mk
├── apps
│ ├── Android.mk
│ ├── client
│ └── upgrade
├── frameworks
│ ├── Android.mk
│ └── PlatformLibrary
├── MODULE_LICENSE_APACHE2
├── products
│ ├── AndroidProducts.mk
│ └── sample_addon.mk
├── README.txt
├── sdk_addon
│ ├── hardware.ini
│ └── manifest.ini
└── skins
└── WVGAMedDpi
Android.mk: 該文件用于編寫構(gòu)建規(guī)則,默認(rèn)繼承 Android 的 make 框架。
frameworks: 該目錄在這里的意義等同于 Android 源碼中的 frameworks 。
PlatformLibrary: 該目錄就自定義共享庫。
apps: 該目錄用于編寫依賴該庫的應(yīng)用程序。經(jīng)過測試也可以用來編寫不依賴該庫的程序,這有個好處,讓開發(fā)商可以把自己特有的應(yīng)用集成到框架中。
client 與 upgrade: 這是兩個依賴該庫的應(yīng)用程序示例。
products: 該目錄中的文件對包含該庫與 Android 框架集成的信息,如模塊名稱等。
AndroidProducts.mk: 指明該模塊的 make 配置文件的在哪里。
sample_addon.mk :模塊的配置信息。
sdk_addon: 該目錄對該庫的硬件需求進(jìn)行定義。
hardware.ini: 定義模塊對硬件的需求。
manifest.ini: 模塊的說明文件。名稱、供應(yīng)商等。
skins: 該目錄用于存放自定義皮膚。
WVGAMedDpi: 已經(jīng)定義好的一套皮膚。
2.如何封裝 Java 共享庫?
PlatformLibrary 為我們展示了封裝 Java 共享庫的方法。其目錄結(jié)構(gòu)如下: frameworks/PlatformLibrary
├── Android.mk
├── com.example.android.platform_library.xml
├── java
│ └── com
│ └── example
│ └── android
│ └── platform_library
│ └── PlatformLibrary.java
└── README.txt
Android.mk: 該文件說明如何構(gòu)建該模塊。
com.example.android.platform_library.xml: 該文件是模塊注冊時需要的文件。該文件需要被放置到 /system/etc/permissions 目錄下。
Java /*: Java 源碼所在目錄。具體步驟:
a、編寫 Java 庫,并將源碼放到 java 目錄下。這一步和編寫普通 Java 程序沒有差別。
b、編寫 Android.mk,內(nèi)容如下:
# 獲得當(dāng)前目錄,清空環(huán)境變量
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS) # 源碼所在目錄,all-subdir-java-files 表示所有了目錄中的 Java 文件。
LOCAL_SRC_FILES := \
$(call all-subdir-java-files) # 該模塊是可選的。
LOCAL_MODULE_TAGS := optional # Java 模塊名稱
LOCAL_MODULE:= com.example.android.platform_library # 編譯為 Java 庫。最近以 jar 的形式而不是 apk 的形式存在。
include $(BUILD_JAVA_LIBRARY) # 構(gòu)建該庫的 API 文檔
include $(CLEAR_VARS)
LOCAL_SRC_FILES := $(call all-subdir-java-files) $(call all-subdir-html-files)
LOCAL_MODULE:= platform_library
# 文檔對應(yīng)的庫
LOCAL_DROIDDOC_OPTIONS := com.example.android.platform_library
# 庫的類型
LOCAL_MODULE_CLASS := JAVA_LIBRARIES
LOCAL_DROIDDOC_USE_STANDARD_DOCLET := true # 編譯為 Java API。
include $(BUILD_DROIDDOC)
c、編寫 com.example.android.platform_library.xml,內(nèi)容如下:
< xml version=1.0 encoding=utf-8 >
file=/system/framework/com.example.android.platform_library.jar/> 現(xiàn)在基本的庫我們已經(jīng)編寫完成,現(xiàn)在需要對框架中的其它文件進(jìn)行配置。
d、編寫 sample/frameworks/Android.mk, 內(nèi)容如下:
# 包含子目錄中的所有 make 文件 include $(call all-subdir-makefiles) 該文件與 sample/Android.mk 文件相同。
e、編寫 sample/sdk_addon/manifest.ini,內(nèi)容如下: # 該模塊的名稱、供應(yīng)商及描述
name=Sample Add-On
vendor=Android Open Source Project
description=sample add-on # 構(gòu)建該模塊的 Android 平臺代號
api=3 # 模塊的版本號。必須為整數(shù)。
revision=1 # 該模塊中包括的共享庫列表
libraries=com.example.android.platform_library # 對每個庫的詳細(xì)定義,格式如下:
# =.jar; # : 通過前面 libraies 定義的庫的名稱。 # .jar: 包含庫 API 的 jar 文件。該文件放在 libs/add-on 下面。 com.example.android.platform_library=platform_library.jar;Sample optional plaform library 該文件還可包括該模塊的其它定義,如皮膚等,為了保持該文檔清晰易懂的初衷,這里不做介紹,需要了解可以給我郵件。 f、編寫 sample/products/sample_addom.mk,內(nèi)容如下: # 要植入系統(tǒng)鏡像的應(yīng)用及可選類庫??梢园?Java 庫和本地庫。這里我們只有 Java 庫。 PRODUCT_PACKAGES := \ com.example.android.platform_library # 把 xml 文件復(fù)制到系統(tǒng)鏡像中相應(yīng)的位置去。 PRODUCT_COPY_FILES := \ vendor/ sample/frameworks/PlatformLibrary/com.example.android.platform_library.xml:system/etc/permissions/ com.example.android.platform_library.xml # 這個擴(kuò)展的名稱 PRODUCT_SDK_ADDON_NAME := platform_library # 把模塊的 manifest 和硬件配置文件復(fù)制到系統(tǒng)鏡像中相應(yīng)的位置。 PRODUCT_SDK_ADDON_COPY_FILES := \ vendor/sample/sdk_addon/manifest.ini:manifest.ini \ vendor/sample/sdk_addon/hardware.ini:hardware.in # 把庫的 Jar 包復(fù)制到相應(yīng)的位置。 PRODUCT_SDK_ADDON_COPY_MODULES := \ com.example.android.platform_library:libs/platform_library.jar # 文檔的名稱。必須與。 # LOCAL_MODULE:= platform_library PRODUCT_SDK_ADDON_DOC_MODULE := platform_library # 這個擴(kuò)展繼承系統(tǒng)擴(kuò)展。 $(call inherit-product, $(SRC_TARGET_DIR)/product/sdk.mk) # 這個擴(kuò)展的真實(shí)名字。這個名字會用于編譯。 # 用 'make PRODUCT--sdk_addon' 的形式來編譯此擴(kuò)展。 PRODUCT_NAME := sample_addon g、編寫 sample/products/AndroidProducts.mk,內(nèi)容如下: PRODUCT_MAKEFILES := \ $(LOCAL_DIR)/sample_addon.mk h、最后運(yùn)行make -j8 PRODUCT-sample_addon-sdk_addon,編譯擴(kuò)展。 至此,我們就完成了 Java 庫的封裝。 3、接下來我們再來看如何通過 JNI 的方式對 C 代碼進(jìn)行封裝。 a、在 sample/frameworks/PlatformLibrary 目錄下添加一個文件夾,用于放置 JNI 本地代碼,目錄結(jié)構(gòu)如下: frameworks/PlatformLibrary/jni ├── Android.mk └── PlatformLibrary.cpp b、把 frameworks/PlatformLibrary/java/com/example/android/platform_library/PlatformLibrary.java 文件改寫為 JIN 調(diào)用接口,代碼如下 : package com.example.android.platform_library; import android.util.Config; import android.util.Log; public final class PlatformLibrary { static { / Load the library. If it's already loaded, this does nothing. System.loadLibrary(platform_library_jni); private int mJniInt = -1; public PlatformLibrary() {} / Test native methods. public int getInt(boolean bad) { // this alters mJniInt // int result = getJniInt(bad); // reverse a string, for no very good reason // String reverse = reverseString(Android!); Log.i(PlatformLibrary, getInt: + result + , ' + reverse + '); return mJniInt; // / Simple method, called from native code. private static void yodel(String msg) { Log.d(PlatformLibrary, yodel: + msg); // / Trivial native method call. If bad is true, this will throw an / exception. native private int getJniInt(boolean bad); / Native method that returns a new string that is the reverse of / the original. This also calls yodel(). native private static String reverseString(String str); } c、在 frameworks/PlatformLibrary/jni/PlatformLibrary.cpp 中編寫 PlatformLibrary.java 中規(guī)定本地調(diào)用的具體實(shí)現(xiàn)。 d、編寫 frameworks/PlatformLibrary/jni/Android.mk,內(nèi)容如下: LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE_TAGS := optional # JNI 模塊的名稱 LOCAL_MODULE:= libplatform_library_jni # 依賴的源代碼文件 LOCAL_SRC_FILES:= \ PlatformLibrary.cpp # 編譯時需要的庫 LOCAL_SHARED_LIBRARIES := \ libandroid_runtime \ libnativehelper \ libcutils \ libutils # 沒有靜態(tài)庫 LOCAL_STATIC_LIBRARIES := # 包含必須的 JNI 頭文件 LOCAL_C_INCLUDES += \ $(JNI_H_INCLUDE) # 編譯器選項(xiàng) LOCAL_CFLAGS += # 對該模塊不進(jìn)行預(yù)編譯。使用預(yù)編譯可以提高模塊的性能。 LOCAL_PRELINK_MODULE := false # 把它編譯成動態(tài)共享庫 include $(BUILD_SHARED_LIBRARY) 該文件主要定義了本地庫的名字、依賴、編譯選項(xiàng)及編譯方式。 e、修改 frameworks/PlatformLibrary/Android.mk,在末尾添加如下兩行: include $(CLEAR_VARS) # 調(diào)用子目錄中的 make 文件。 include $(call all-makefiles-under,$(LOCAL_PATH)) f、修改 sdk_addon/sample_addon.mk,在PRODUCT_PACKAGES 中添加該 JNI 本地庫。 PRODUCT_PACKAGES := \ com.example.android.platform_library \ libplatform_library_jni g、編譯即可。至此,添加 JNI 庫完畢。 4、添加接下來我們再看看如何添加原生應(yīng)用程序 添加原生應(yīng)用程序就很簡單了,只需要把按照 Android 應(yīng)用開發(fā)的基本方法,寫好一個應(yīng)用,該應(yīng)用可以依賴這個擴(kuò)展,也可以不依賴。如 sample 中的 client 應(yīng)用,目錄結(jié)構(gòu)如下: apps/client/ ├── AndroidManifest.xml ├── Android.mk └── src └── com └── example └── android └── platform_library └── client └── Client.java a、在應(yīng)用根目錄中添加一個 Android.mk 文件,內(nèi)容如下: LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE_TAGS := user # 目標(biāo)名稱 LOCAL_PACKAGE_NAME := PlatformLibraryClient # 只編譯這個apk包中的java文件 LOCAL_SRC_FILES := $(call all-java-files-under, src) # 使用當(dāng)前版本的 SDK LOCAL_SDK_VERSION := current # 依賴使用剛才編寫的擴(kuò)展 LOCAL_JAVA_LIBRARIES := com.example.android.platform_library include $(BUILD_PACKAGE) b、在 AndroidManifest.xml 中添加一句: c、修改 sdk_addon/sample_addon.mk,在PRODUCT_PACKAGES 中添加該 JNI 本地庫。 PRODUCT_PACKAGES := \ com.example.android.platform_library \ libplatform_library_jni \ PlatformLibraryClient d、編譯即可。至此,添加 JNI 庫完畢。 5、其他功能如添加皮膚等,這里就不一一示范了,請參考/vendor/sample。
- 相關(guān)評論
- 我要評論
-