一、开发者的难言之隐---讨厌的集成
在?Android实际开发过程中,每个供应商都会有自己专有的开发库如驱动程序、常用API的封装等。如何把这些用于开发的库无缝地集成到Android框架中成为了开发者最为头痛的事,每添加一个新的库就需要把?Android框架翻个遍,寻找合适的地方放置自己的代码,到最后把 Android的源码改得支离破碎、面目全非,调试?Bug
或者查看代码得在若大的框架中翻来翻去,一片混乱。如果你以为这是最痛苦的事,那就错了,最痛苦的事莫过于?Android平台版本切换,由于?Android版本更新很快,隔三五几个月,就要把幸苦添加到框架中的代码找出来,重新添加到新的版本中去,费时费力,令开发人员苦不甚言。但是项目要继续,这些工作必须进行。如何添加自己的库又不影响?Android框架源码呢?如何让要集成的代码与框架耦合度最低,能在各个版本中便捷切换呢?谁能解救我们?
?
二、开发者的福音---自定义共享库
由于?Google自己也在开发第三方库如?Google Map API 等,加上合作伙伴的抱怨,Google也意识到了这个问题的严重性,于是提供了一套用于开发自定义共享库的轻量级框架——自定义共享库。通过使用该框架,不但可以添加驱动层到应用层的代码,还有其它一些有趣的功能。该框架把所有第三方源码整合到一个目录中,可以自由添加删除,便捷的移动,同时也保证了第三方代码的完整性。
使用该框架可以完成的任务包括:
?* 封装硬件驱动。
?* 封装?Java API。
?* 移植?C/C++代码到?Android 平台。
?* 编写原生应用程序。
?* 自定义模拟器皮肤。
?
三、开发者的实践---sample示例解读。
在源码根目录下有个?vendor(供应商) 目录,专门用于存放各个供应商的会有代码。其中有一个个?sample目录,这是?Google 用于示范如何编写自定义共享库的示例,它展示了自定义共享库、JNI调用、对库的使用方法及皮肤定制等功能。下面我们通过对该示例进行分析,让大家熟悉这个轻量级的框架。
?
1、首先看一下sample?目录的结构:
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:该文件用于编写构建规则,默认继承?Android的?make 框架。
frameworks:该目录在这里的意义等同于?Android源码中的?frameworks 。
????PlatformLibrary:该目录就自定义共享库。
apps:该目录用于编写依赖该库的应用程序。经过测试也可以用来编写不依赖该库的程序,这有个好处,让开发商可以把自己特有的应用集成到框架中。
????client 与?upgrade:这是两个依赖该库的应用程序示例。
products:该目录中的文件对包含该库与?Android框架集成的信息,如模块名称等。
????AndroidProducts.mk:指明该模块的?make配置文件的在哪里。
????sample_addon.mk:模块的配置信息。
sdk_addon:该目录对该库的硬件需求进行定义。
?????hardware.ini:定义模块对硬件的需求。
????manifest.ini:模块的说明文件。名称、供应商等。
skins:该目录用于存放自定义皮肤。
?????WVGAMedDpi:已经定义好的一套皮肤。
2.如何封装?Java共享库?
?
PlatformLibrary 为我们展示了封装?Java共享库的方法。其目录结构如下:frameworks/PlatformLibrary
├── Android.mk
├── com.example.android.platform_library.xml
├── java
│?? └── com
│??????└── example
│??????????└── android
│??????????????└── platform_library
│??????????????????└── PlatformLibrary.java
└── README.txt
?
Android.mk:该文件说明如何构建该模块。
com.example.android.platform_library.xml:该文件是模块注册时需要的文件。该文件需要被放置到?/system/etc/permissions目录下。
Java /*:Java 源码所在目录。具体步骤:
a、编写?Java库,并将源码放到?java 目录下。这一步和编写普通?Java程序没有差别。
b、编写?Android.mk,内容如下:
# 获得当前目录,清空环境变量
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)# 源码所在目录,all-subdir-java-files 表示所有了目录中的?Java文件。
LOCAL_SRC_FILES := \
??? $(callall-subdir-java-files)# 该模块是可选的。
LOCAL_MODULE_TAGS := optional# Java 模块名称
LOCAL_MODULE:= com.example.android.platform_library# 编译为?Java 库。最近以?jar的形式而不是?apk 的形式存在。
include $(BUILD_JAVA_LIBRARY)# 构建该库的?API 文档
include $(CLEAR_VARS)
LOCAL_SRC_FILES := $(call all-subdir-java-files) $(callall-subdir-html-files)
LOCAL_MODULE:= platform_library
# 文档对应的库
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,内容如下:?
?<?xml version="1.0"encoding="utf-8"?>
?<permissions>
???<!-- 库的名称及对应的?Jar文件位置?-->?
???<libraryname="com.example.android.platform_library"
???file="/system/framework/com.example.android.platform_library.jar"/>
?</permissions>现在基本的库我们已经编写完成,现在需要对框架中的其它文件进行配置。
d、编写?sample/frameworks/Android.mk,内容如下:
?
# 包含子目录中的所有?make 文件include $(call all-subdir-makefiles)该文件与?sample/Android.mk 文件相同。
e、编写?sample/sdk_addon/manifest.ini,内容如下:# 该模块的名称、供应商及描述
name=Sample Add-On
vendor=Android Open Source Project
description=sample add-on# 构建该模块的?Android 平台代号
api=3# 模块的版本号。必须为整数。
revision=1# 该模块中包括的共享库列表
libraries=com.example.android.platform_library# 对每个库的详细定义,格式如下:
#<library.name>=<name>.jar;<desc># <library.name>:通过前面?libraies 定义的库的名称。
# <name>.jar:包含库?API 的?jar文件。该文件放在?libs/add-on 下面。
com.example.android.platform_library=platform_library.jar;Sampleoptional plaform library该文件还可包括该模块的其它定义,如皮肤等,为了保持该文档清晰易懂的初衷,这里不做介绍,需要了解可以给我邮件。
f、编写?sample/products/sample_addom.mk,内容如下:
?
# 要植入系统镜像的应用及可选类库。可以包括?Java库和本地库。这里我们只有?Java 库。
PRODUCT_PACKAGES := \ com.example.android.platform_library# 把?xml 文件复制到系统镜像中相应的位置去。
PRODUCT_COPY_FILES :=\?????vendor/
sample/frameworks/PlatformLibrary/com.example.android.platform_library.xml:system/etc/permissions/
com.example.android.platform_library.xml# 这个扩展的名称
PRODUCT_SDK_ADDON_NAME := platform_library# 把模块的?manifest 和硬件配置文件复制到系统镜像中相应的位置。PRODUCT_SDK_ADDON_COPY_FILES := \
???vendor/sample/sdk_addon/manifest.ini:manifest.ini \
???vendor/sample/sdk_addon/hardware.ini:hardware.in# 把库的?Jar 包复制到相应的位置。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# 这个扩展继承系统扩展。$(call inherit-product, $(SRC_TARGET_DIR)/product/sdk.mk)# 这个扩展的真实名字。这个名字会用于编译。
# 用?'makePRODUCT-<PRODUCT_NAME>-sdk_addon'的形式来编译此扩展。
PRODUCT_NAME := sample_addon?
?
g、编写?sample/products/AndroidProducts.mk,内容如下:
?PRODUCT_MAKEFILES := \
? $(LOCAL_DIR)/sample_addon.mkh、最后运行make -j8 PRODUCT-sample_addon-sdk_addon,编译扩展。
至此,我们就完成了?Java 库的封装。
?
3、接下来我们再来看如何通过JNI?的方式对?C代码进行封装。
?
a、在?sample/frameworks/PlatformLibrary目录下添加一个文件夹,用于放置?JNI本地代码,目录结构如下:
?
frameworks/PlatformLibrary/jni
├── Android.mk
└── PlatformLibrary.cpp
?
b、把?frameworks/PlatformLibrary/java/com/example/android/platform_library/PlatformLibrary.java
文件改写为?JIN 调用接口,代码如下:package com.example.android.platform_library;import android.util.Config;
import android.util.Log;public final class PlatformLibrary{????
??? static{?/ Load the library.? If it'salready loaded, this does nothing.System.loadLibrary("platform_library_jni");?
??? private intmJniInt = -1;??? publicPlatformLibrary() {}???????/ Test native methods.??? public intgetInt(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.??? privatestatic void yodel(String msg) {
Log.d("PlatformLibrary", "yodel: " + msg);??//
????/ Trivial native method call.? If "bad" is true,this will throw an
????/ exception.??? nativeprivate int getJniInt(boolean bad);??????/ Native method that returns a new string that is the reverseof
????/ the original.? This also calls yodel().??? nativeprivate static String reverseString(String str);
}
c、在?frameworks/PlatformLibrary/jni/PlatformLibrary.cpp中编写?PlatformLibrary.java 中规定本地调用的具体实现。
d、编写?frameworks/PlatformLibrary/jni/Android.mk,内容如下:
?
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# 没有静态库
LOCAL_STATIC_LIBRARIES :=# 包含必须的?JNI 头文件
LOCAL_C_INCLUDES += \
$(JNI_H_INCLUDE)# 编译器选项
LOCAL_CFLAGS +=# 对该模块不进行预编译。使用预编译可以提高模块的性能。
LOCAL_PRELINK_MODULE := false# 把它编译成动态共享库
include $(BUILD_SHARED_LIBRARY)该文件主要定义了本地库的名字、依赖、编译选项及编译方式。
e、修改?frameworks/PlatformLibrary/Android.mk,在末尾添加如下两行:
?
include $(CLEAR_VARS)# 调用子目录中的?make 文件。
?include $(callall-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、添加接下来我们再看看如何添加原生应用程序
添加原生应用程序就很简单了,只需要把按照?Android应用开发的基本方法,写好一个应用,该应用可以依赖这个扩展,也可以不依赖。如?sample中的?client 应用,目录结构如下:apps/client/
├── AndroidManifest.xml
├── Android.mk
└── src
??? └──com?
???????└── example?
???????????└── android?
???????????????└── platform_library?
???????????????????└── client?
???????????????????????└── Client.java
?
a、在应用根目录中添加一个?Android.mk文件,内容如下:?
?
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)LOCAL_MODULE_TAGS := user# 目标名称
LOCAL_PACKAGE_NAME := PlatformLibraryClient# 只编译这个apk包中的java文件
LOCAL_SRC_FILES := $(call all-java-files-under, src)# 使用当前版本的?SDK
LOCAL_SDK_VERSION := current# 依赖使用刚才编写的扩展
LOCAL_JAVA_LIBRARIES := com.example.android.platform_libraryinclude $(BUILD_PACKAGE)
?
b、在?AndroidManifest.xml中添加一句:?
<uses-libraryandroid:name="com.example.android.platform_library"/>
?
c、修改?sdk_addon/sample_addon.mk,在PRODUCT_PACKAGES中添加该?JNI本地库。
?
PRODUCT_PACKAGES := \?
???com.example.android.platform_library \?
???libplatform_library_jni \?
???PlatformLibraryClient
?
d、编译即可。至此,添加?JNI库完毕。
?
5、其他功能如添加皮肤等,这里就不一一示范了,请参考<sdk-src>/vendor/sample。