Android首次启动时间长优化之预编译提取Odex

提示!应用程序的安装有两种情况,第一:首次启动系统时安装;第二:系统启动完成后安装。本篇博文基于第一种安装场景。在系统首次启动的场景中,系统会对/system/app、/system/priv-app、/data/app目录下的所有APK进行dex字节码到本地机器码的翻译,同样也会对/system/framework目录下的APK或者JAR文件,以及这些APK所引用的外部JAR,进行dex字节码到本地机器码的翻译。这样可以保证除了应用之外,系统中使用Java来开发的系统服务,也会统一地从dex字节码翻译成本地机器码。详细内容请移步老罗的博客Android
ART运行时无缝替换Dalvik虚拟机的过程分析

一、JVM、DVM、ART虚拟机了解

  • JVM虚拟机运行的是java字节码:

java->java bytecode(class)->java bytecode(jar)

注!java虚拟机基于栈,基于栈的机器必须使用指令来载入和操作栈上的数据,所需指令相对来说比较多。

  • Dalvik虚拟机解释执行的dex字节码:

java->java bytecode(class)->dalvik bytecode(dex)

注:相对JVM,Dalvik基于寄存器,且经过优化并允许有限的内存中同时运行多个虚拟机实例,每个Dalvik应用作为一个独立的Linxu进程执行。如果一个应用中有很多类,编译后会相应生成很多class文件,class文件之间也会有不少冗余信息,dex格式文件把所有classs文件内容整合到一个文件,这样可以减少整体文件占用,IO操作,同时也提高了类的查找速度。此外,dex格式文件增加了新的操作码支持,文件结构也相对简洁,使用等长的指令来提高解析速度。而且dex文件会尽量扩大只读结构的大小,来提高进程间数据共享的速度。

  • ART虚拟机执行的本地机器码:

java->java bytecode(class)->dalvik bytecode(dex)->optimized android runtime machine code(oat)

注:ART所使用的AOT(Ahead-Of-Time)编译,在应用首次安装时,字节码预编译成机器码存储在本地,也就是说在程序运行前编译。而Dalvik是典型的JIT(Just_In_Time),此模式下,应用每次运行的时候,字节码都需要即时编译器转换为机器码再执行,也就是在程序运行时编译。因此在App运行时,ART模式相对于Dalvik省去了解释字节码的过程,占用内存也相应减少,进而提高App的运行效率。

二、Odex

从上面一节中我们知道,在编译打包APK时,Java类会被编译成一个或者多个字节码文件(.class),通过dx工具CLASS文件转换成一个DEX(Dalvik Executable)文件。

通常情况下,我们看到的Android应用程序实际上是一个以.apk为后缀名的压缩文件。我们可以通过压缩工具对apk进行解压,解压出来的内容中有一个名为classes.dex的文件。那么我们首次开机的时候系统需要将其从apk中解压出来保存在data/app目录中。

如果当前运行在Dalvik虚拟机下,Dalvik会对classes.dex进行一次“翻译”,“翻译”的过程也就是守护进程installd的函数dexopt来对dex字节码进行优化,实际上也就是由dex文件生成odex文件,最终odex文件被保存在手机的VM缓存目录data/dalvik-cache下(注意!这里所生成的odex文件依旧是以dex为后缀名,格式如:[email protected]@[email protected]@classes.dex)。

如果当前运行于Art模式下,    Art同样会在首次进入系统的时候调用/system/bin/dexopt工具来将dex字节码翻译成本地机器码,保存在data/dalvik-cache下。

那么这里需要注意的是,无论是对dex字节码进行优化,还是将dex字节码翻译成本地机器码,最终得到的结果都是保存在相同名称的一个odex文件里面的,但是前者对应的是一个dey文件(表示这是一个优化过的dex),后者对应的是一个oat文件(实际上是一个自定义的elf文件,里面包含的都是本地机器指令)。通过这种方式,原来任何通过绝对路径引用了该odex文件的代码就都不需要修改了。

由于在系统首次启动时会对应用进行安装,那么在预置APK比较多的情况下,将会大大增加系统首次启动的时间。从前面的描述可知,既然无论是DVM还是ART,对DEX的优化结果都是保存在一个相同名称的odex文件,那么如果我们把这两个过程在ROM编译的时候预处理提取Odex文件将会大大优化系统首次启动的时间。

三、预编译提取Odex

在BoardConfig.mk中定义:WITH_DEXPREOPT := true。打开这个宏之后,无论是有源码还是无源码的预置apk预编译时都会提取odex文件。

不过这里需要注意的是打开WITH_DEXPREOPT 宏之后,预编译时提取Odex会增加一定的空间,预置太多apk,会导致system.img 过大,而编译不过。遇到这种情况可以通过删除apk中的dex文件、调大system.img的大小限制,或在预编译时跳过一些apk的odex提取。

例如跳过helloworld应用提取的方法如下:

在目录\build\core\dex_preopt_odex_install.mk中添加红色标记的代码:

ifeq ($(LOCAL_MODULE),helloworld)

LOCAL_DEX_PREOPT:=

endif

build_odex:=

installed_odex:=

....

helloworld可替换为需要跳过提取odex的apk的LOCAL_MODULE名字,如Settings等。

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-07-29 06:28:44

Android首次启动时间长优化之预编译提取Odex的相关文章

