一、开发者的难言之隐---讨厌的集成
在 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 := \
$(call
all-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) $(call
all-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
文件位置 -->
<library
name="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;Sample
optional 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)
# 这个扩展的真实名字。这个名字会用于编译。
# 用 'make
PRODUCT-<PRODUCT_NAME>-sdk_addon'
的形式来编译此扩展。
PRODUCT_NAME := sample_addon
g、编写 sample/products/AndroidProducts.mk,内容如下:
PRODUCT_MAKEFILES := \
$(LOCAL_DIR)/sample_addon.mk
h、最后运行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'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 中规定本地调用的具体实现。
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 $(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、添加接下来我们再看看如何添加原生应用程序
添加原生应用程序就很简单了,只需要把按照 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_library
include $(BUILD_PACKAGE)
b、在 AndroidManifest.xml
中添加一句:
<uses-library
android: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。
分享到:
相关推荐
本教程将详细介绍如何创建并使用自定义的SO(共享对象)库,并通过一个名为TestDemo的示例进行方法调用。 首先,理解SO库在Android中的作用。SO库是Linux系统中的动态链接库,相当于Windows下的DLL文件。在Android...
Android如何自定义共享库[收集].pdf
下面将详细讲解Android自定义框架的构建过程、设计原则以及如何在小项目中有效利用。 一、自定义框架的设计与构建 1. **模块化设计**:一个良好的自定义框架应具备清晰的模块划分,例如,可以分为网络请求模块、...
Android高级编程雪梨作业之自定义ContentProvider 将任务01生词本作业中生成的生词本数据库通过自定义ContentProvider的方式,共享给其他应用。 要求如下: (1) 使用自定义SQLiteOpenHelper来管理数据库; (2) 提交...
总的来说,Android自定义全局Loading页面是提升用户体验的重要手段。通过巧妙地使用ProgressBar和AVLoadingIndicatorView,我们可以创建出既美观又实用的加载指示器。同时,通过设计合理的加载管理机制,确保Loading...
2. ContentProvider:它是Android系统中用于数据共享的关键组件,可以使其他应用程序(如系统自带的联系人应用)访问你的数据。你需要创建一个自定义的ContentProvider,实现增删查改操作,并注册到AndroidManifest....
在Android系统中,ContentProvider是四大组件之一,它扮演着数据共享和跨应用数据访问的角色。自定义ContentProvider允许开发者创建自己的数据存储解决方案,并与其他应用程序无缝交互。这篇博客将深入探讨如何在...
android自定义相机SurfaceView形式,SurfaceView是一个功能强大的控件,它拥有独立的绘图表面(不与其宿主窗口共享同一个绘图表面)。由于其拥有独立的绘图表面,因此SurfaceView的UI就可以在一个独立的线程中进行...
android自定义Dialog下载文件 ,在自定义的Dialog上显示文件下载的速度和ProgressBar进度,共享给大家! 欢迎指点提议 Email:vipa1888@163.com QQ 840950105 Author: spring sky
ContentProvider是Android中数据共享的关键机制,它提供了统一的数据访问接口,使得不同应用间可以安全地交互数据。 其次,实现自定义头像的关键在于图像处理。项目中可能使用了Bitmap类来加载和处理图像,例如从...
在Android开发中,自定义Dialog和Activity的跳转样式是提升用户体验和应用独特性的重要手段。自定义Dialog可以使应用程序在提示用户信息或者进行选择时,展现出更符合应用风格的界面,而自定义Activity跳转样式则能...
总之,自定义ContentProvider和使用ContentResolver是Android平台中实现跨应用数据共享的重要手段,理解并熟练掌握这两者对于提升Android应用的可扩展性和可维护性至关重要。在实际项目中,根据需求合理设计和实现...
在实际开发中,你可能还需要考虑如何在多个应用之间共享自定义权限,这通常涉及签名一致性、权限声明以及跨应用通信等方面。通过合理的权限设计,开发者可以更好地控制应用的功能访问,同时提供给用户更精细的隐私...
- 共享代码库:自定义View可以打包成独立库,供其他项目复用,减少重复工作。 综上所述,创建自定义组合View是Android开发中的一个重要环节,它能够帮助我们打造个性化的用户体验。理解并熟练掌握自定义View的原理...
总结起来,Android自定义权限涉及权限的声明、请求、依赖和共享等多个方面,是开发复杂功能或保护敏感数据时不可或缺的工具。理解和熟练掌握自定义权限的使用,能帮助开发者创建更安全、更可控的Android应用。
本示例将详细解析如何自定义Content Provider,以便在Android应用间实现数据共享。 首先,理解Content Provider的基本结构至关重要。一个完整的Content Provider主要包含以下部分: 1. **ContentContract(数据...
综上所述,这份"android自定义SurfaceView源代码"可能是为了提供一个高效的游戏渲染框架,利用SurfaceView的优势,实现流畅的动画效果和响应式用户交互。通过分析和学习这个源代码,开发者可以更好地理解和掌握...
除了在应用内部请求权限,自定义权限还涉及到跨应用共享。如果其他应用也需要使用这个权限,它们需要在自己的AndroidManifest.xml中声明依赖这个权限。同时,拥有权限的应用需要通过Intent和其他应用进行交互,比如...
本篇将详细阐述Android的数据存储操作,包括SharedPreferences、文件存储、SQLite数据库以及ContentProvider等存储方式,并结合实例代码展示如何实现自定义的ContentProvider。 首先,我们来了解Android数据存储的...