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

 同时还有一种修改方法:
  如果在全局设置中打开预编译选项

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))

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值