一般编译android应用,生成apk,有以下几个方法:
1.传统的在eclipse平台下编译生成apk,这种情况下,需要指定android sdk,通过eclipse编译生成apk,编译的apk拥有较低级别的用户级别权限,可能有些底层操作没有权限实现,比如部分system调用,访问系统文件目录等。如果存在jni调用,则需要创建jni目录,并在jni目录里面创建android.mk,通过ndk编译生成动态库.so供上层调用,生成的动态库必须放在libs/armeabi或者libs/armeabli-v7a目录下面,在eclipse打包时候才能将.so打包到apk中,安装此apk,会安装到/data/app目录下,同时将apk和so文件写入/data/相应目录下,表示安装成功。
2.如果需要生成apk拥有system权限,则需要在AndroidManifest.xml中指定android.sharedUserId="android.uid.system"里获取system权限,同时在生成apk后要进行签名,签名命令类似如下格式:
java -jar signapk.jar platform.x509.pem platform.pk8 old.apk new.apk
这样生成的apk会比1中生成的apk拥有较高级别的权限。platform.x509.pem platform.pk8这两个文件一般在所在android源码系统中的build\target\product\security目录下面,signapk.jar为一个签名工具,一般在源码out/hostlinux-x86/framework/signapk.jar 目录下面。
3.无论1和2,android应用均只能调用开放的SDK中api或者属性参数,由于SDK是android framework的一个子集,有大量framework中的api或者属性并没有开放给SDK,因此android上层应用无法调用。如果想调用framework中的一些未开放到SDK的api,同时要求apk拥有系统级权限,那么只能将android应用放到源码里进行编译。我将android应用放到android源码中的packages/apps/中,通过mmm命令来编译,这些应用可以调用framework的api并拥有系统级权限,生成的apk在out/target/product/dt307sq/system/app/下,dt307sq是硬件平台名称。packages/apps/下有大量系统级应用,这些应用在源码编译的时候会随源码一起编译,随系统一起安装,安装在系统/system/app下,这些系统级应用可以作为我们的参考。对于牵涉到了jni调用的情况,同方法1,必须编译出.so库,才能编译成功。比如源码里面有个Bluetooth的例子,其目录结构为
Android.mk内容如下:
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := \
$(call all-java-files-under, src)
LOCAL_PACKAGE_NAME := Bluetooth
LOCAL_CERTIFICATE := platform
LOCAL_JNI_SHARED_LIBRARIES := libbluetooth_jni
LOCAL_JAVA_LIBRARIES := javax.obex
LOCAL_STATIC_JAVA_LIBRARIES := com.android.vcard
LOCAL_REQUIRED_MODULES := libbluetooth_jni bluetooth.default
LOCAL_PROGUARD_ENABLED := disabled
include $(BUILD_PACKAGE)
include $(call all-makefiles-under,$(LOCAL_PATH))
LOCAL_JNI_SHARED_LIBRARIES := libbluetooth_jni表示该apk依赖libbluetooth_jni这个模块,这个模块的编译脚本在jni目录下:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
com_android_bluetooth_btservice_AdapterService.cpp \
com_android_bluetooth_hfp.cpp \
com_android_bluetooth_a2dp.cpp \
com_android_bluetooth_hid.cpp \
com_android_bluetooth_hdp.cpp \
com_android_bluetooth_pan.cpp
LOCAL_C_INCLUDES += \
$(JNI_H_INCLUDE) \
LOCAL_SHARED_LIBRARIES := \
libandroid_runtime \
libnativehelper \
libcutils \
libutils \
libhardware
#LOCAL_CFLAGS += -O0 -g
LOCAL_MODULE := libbluetooth_jni
LOCAL_MODULE_TAGS := optional
include $(BUILD_SHARED_LIBRARY)
这个生成libbluetooth_jni.so
这样通过mmm编译的apk包含进了.so库。
但有这样一种情况,.so库是在外面通过android ndk编译生成的,没有在源码里面编译,这个就是第三方库,那么如何将.so库打到apk里面呢。刚开始,我想到一个办法。将.so文件放到/lib/armeabi/这个目录里面,然后再通过mmm编译生成apk,但发现apk里面根本没有lib这个目录。后来我又尝试另外一种方法:
改下android.mk如下:
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_STATIC_JAVA_LIBRARIES:= xstream
LOCAL_STATIC_LIBRARIES:= libnative-backendservice-jni
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES += $(call all-java-files-under, src)
LOCAL_SRC_FILES += $(call all-java-files-under,gen)
LOCAL_PACKAGE_NAME := ClientAgent
LOCAL_CERTIFICATE := platform
#LOCAL_PROGUARD_NEALBED: = false
#LOCAL_PROGUARD_FLAG_FILES := proguard.cfg
include $(BUILD_PACKAGE)
include $(CLEAR_VARS)
LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES:=xstream:lib/xstream-1.4.4.jar
include $(BUILD_MULTI_PREBUILT)
include $(CLEAR_VARS)
LOCAL_PREBUILT_LIBS:=libnative-backendservice-jni:lib/armeabi/libnative-backendservice-jni.so
include $(BUILD_MULTI_PREBUILT)
# Use the folloing include to make our test apk.
#include $(call all-makefiles-under,$(LOCAL_PATH))
这种方法通过include $(BUILD_MULTI_PREBUILT)集成第三方库,但发现这种方法只是在编译apk的时候,将第三方库自动拷贝到out/target/product/dt307sq/system/lib下,根本没有在apk里面打包第三方.so库。
最终发现以下办法能将第三方库打包到源码编译的apk中。
1).将第三方库.so放到应用下的lib/armeabi中
2).通过mmm编译出apk,这个apk里面没有包含lib目录
3).通过aapt命令,添加lib/armeabi里的.so库,例如:./aapt a ../../../out/target/product/dt307sq/system/app/ClientAgent.apk lib/armeabi/libnative-backendservice-jni.so
aapt命令在源码out/host/linux-x86/bin下,是很强大的工具,注意一定要带lib/armeabi/目录,apk会根据名称生成对应的目录
4).最后签名,例如:java -jar signapk.jar platform.x509.pem platform.pk8 ../../../out/target/product/dt307sq/system/app/ClientAgent.apk ClientAgentSign.apk
生成的apk,具有系统级权限,在源码里编译生成的,同时需要调用的jni动态库是在源码外面通过android ndk编译生成的。
一般情况下,需要在源码里编译,最好android应用和jni动态库部分都通过源码来编译,这是最好的方法。但是有些情况下,需要调用大量第三方库,因为源码编译环境下,这些第三方库不一定能编译通过,那就只能在外面编译好了,然后加到源码编译生成的apk中。