Android build

1. 如何将文件编译到system目录

有时候我们会有需求,要将一个文件直接编译到system镜像中,可以使用如下的代码片段:

LOCAL_PATH:= $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE := test.txt
LOCAL_SRC_FILES := test.txt
LOCAL_MODULE_CLASS := ETC
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_PATH := $(TARGET_OUT)/usr
include $(BUILD_PREBUILT)

比如上述代码就会把test.txt文件编译输出到/system/usr目录下面。

在源码中,也有这样的例子。比如将zoneinfo.dat编译到/system/usr/share/zoneinfo目录。这个例子的Android.mk位于bionic/libc/zoneinfo/Android.mk,下面是其代码片段:

include $(CLEAR_VARS)
LOCAL_MODULE := zoneinfo.dat
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
LOCAL_SRC_FILES := $(LOCAL_MODULE)
LOCAL_MODULE_CLASS := ETC
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_PATH := $(TARGET_OUT)/usr/share/zoneinfo
include $(BUILD_PREBUILT)

如果没有指定LOCAL_MODULE_PATH,LOCAL_MODULE_CLASS 取值如下:

  • ETC:默认将module放置在/system/ect目录下面

  • EXECUTABLES:默认将module放置在/system/bin目录下面

  • JAVA_LIBRARIES:默认将module放置在/system/framework

  • SHARED_LIBRARIES:默认将module放置在/system/lib目录

  • APPS

2. 预编译so到/system/lib

在系统开发中,可能有很多个编译好的so需要打包到system.img中,mk文件可以如此编写:

LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_PREBUILT_LIBS := libtest.so

LOCAL_MODULE_TAG := optional
include $(BUILD_MULTI_PREBUILT)

对于BUILD_MULTI_PREBUILT编译宏,PREBUILD变量还有如下:

  • LOCAL_PREBUILT_OBJ_FILES

  • LOCAL_PREBUILT_LIBS

  • LOCAL_PREBUILT_EXECUTABLES

  • LOCAL_PREBUILT_JAVA_LIBRARIES

  • LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES

  • LOCAL_PREBUILT_STRIP_COMMENTS

对于多个so使用上面的方法比较方便,对于单个的so也可以使用下面的方法:

LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := libtest
LOCAL_SRC_FILES := libtest.so
LOCAL_MODULE_CLASS := SHARED_LIBRARIES
LOCAL_MODULE_SUFFIX := .so
LOCAL_MODULE_TAGS := optional
include $(BUILD_PREBUILT)

3. 预编译APP到/system/app

LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := test
LOCAL_SRC_FILES := test.apk
LOCAL_MODULE_CLASS := APPS
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX)
LOCAL_CERTIFICATE := platform
include $(BUILD_PREBUILT)

4. StaleDexCacheError错误的发生与解决

前段时间为了对应一个机能,需要为android system添加一个新的jar。将jar放置到system/framework目录下面之后,发现使用此jar的class 在运行的时候会报出class not found的错误。于是将jar添加到init.rc文件中的BOOTCLASSPATH环境变量中,虚拟机会在boot classloader中 加载此环境变量中的jar包。虽然此解决了class not found的问题,但是引入了新的问题。在运行的时候会导致StaleDexCacheError的异常。 查阅dalvik的源码之后发现,odex文件中存放有此odex文件所依赖的其他jar文件名,这个文件名列表必须要和BOOTCLASSPATH中的列表保持一致。 代码如下:

// dalvik/vm/analysis
bool dvmCheckOptHeaderAndDependencies(int fd, bool sourceAvail, u4 modWhen,
    u4 crc, bool expectVerify, bool expectOpt)
{
……………………

    /*
     * Verify dependencies on other cached DEX files.  It must match
     * exactly with what is currently defined in the bootclasspath.
     */
    ClassPathEntry* cpe;
    u4 numDeps;

    numDeps = read4LE(&ptr);
    ALOGV("+++ DexOpt: numDeps = %d", numDeps);
    for (cpe = gDvm.bootClassPath; cpe->ptr != NULL; cpe++) {
        const char* cacheFileName =
            dvmPathToAbsolutePortion(getCacheFileName(cpe));
        assert(cacheFileName != NULL); /* guaranteed by Class.c */

        const u1* signature = getSignature(cpe);
        size_t len = strlen(cacheFileName) +1;
        u4 storedStrLen;

        if (numDeps == 0) {
            /* more entries in bootclasspath than in deps list */
            ALOGI("DexOpt: not all deps represented");
            goto bail;
        }

        storedStrLen = read4LE(&ptr);
        if (len != storedStrLen ||
            strcmp(cacheFileName, (const char*) ptr) != 0)
        {
            ALOGI("DexOpt: mismatch dep name: '%s' vs. '%s'",
                cacheFileName, ptr);
            goto bail;
        }

        ptr += storedStrLen;

        if (memcmp(signature, ptr, kSHA1DigestLen) != 0) {
            ALOGI("DexOpt: mismatch dep signature for '%s'", cacheFileName);
            goto bail;
        }
        ptr += kSHA1DigestLen;

        ALOGV("DexOpt: dep match on '%s'", cacheFileName);

        numDeps--;
    }

    if (numDeps != 0) {
        while(numDeps) {
            u4 storedStrLen = read4LE(&ptr);
            LOGD("DexOpt: %s deps went away",ptr);
            ptr += storedStrLen;
            numDeps--;
        }

        /* more entries in deps list than in classpath */
        ALOGI("DexOpt: Some deps went away");
        goto bail;
    }

}

从以上代码中可知,odex文件中的依赖,必须要和bootclasspath中的文件保持一致。 在PMS的启动阶段,会对系统jar进行check,这个check过程会到上述方法中。一旦发现匹配不准确的情况,就会抛出StaleDexCacheError。

修改方案: 如果必须要将第三方的jar加入到bootclasspath中,那么对应的也要添加到build的DEXPREOPT_BOOT_JARS 变量中。

5. 系统资源定制

如果想裁剪Android system所支持的语言等资源,可以通过更改PRODUCT_LOCALES的值来决定哪些资源被打包进资源表。PRODUCT_LOCALES的值最后 都传递给了aapt,aapt的参数-c就可以用来指定编译时候的配置。aapt d --values resources app.apk命令可以打印出apk中资源的详情。

results matching ""

    No results matching ""