共享库服务我们取名为myserver
系统预编译(预优化):目的是加快系统的启动时间,如下设置:
device\atc\evb3561sv_w_no2\BoardConfig.mk
### add by zhaojr for odex
# Enable dex-preoptimization to speed up first boot sequence
ifeq ($(HOST_OS),linux)
ifeq (user,userdebug $(TARGET_BUILD_VARIANT))
ifeq ($(WITH_DEXPREOPT),)
WITH_DEXPREOPT := true
endif
endif
endif
编译之后,下载系统运行提示:
E SystemServer: BOOT FAILURE starting MySecure Service
E SystemServer: java.lang.NoClassDefFoundError: Failed resolution of: Laa/bb/cc/MySecure;
E SystemServer: at com.android.server.SystemServer.startOtherServices(SystemServer.java:1034)
E SystemServer: at com.android.server.SystemServer.run(SystemServer.java:286)
E SystemServer: at com.android.server.SystemServer.main(SystemServer.java:178)
E SystemServer: at java.lang.reflect.Method.invoke(Native Method)
E SystemServer: at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:772)
E SystemServer: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:662)
E SystemServer: Caused by: java.lang.ClassNotFoundException: Didn't find class "aa.bb.cc.MySecure" on path: DexPathList[[zip file "/system/framework/services.jar", zip file "/system/framework/ethernet-service.jar", zip file "/system/framework/wifi-service.jar", zip file "/system/framework/pppoe-service.jar"],nativeLibraryDirectories=[/vendor/lib, /system/lib]]
E SystemServer: at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56)
E SystemServer: at java.lang.ClassLoader.loadClass(ClassLoader.java:511)
E SystemServer: at java.lang.ClassLoader.loadClass(ClassLoader.java:469)
E SystemServer: ... 6 more
E SystemServer: Suppressed: java.lang.ClassNotFoundException: Didn't find class "aa.bb.cc.MySecure" on path: DexPathList[[directory "."],nativeLibraryDirectories=[/vendor/lib, /system/lib]]
E SystemServer: at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56)
E SystemServer: at java.lang.ClassLoader.loadClass(ClassLoader.java:511)
E SystemServer: at java.lang.ClassLoader.loadClass(ClassLoader.java:504)
E SystemServer: ... 7 more
E SystemServer: Suppressed: java.lang.ClassNotFoundException: aa.bb.cc.MySecure
E SystemServer: at java.lang.Class.classForName(Native Method)
E SystemServer: at java.lang.BootClassLoader.findClass(ClassLoader.java:781)
E SystemServer: at java.lang.BootClassLoader.loadClass(ClassLoader.java:841)
E SystemServer: at java.lang.ClassLoader.loadClass(ClassLoader.java:504)
E SystemServer: ... 8 more
E SystemServer: Caused by: java.lang.NoClassDefFoundError: Class not found using the boot class loader; no stack trace available
如果没有以上预编译设置,编译系统运行是OK。
分析:
开启Pre-optimization前后编译差别:
开启Pre-optimization后,每个模块会额外生成一个.odex文件,位于/system/framework/oat/arm/目录;
模块的一些信息被记录到boot.art和boot.oat文件中,位于/system/framework/arm/目录。
boot.art和boot.oat文件见后面的参考文章
参考文章提到:boot.art里面使用的都是绝对地址。
绝对地址!
来,类比几个概念:
相对路径<--->绝对路径
相对地址<--->绝对地址
动态链接<--->静态链接
LOCAL_JAVA_LIBRARIES<--->LOCAL_STATIC_JAVA_LIBRARIES
基于这个原理,我们可以在Android.mk中设置:
LOCAL_JAVA_LIBRARIES := myserver 改为>>LOCAL_STATIC_JAVA_LIBRARIES += myserver
就可以解决上述问题。
但是第三方APP调用到myserver 库仍然存在问题,不可能让第三方APP使用LOCAL_STATIC_JAVA_LIBRARIES的方式。
基于以上分析我们参考系统中编译的电话模块就不会出现问题,如下:
那些引用telephony-common.jar的使用的LOCAL_JAVA_LIBRARIES而并没有出现问题,所以我们可以推测还有其他方式解决类似问题。在build目录中搜索telephony-common,可以发现在build/target/product/core_minimal.mk文件PRODUCT_BOOT_JARS的变量中有telephony-common添加,如下:
build/target/product/core_minimal.mk
...........................................
# The order of PRODUCT_BOOT_JARS matters.
PRODUCT_BOOT_JARS := \
$(TARGET_CORE_JARS) \
legacy-test \
ext \
framework \
telephony-common \
voip-common \
ims-common \
org.apache.http.legacy.boot \
android.hidl.base-V1.0-java \
android.hidl.manager-V1.0-java
我们参考上面的分析,将我们自己的模块添加进去,如下:
build/target/product/core_minimal.mk
...........................................
# The order of PRODUCT_BOOT_JARS matters.
PRODUCT_BOOT_JARS := \
$(TARGET_CORE_JARS) \
legacy-test \
ext \
framework \
telephony-common \
myserver \
voip-common \
ims-common \
org.apache.http.legacy.boot \
android.hidl.base-V1.0-java \
android.hidl.manager-V1.0-java
这就相当于把myserver.jar放到了一个公共的、众所周知的地方,自然不会出现找不到class的问题.
但是这样添加之后,编译会报以下错误,如下:
unknown package name of class file
用grep查找错误的来源,发现出自一个Python脚本:
build/core/tasks/check_boot_jars/check_boot_jars.py
build\core\tasks\check_boot_jars\check_boot_jars.py
#!/usr/bin/env python
"""
Check boot jars.
Usage: check_boot_jars.py <package_whitelist_file> <jar1> <jar2> ...
"""
import logging
import os.path
import re
import subprocess
import sys
# The compiled whitelist RE.
whitelist_re = None
def LoadWhitelist(filename):
""" Load and compile whitelist regular expressions from filename.
"""
lines = []
with open(filename, 'r') as f:
for line in f:
line = line.strip()
if not line or line.startswith('#'):
continue
lines.append(line)
combined_re = r'^(%s)$' % '|'.join(lines)
global whitelist_re
try:
whitelist_re = re.compile(combined_re)
except re.error:
logging.exception(
'Cannot compile package whitelist regular expression: %r',
combined_re)
whitelist_re = None
return False
return True
def CheckJar(jar):
"""Check a jar file.
"""
# Get the list of files inside the jar file.
p = subprocess.Popen(args='jar tf %s' % jar,
stdout=subprocess.PIPE, shell=True)
stdout, _ = p.communicate()
if p.returncode != 0:
return False
items = stdout.split()
for f in items:
if f.endswith('.class'):
package_name = os.path.dirname(f)
package_name = package_name.replace('/', '.')
# Skip class without a package name
if package_name and not whitelist_re.match(package_name):
print >> sys.stderr, ('Error: %s contains class file %s, which is not in the whitelist'
% (jar, f))
return False
return True
def main(argv):
if len(argv) < 2:
print __doc__
return 1
if not LoadWhitelist(argv[0]):
return 1
passed = True
for jar in argv[1:]:
if not CheckJar(jar):
passed = False
if not passed:
return 1
return 0
if __name__ == '__main__':
sys.exit(main(sys.argv[1:]))
很明显,如果自己的jar的包名(package name)不在whitelist_re里面的话,编译报错,通过添加log发现whitelist_re来自一个txt文件:
build/core/tasks/check_boot_jars/package_whitelist.txt
build\core\tasks\check_boot_jars\package_whitelist.txt
###################################################
# core-libart.jar
java\.awt\.font
java\.beans
java\.io
java\.lang
java\.lang\.annotation
java\.lang\.ref
java\.lang\.reflect
java\.math
java\.net
java\.nio
............................
...........................
dalvik\..*
libcore\..*
android\..*
com\.android\..*
###################################################
# legacy-test.jar
junit\.extensions
junit\.framework android\.test
android\.test\.suitebuilder\.annotation
.............................
查看该文件发现PRODUCT_BOOT_JARS的其他jar的包名都有在这里定义,仿照文件格式把自己的包名添加到这里就OK
同时还有一种修改方法:
如果在全局设置中打开预编译选项
device\atc\evb3561sv_w_no2\BoardConfig.mk
### add by zhaojr for odex
# Enable dex-preoptimization to speed up first boot sequence
ifeq ($(HOST_OS),linux)
ifeq (user,userdebug $(TARGET_BUILD_VARIANT))
ifeq ($(WITH_DEXPREOPT),)
WITH_DEXPREOPT := true
endif
endif
endif
系统编译运行,有时会找不到相应的类库,这个时候可以将相应类库所在的模块不进行预编译处理,也可以解决这个问题, 在相应模块的Android.mk文件中加入如下设置:
LOCAL_DEX_PREOPT := false
如之前在打开预编译选项之后,Luncher启动的时候就是出现问题导致总是启不到主见面,修改如下:
packages\apps\Launcher2\Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_STATIC_JAVA_LIBRARIES := android-common android-support-v13
LOCAL_STATIC_JAVA_LIBRARIES += com.mediatek.launcher2.ext
LOCAL_JAVA_LIBRARIES += mediatek-framework
LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-renderscript-files-under, src)
#LOCAL_SDK_VERSION := current
LOCAL_DEX_PREOPT := false
LOCAL_PACKAGE_NAME := Launcher2
LOCAL_CERTIFICATE := shared
LOCAL_PRIVILEGED_MODULE := true
LOCAL_OVERRIDES_PACKAGES := Home Launcher3
LOCAL_PROGUARD_FLAG_FILES := proguard.flags
LOCAL_MULTILIB := 32
include $(BUILD_PACKAGE)
include $(call all-makefiles-under,$(LOCAL_PATH))