有关android加快开机速度

有关android加快开机速度

      我们知道,android开机速度慢,在恢复出厂设置后开机速度更慢,其中优化dex文件的过程就耗时很多。那么我们就可以尝试将优化dexopt步骤提前到编译阶段生成img文件的环节去。当然了这是一个以rom空间换取时间的策略。

可以通过在BoardConfig.mk中加入
# Enable the optimized DEX
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.make once host-based Dalvik preoptimization is
  # 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))
ifndef LOCAL_DEX_PREOPT
LOCAL_DEX_PREOPT := true
endif
endif
endif
endif

=====================================================

build/core/prebuild.mk

ifneq ($(filter APPS,$(LOCAL_MODULE_CLASS)),)
ifeq (true,$(WITH_DEXPREOPT))
ifeq (,$(TARGET_BUILD_APPS))
ifndef LOCAL_DEX_PREOPT
LOCAL_DEX_PREOPT := true
endif
endif
endif
endif

=====================================================

build/core/java_library.mk

ifeq (true,$(WITH_DEXPREOPT))
ifeq (,$(TARGET_BUILD_APPS))
ifndef LOCAL_DEX_PREOPT
LOCAL_DEX_PREOPT := true
endif
endif
endif

====================================================

build/core/product_config.mk

  # Hack to make the linux build servers use dexpreopt (emulator-based
  # preoptimization). Most engineers don't use this type of target
  # ("make PRODUCT-blah-user"), so this should only tend to happen when
  # using buildbot.
  # TODO: Remove this once host Dalvik preoptimization is working.
  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优化的:

有关android加快开机速度_第1张图片

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@[email protected]@classes.dex' vs. '/system/framework/conscrypt.odex'
10 dalvikvm /system/framework/bouncycastle.jar odex has stale dependencies
11 dalvikvm DexOpt: --- BEGIN 'bouncycastle.jar' (bootstrap=1) ---
12 dalvikvm DexOpt: Some deps went away
13 dalvikvm /system/framework/core-junit.jar odex has stale dependencies
14 dalvikvm DexOpt: load 33ms, verify+opt 350ms, 681812 bytes
15 dalvikvm DexOpt: --- END 'bouncycastle.jar' (success) ---
16 dalvikvm DEX prep '/system/framework/bouncycastle.jar': unzip in 57ms, rewrite 548ms
17 dalvikvm DexOpt: mismatch dep name: '/data/dalvik-cache/system@[email protected]@classes.dex' vs. '/system/framework/conscrypt.odex'
18 dalvikvm /system/framework/ext.jar odex has stale dependencies
19 dalvikvm DexOpt: --- BEGIN 'ext.jar' (bootstrap=1) ---
20 dalvikvm DexOpt: Some deps went away
21 dalvikvm /system/framework/core-junit.jar odex has stale dependencies
22 dalvikvm DexOpt: mismatch dep name: '/data/dalvik-cache/system@[email protected]@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@[email protected]@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优化就能正确进行下去。


你可能感兴趣的:(有关android加快开机速度)