美文网首页Android系统方面那些事Android收藏集Android开发
应用启动流程梳理(一)-应用安装流程

应用启动流程梳理(一)-应用安装流程

作者: Stan_Z | 来源:发表于2019-11-07 00:08 被阅读0次

    想针对启动耗时&卡顿优化做一个专题,一来是复习二来是构建完整知识结构。分析应用的启动势必需要先了解整个启动的流程,那么在分析前,需要先简单总结下整个启动流程的梳理大纲,基于android 9.0版本:


    梳理大纲

    那么我梳理的场景整体流程是:安装一个应用,完成安装之后点击Launcher桌面某App图标,应用启动到主界面显示完成整个过程。过程不涉及代码,具体分析可以去看之前对应的文章,里面有详细的源码分析。

    一、应用安装流程

    应用安装过程简单说就两步:

    1.1 复制Apk到指定目录

    主Apk复制路径如下:
    system/app 系统应用存放目录
    data/app 三方应用存放目录

    ~:/system/app/Bluetooth # ls -al
    total 4048
    drwxr-xr-x   4 root root    4096 2009-01-01 08:00 .
    drwxr-xr-x 103 root root    4096 2009-01-01 08:00 ..
    -rw-r--r--   1 root root 4124698 2009-01-01 08:00 Bluetooth.apk
    drwxr-xr-x   3 root root    4096 2009-01-01 08:00 lib
    drwxr-xr-x   3 root root    4096 2009-01-01 08:00 oat
    
    ~:/data/app/com.ss.android.article.news-kk9ZSokxPMvccIA7c41aJA== # ls -al
    total 26108
    drwxrwxr-x  4 system system      3488 2019-10-31 23:57 .
    drwxrwx--x 41 system system      3488 2019-10-31 23:57 ..
    -rw-r--r--  1 system system  26690526 2019-10-31 23:57 base.apk
    drwxr-xr-x  3 system system      3488 2019-10-31 23:57 lib
    drwxrwx--x  3 system install     3488 2019-10-31 23:57 oat
    

    内容与权限一目了然,这里就不赘述了。另外再关注一个目录,data/data,这是各三方应用的私有目录,自然是用户权限的,那么是不是root权限的进程就可以随意访问了呢?其实也不然,在锁屏解锁前,data/data目录内的文件都是按FBE加密的,但是在解锁之后会解密。这属于文件系统的东西了,没有深入研究。

    但是我总结的结论是:/data目录下,凡是_ce结尾的是一直加密的,_de结尾是不加密的。其他的没有调研,但是data/data是在锁屏解锁前加密,解锁后解密。感兴趣的可以自行研究Android的文件系统加密。
    [FBE加密官方介绍]https://source.android.google.cn/security/encryption/file-based

    ~:/data/data/com.ss.android.article.news # ls -al
    total 73
    drwx------  13 u0_a214 u0_a214        3488 2019-11-01 00:08 .
    drwxrwx--x 322 system  system        24576 2019-10-31 23:57 ..
    drwxrwx--x   2 u0_a214 u0_a214        3488 2019-11-01 00:08 app_textures
    drwx------   3 u0_a214 u0_a214        3488 2019-11-01 00:08 app_webview
    drwx------   2 u0_a214 u0_a214        3488 2019-11-01 00:08 app_webview_2930
    drwx------   2 u0_a214 u0_a214        3488 2019-11-01 00:08 app_webview_3017
    drwx------   4 u0_a214 u0_a214        3488 2019-11-01 00:08 app_webview_3298
    drwxrws--x  10 u0_a214 u0_a214_cache  3488 2019-11-01 00:08 cache
    drwxrws--x   2 u0_a214 u0_a214_cache  3488 2019-10-31 23:57 code_cache
    drwxrwx--x   2 u0_a214 u0_a214        3488 2019-11-01 00:08 databases
    drwxrwx--x  26 u0_a214 u0_a214        3488 2019-11-01 00:08 files
    lrwxrwxrwx   1 root    root             74 2019-10-31 23:57 lib -> /data/app/com.ss.android.article.news-kk9ZSokxPMvccIA7c41aJA==/lib/arm
    drwx------   2 u0_a214 u0_a214        3488 2019-11-01 00:08 lib-main
    drwxrwx--x   2 u0_a214 u0_a214       20480 2019-11-01 00:09 shared_prefs
    

    1.2 安装APK:

    安装过程简单说就是AndroidManifest.xml的解析,包括名字、版本、权限、四大组件等信息。然后分别做了内存存储和文件持久化。Launcher会获取<action android:name="android.intent.action.MAIN" />和<category android:name="android.intent.category.LAUNCHER" />的Activity,并绑定一个应用图标,点击Launcher对应的应用图标,实际上就是做了一次Activity的隐式启动。

    当然应用安装过程是很复杂的,包括权限的检查、文件夹的创建、安装逻辑判断、数据结构的封装等等内容。我这只是挑了几点与启动相关的总结一下。

    1.3 其他APK相关知识点

    1)APK组成

    • AndroidManifest.xml 应用整体配置信息。

    • META-INF 签名信息。

    • lib 应用依赖的native .so库,也包括一些插件。根据手机CPU的架构,lib库大体上可以分为4种:ARM、ARM-V7、MIPS和X86,分别对应着4种CPU架构。实际上,市面上的手机几乎全都是ARM架构的,所以大多数情况下我们只需要有armeabi和armeabi-v7a两种类型的库就足够了。

    • res 系统资源目录,与工程对应目录内容一致。

    • assets 跟res目录有点相似,但实际上二者还是有区别的。res目录中的文件会映射到R文件中,每个资源文件都有自己的ID,而assets中的文件则直接通过AssetManager类进行访问,而且assets目录你可以添加任意深度的子目录,这一点会比较方便管理和归类文件。相比较之下,res目录目前不能支持更深级的子目录。附带了解assets与raw的区别:assets不做任何处理被打包,资源通过AssetManager访问,目录可以分层。raw通过资源ID访问,不参与编译,目录不可分层。

    • classes.dex .java文件通过javac编译生成.class,多个.class通过dex生成.dex文件。.dex是面向Android虚拟机的标准字节码。

    • resources.arsc 编译后的二进制资源文件索引,记录了资源文件(即res目录中的文件)和资源文件ID的映射关系,这样程序运行的时候就可以根据资源的ID获取到相应的资源。

    2)编译

      App在android上运行,首先需要dex加载到内存,dex执行方式主要有两种:解释器执行 和 执行编译后的机器码 两种执行方式。解释执行就是及时编译,运行到哪解释到哪,这样效率不高,所以google在2.2版本引入了JIT,在解释执行过程中针对热点代码编译为机器码并且优化,然后缓存在内存中,下次执行到相同代码直接调用缓存的机器码,提升效率。但是app进程消亡下次重新冷启就没了。google4.4.推出art虚拟机替换Dalvik,推出AOT编译,即生成的机器码缓存为文件,作为持久化优化,每次编译会更新文件,但是更新频率并不高。优点是机器码持久化保证下次使用也能直接跑机器码,提升执行效率,缺点是编译耗时且CPU抢占严重。为了不影响用户体验,编译时机非常关键。
    

    Android推出4种编译filter:

    • verify:只运行 DEX 代码验证。
    • quicken:运行 DEX 代码验证,并优化一些 DEX 指令,以获得更好的解释器性能。
    • speed-profile:运行 DEX 代码验证,并对配置文件中列出的方法进行 AOT 编译。
    • speed:运行 DEX 代码验证,并对所有方法进行 AOT 编译。
      执行效率上:
      verify < quicken < speed-profile < speed
      编译速度上:
      verify > quicken > speed-profile > speed
      编译触发的几个时机:
    路径 描述 编译方式 编译内容
    Install 应用安装通过installd触发的编译 speed-profile 主apk
    OTA升级 系统升级通过installd触发的编译 verify 主apk
    load dexFile 动态加载插件直接通过虚拟机触发的编译 quicken 插件
    postboot 开机1分钟后,针对全部安装的7天内未使用且过期的应用通过installd触发的编译 verify 主apk
    idle 同时满足充电、idle状态且24小时内只触发一次,主apk通过installd触发编译,插件通过虚拟机触发编译 speed-profile 主apk 和 插件

    另外,编译的内存除了主apk之外还包含应用的插件,主apk间距通过PMS交由installd来触发dex2oat编译,插件则通过动态加载直接由虚拟机触发dex2oat编译。

    3)动态加载插件

    核心流程在于loadDexFile,这个过程核心逻辑是:先判断是否有.odex文件,然后判断是否过期,如果既有.odex又没过期,那么直接加载,如果不满足则先执行dex2oat编译,编译完之后将.odex加载到内存才开始执行。另外.dex编译过程是持锁的,个人感觉目的可能是防止多处同时触发loadDexFile,避免重复编译。到了Android Q之后,动态加载插件去掉了编译逻辑,也就是如果没有.odex或者有但是过期,直接跳出去加载.dex文件走解释模式,不再进行编译了。 好处是减少第一次加载等锁造成的卡顿,坏处就是解释模式执行效率不高。

    这里其实我多加了很多内容,不光是关于应用安装流程的,好了这部分先到这。

    上面牵涉到的部分知识点详细细节可以参看之前写过的文章:
    Android PMS(一)-启动流程
    Android PMS(二)-Apk安装流程
    Android PMS(三)-Installd执行dexopt流程
    Android PMS(四)-安装微信
    启动耗时分析(三)-ART编译分析
    Android 9.0 ART编译分析(一)-编译通路梳理
    Android 9.0 ART编译分析(二)-Installd触发dex2oat编译流程
    Android 9.0 ART编译分析(三)-虚拟机触发dex2oat编译流程

    相关文章

      网友评论

        本文标题:应用启动流程梳理(一)-应用安装流程

        本文链接:https://www.haomeiwen.com/subject/rrxmbctx.html