Android6.0系統的framework層中加入自己的共享庫服務,在系統預編譯之後,系統啟動提示找不到類的問題
共享庫服務我們取名為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