有关android加快开机速度
有关android加快开机速度
我们知道,android开机速度慢,在恢复出厂设置后开机速度更慢,其中优化dex文件的过程就耗时很多。那么我们就可以尝试将优化dexopt步骤提前到编译阶段生成img文件的环节去。当然了这是一个以rom空间换取时间的策略。
可以通过在BoardConfig.mk中加入
#EnabletheoptimizedDEX
WITH_DEXPREOPT=true
或者直接修改system.prop
#ODEX
dalvik.vm.verify-bytecode=true
然后我们探索看看android是怎么样进行这个特性的?
$grep-r"WITH_DEXPREOPT"build/
build/core/prebuilt.mk:ifeq(true,$(WITH_DEXPREOPT))
build/core/java_library.mk:ifeq(true,$(WITH_DEXPREOPT))
build/core/main.mk:ifeq($(HOST_OS)-$(WITH_DEXPREOPT_buildbot),linux-true)
build/core/main.mk:WITH_DEXPREOPT:=true
build/core/package.mk:ifeq(true,$(WITH_DEXPREOPT))
build/core/product_config.mk:WITH_DEXPREOPT_buildbot:=true
在build/core下面有个文件dex_preopt.mk,这个文件值得注意。
android真是太庞大了,其脚本之间的关系对于我这个初涉的人还是好生畏惧,值得研究。详细如下。
======================================================
build/core/main.mk
#config/product_config.makeoncehost-basedDalvikpreoptimizationis
#working.
ifneq(true,$(DISABLE_DEXPREOPT))
ifeq($(HOST_OS)-$(WITH_DEXPREOPT_buildbot),linux-true)
WITH_DEXPREOPT:=true
endif
endif
=====================================================
build/core/package.mk
ifeq(true,$(WITH_DEXPREOPT))
ifeq(,$(TARGET_BUILD_APPS))
ifneq(,$(LOCAL_SRC_FILES))
ifndefLOCAL_DEX_PREOPT
LOCAL_DEX_PREOPT:=true
endif
endif
endif
endif
=====================================================
build/core/prebuild.mk
ifneq($(filterAPPS,$(LOCAL_MODULE_CLASS)),)
ifeq(true,$(WITH_DEXPREOPT))
ifeq(,$(TARGET_BUILD_APPS))
ifndefLOCAL_DEX_PREOPT
LOCAL_DEX_PREOPT:=true
endif
endif
endif
endif
=====================================================
build/core/java_library.mk
ifeq(true,$(WITH_DEXPREOPT))
ifeq(,$(TARGET_BUILD_APPS))
ifndefLOCAL_DEX_PREOPT
LOCAL_DEX_PREOPT:=true
endif
endif
endif
====================================================
build/core/product_config.mk
#Hacktomakethelinuxbuildserversusedexpreopt(emulator-based
#preoptimization).Mostengineersdon'tusethistypeoftarget
#("makePRODUCT-blah-user"),sothisshouldonlytendtohappenwhen
#usingbuildbot.
#TODO:RemovethisoncehostDalvikpreoptimizationisworking.
ifeq($(TARGET_BUILD_VARIANT),user)
WITH_DEXPREOPT_buildbot:=true
endif
另外补充:
1、system/app下放置系统apk;data/app下放置内置第三方apk;
2、在生成的system/app和system/framework下会生成优化后的odex文件;
3、odex占用物理空间比dex和apk文件要大,所以上述方法是计算机操作系统中典型的空间和时间互换的思想;
下面的转自:http://www.cnblogs.com/jacobchen/p/3599483.html
[Android]Dalvik的BOOTCLASSPATH和dexopt流程
BOOTCLASSPATH简介
1.BOOTCLASSPATH是Android Linux的一个环境变量,可以在adb shell下用$BOOTCLASSPATH看到。
2.BOOTCLASSPATH于/init.rc文件中export,如果没有找到的话,可以在init.rc中import的文件里找到(如import /init.environ.rc)。
3.init.rc文件存在于boot.img的ramdisk映像中。如果仅仅是修改/init.rc文件,重启后会被ramdisk恢复,所以直接修改是没有效果的。
4.boot.img是一种特殊的Android定制格式,由boot header,kernel,ramdisk以及second stage loader(可选)组成,详见android/system/core/mkbootimg/bootimg.h。
boot.img空间结构:
** +-----------------+** | boot header | 1 page** +-----------------+** | kernel | n pages** +-----------------+** | ramdisk | m pages** +-----------------+** | second stage | o pages** +-----------------+
典型的ramdisk文件结构:
./init.trout.rc
./default.prop
./proc
./dev
./init.rc
./init
./sys
./init.goldfish.rc
./sbin
./sbin/adbd
./system
./data
BOOTCLASSPATH的作用
以Android4.4手机的BOOTCLASSPATH为例:
export BOOTCLASSPATH /system/framework/core.jar:/system/framework/conscrypt.jar:/system/framework/okhttp.jar...
当kernel启动时1号进程init解析init.rc,将/system/framework下的jar包路径export出来。
Dalvik虚拟机在初始化过程中,会读取环境变量BOOTCLASSPATH,用于之后的类加载和优化。
Dalvik虚拟机的启动和dexopt流程
从Dalvik虚拟机的启动过程分析一文可以知道,Zygote会在启动后创建Dalvik虚拟机实例,并进行初始化。
那我们就接着Dalvik虚拟机初始化后开始探究它是如何通过BOOTCLASSPATH来进行dex优化的:
1.1. VM initialization
android/dalvik/vm/Init.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 | std::string dvmStartup( int argc, const char * const argv[], bool ignoreUnrecognized, JNIEnv* pEnv) { ... ALOGV( "VM init args (%d):" , argc); ... setCommandLineDefaults(); // ---> 读取BOOTCLASSPATH ... if (!dvmClassStartup()) { // ---> 初始化bootstrap class loader return "dvmClassStartup failed" ; } } |
1.2. 读取BOOTCLASSPATH
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | static void setCommandLineDefaults() { const char * envStr = getenv ( "CLASSPATH" ); if (envStr != NULL) { gDvm.classPathStr = strdup(envStr); } else { gDvm.classPathStr = strdup( "." ); } envStr = getenv ( "BOOTCLASSPATH" ); // 读取到BOOTCLASSPATH环境变量<br> if (envStr != NULL) { gDvm.bootClassPathStr = strdup(envStr); } else { gDvm.bootClassPathStr = strdup( "." ); } ... } |
就这样,BOOTCLASSPATH的值被保存到gDvm.bootClassPathStr中。
2.1. 初始化bootstrap class loader
android/dalvik/vm/oo/Class.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | bool dvmClassStartup() { ... /* * Process the bootstrap class path. This means opening the specified * DEX or Jar files and possibly running them through the optimizer. */ assert (gDvm.bootClassPath == NULL); processClassPath(gDvm.bootClassPathStr, true ); // 下一步 if (gDvm.bootClassPath == NULL) return false ; } |
2.2. 将路径、Zip文件和Dex文件的list转换到ClassPathEntry结构体当中
1 2 3 4 5 6 | static ClassPathEntry* processClassPath( const char * pathStr, bool isBootstrap) { ClassPathEntry* cpe = NULL; ... if (!prepareCpe(&tmp, isBootstrap)) {} } |
2.3. 根据cpe打开文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | static bool prepareCpe(ClassPathEntry* cpe, bool isBootstrap) { ... if (( strcmp (suffix, "jar" ) == 0) || ( strcmp (suffix, "zip" ) == 0) || ( strcmp (suffix, "apk" ) == 0)) { JarFile* pJarFile = NULL; /* 打开jar包,找到class.dex或jar包旁边的.odex文件 */ if (dvmJarFileOpen(cpe->fileName, NULL, &pJarFile, isBootstrap) == 0) { cpe->kind = kCpeJar; cpe->ptr = pJarFile; return true ; } } else if ( strcmp (suffix, "dex" ) == 0) { RawDexFile* pRawDexFile = NULL; /* 与dvmJarFileOpen函数作用类似,是由它复制过来重构的 */ if (dvmRawDexFileOpen(cpe->fileName, NULL, &pRawDexFile, isBootstrap) == 0) { cpe->kind = kCpeDex; cpe->ptr = pRawDexFile; return true ; } } else { ALOGE( "Unknown type suffix '%s'" , suffix); } ... } |
3. 打开jar包,找到class.dex或jar包旁边的.odex文件
android/dalvik/vm/JarFile.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | int dvmJarFileOpen( const char * fileName, const char * odexOutputName, JarFile** ppJarFile, bool isBootstrap) { ... /* Even if we're not going to look at the archive, we need to * open it so we can stuff it into ppJarFile. */ if (dexZipOpenArchive(fileName, &archive) != 0) goto bail; archiveOpen = true ; /* If we fork/exec into dexopt, don't let it inherit the archive's fd. */ dvmSetCloseOnExec(dexZipGetArchiveFd(&archive)); /* First, look for a ".odex" alongside the jar file. It will * have the same name/path except for the extension. */ fd = openAlternateSuffix(fileName, "odex" , O_RDONLY, &cachedName); if (fd >= 0) { ALOGV( "Using alternate file (odex) for %s ..." , fileName); /* 读、验证header和dependencies */ if (!dvmCheckOptHeaderAndDependencies(fd, false , 0, 0, true , true )) { ALOGE( "%s odex has stale dependencies" , fileName); free (cachedName); cachedName = NULL; close(fd); fd = -1; goto tryArchive; } else { ALOGV( "%s odex has good dependencies" , fileName); //TODO: make sure that the .odex actually corresponds // to the classes.dex inside the archive (if present). // For typical use there will be no classes.dex. } } else { ZipEntry entry; tryArchive: /* * Pre-created .odex absent or stale. Look inside the jar for a * "classes.dex". */ ... } |
4.读、验证opt的header,读、验证dependencies
android/dalvik/vm/analysis/DexPrepare.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | 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) { /* more entries in deps list than in classpath */ ALOGI( "DexOpt: Some deps went away" ); goto bail; } ... } |
实际应用
打通了Dalvik dexopt的这个流程,那这到底又有什么用呢?
让我们看看实际开发过程中的手机升级binary后无法boot到Home界面的log:
1 AndroidRuntime >>>>>> AndroidRuntime START com.android.internal.os.ZygoteInit <<<<<< 2 AndroidRuntime CheckJNI is ON 3 dalvikvm DexOpt: Some deps went away 4 dalvikvm /system/framework/core-junit.jar odex has stale dependencies 5 dalvikvm DexOpt: --- BEGIN 'core-junit.jar' (bootstrap=1) --- 6 dalvikvm DexOpt: load 42ms, verify+opt 25ms, 143956 bytes 7 dalvikvm DexOpt: --- END 'core-junit.jar' (success) --- 8 dalvikvm DEX prep '/system/framework/core-junit.jar': unzip in 1ms, rewrite 126ms 9 dalvikvm DexOpt: mismatch dep name: '/data/dalvik-cache/system@framework@core-junit.jar@classes.dex' vs. '/system/framework/conscrypt.odex'10 dalvikvm /system/framework/bouncycastle.jar odex has stale dependencies11 dalvikvm DexOpt: --- BEGIN 'bouncycastle.jar' (bootstrap=1) ---12 dalvikvm DexOpt: Some deps went away13 dalvikvm /system/framework/core-junit.jar odex has stale dependencies14 dalvikvm DexOpt: load 33ms, verify+opt 350ms, 681812 bytes15 dalvikvm DexOpt: --- END 'bouncycastle.jar' (success) ---16 dalvikvm DEX prep '/system/framework/bouncycastle.jar': unzip in 57ms, rewrite 548ms17 dalvikvm DexOpt: mismatch dep name: '/data/dalvik-cache/system@framework@core-junit.jar@classes.dex' vs. '/system/framework/conscrypt.odex'18 dalvikvm /system/framework/ext.jar odex has stale dependencies19 dalvikvm DexOpt: --- BEGIN 'ext.jar' (bootstrap=1) ---20 dalvikvm DexOpt: Some deps went away21 dalvikvm /system/framework/core-junit.jar odex has stale dependencies22 dalvikvm DexOpt: mismatch dep name: '/data/dalvik-cache/system@framework@core-junit.jar@classes.dex' vs. '/system/framework/conscrypt.odex'23 dalvikvm /system/framework/bouncycastle.jar odex has stale dependencies
根据前面的流程,结合log我们就可以分析出,DexOpt: mismatch dep name: '/data/dalvik-cache/system@framework@core-junit.jar@classes.dex' vs. '/system/framework/conscrypt.odex'是错误所在,是由于data/dalvik-cache/下的dex cache文件和system/framework/下的jar文件验证依赖关系时候对应不上。
从函数dvmCheckOptHeaderAndDependencies()可以得知,BOOTCLASSPATH和cache必须是完全一致的
尝试删除所有cache文件,重启还是不行。那么应该想到BOOTCLASSPATH和实际的system/framework/的jar包不一致,才会导致和其生成的cache不一致。
对比一下果然不一致,issue trouble-shooted.
解决方法:把对应boot.img也烧进去,这样BOOTCLASSPATH就能更新一致,dex优化就能正确进行下去。
更多相关文章
- NDK简单实例
- android 定制个性按钮 控件
- Head First Android(安卓)Development-笔记
- eclipse下android工程目录讲解
- Android(安卓)NDK系列三(Android(安卓)Studio cmke 编译多个个.so
- 用eclipse编写Android程序时怎样生成apk文件
- Android(安卓)event日志打印原理
- android httpClient 支持HTTPS的处理方式
- Android(安卓)实用技巧 --- 命令godir