面试题--如何防止sql注入,使用PreparedStatement的预编译,传入的内容就不会和原来的语句发生任何匹配的关系,达到防止注入的方法

PreparedStatement的用法 jdbc(java database connectivity,java数据库连接)的api中的主要的四个类之一的java.sql.statement要求开发者付出大量的时间和精力.在使用statement获取jdbc访问时所具有的一个共通的问题是输入适当格式的日期和时间戳:2002-02-05 20:56 或者 02/05/02 8:56 pm. 通过使用java.sql.preparedstatement,这个问题可以自动解决.一个prepareds

Pch文件预编译

因为项目用到Pch文件链接宏变量,因而稍作研究怎样使用,define宏变量其实并不合适 ,static const才最适合 Pch文件听说是上古世纪存在的文件,主要是用来全局预编译文件统一在一个出口,方便编程管理,但同时往往会造成预编译时间过长,但预编译只要设置恰当,其实并没有增加时间,还方便了开发,用着好处多多,废话不多说,现在立刻学习怎么使用. 1.在Xcode创建Pch文件 Pch文件已经被放到Other中,可见Apple已经不怎么待见这个文件了,还是希望开发者多在文件头部引入减少编译时间

atitit.查看预编译sql问号 本质and原理and查看原生sql语句

atitit.查看预编译sql问号 本质and原理and查看原生sql语句 1. 预编译原理. 1 2. preparedStatement 有三大优点: 1 3. How to look  gene  sql 2 1. Hb cfg all debug ,cant see... 2 2. WSExplorer按照进程抓取pack可以看见.. 2 3. Mysql 5.6 开放日志可以看见 2 4. Mysql and msssql的不同实现 2 4. MYSql的实现是jdbc连接完全的sql

网站优化记录-通过命令预编译Asp.net 网站,成功优化到毫秒级别。

在去年一次项目上线时发现部署的站点首次访问跟回收后响应特别慢,每次都在3秒以内.(使用的是vs工具预编译的方式发布),在随后找到解决办法是通过命令预编译Asp.net 网站,成功解决站点响应在毫秒级别. 预编译 ASP.NET 网站 预编译 ASP.NET 网站可缩短用户的初始响应时间,因为页在第一次被请求时无需编译.这对于经常更新的大型网站尤其有用. https://msdn.microsoft.com/zh-cn/library/ms227972(VS.80).aspx 微软介绍 使用说明打

Android NDK引用预编译的动态链接库

NDK里有个例子: android-ndk-r10/samples/module-exports/jni一看就懂了 ———————————————————————————– 从r5版本开始,就支持预编译的库(共享和静态). 也就是说在你的应用中,可包含和使用 预先编译的库. 这个功能的用处1. 你想分发你自己的库给第3方 NDK开发者,但不想把源码给他们2. 你想使用自己的预编译的库 来加速项目的Build过程. 声明一个预编译的库模块每个预编译的库,都必须声明为一个独立的模块 给ndk bui

typeof、摇树优化、js预编译

typeof:  typeof用以获取一个变量或者表达式的类型,一元运算符 null:空.无.表示不存在,当为对象的属性赋值为null,表示删除该属性 undefined:未定义.当声明变量却没有赋值时会显示该值.可以为变量赋值为undefined number:数值.最原始的数据类型,表达式计算的载体 string:字符串.最抽象的数据类型,信息传播的载体 boolean:布尔值.最机械的数据类型,逻辑运算的载体 object:对象.面向对象的基础 注意:没有数组,因为type数组时结果是ob

android 内存和性能优化汇总

1.即时编译(Just-in-time Compilation,JIT),又称动态转译(Dynamic Translation),是一种通过在运行时将字节码翻译为机器码,从而改善字节码编译语言性能的技术.即时编译前期的两个运行时理论是字节码编译和动态编译.Android原来Dalvik虚拟机是作为一种解释器实现,新版(Android2.2+)将换成JIT编译器实现.性能测试显示,在多项测试中新版本比旧版本提升了大约6倍. 2. 就像世界上没有免费的午餐,世界上也没有免费的对象.虽然gc为每个线程

Android ListView复杂列表优化实践

原文:Android ListView复杂列表优化实践 很多社交App都不免会涉及到复杂的列表元素实现,一个列表上面可能大量的图片,不定长的评论列表,给手机端的程序员带来了不少的挑战.本文就是在实现复杂的列表滑动的情况下,利用已知的优化方法指导下的一次优化实践,旨在提升ListView的滑动流畅度,为用户带来良好的体验. 1:设计稿: 这是列表中可能出现的ItemView,有两种,但是又有许多相同的地方,比如一样有点赞的图片,评论等...其中,评论和点赞的数量是可变的. 2:使用一般布局带来的问

第8章 Android 4.0系统的下载与编译

第8章  Android 4.0系统的下载与编译 本章首先的准备Android下载与编译环境,内容主要分为:准备Android下载与编译环境.下载源码.编译源码及内核源码.下载Android 4.0及Goldfish源码中包括下载并初始化repo工具.下载Android源码.其他源码下载源.下载模拟器Goldfish内核源码.编译Android及Goldfish内核源码中切换到Android源码目录命令:$cd WORKING_DIRECTORY 执行如下命令,加载编译过程中用到的命令.环境变量