Android 性能优化 基本概念

前言

在学习Android内存性能优化时,发现需要对Android系统的内存概况得有个概况了解,便有了此篇文章。
这篇文章仅仅介绍一些Android的一些概念,从Android架构介绍到启动等,好在进行性能优化时心里有个谱。

1. Android系统介绍

Android 是一款基于 Linux 内核,面向移动终端的操作系统。 Android并不是传统的Linux风格的一个规范或分发版本,也不是一些列可重用的组件集成,Android是一个用于连接设备的软件块。

Android系统的四层架构图:
Android四层架构

系统启动时就是从底层往上一层一层启动的。

2. Android系统启动

startupStep

电源键 –> Boot ROM,加载引导程序 –> Linux Kernel 启动内核 –> init进程(第一个进程)。

第五步、第六步:
1. init进程开始运行:创建目录,挂载设备,初始化属性,加载配置文件(init.rc),开启守护进程,在init.rc配置文件中 就启动了Zygote进程,由init进程通过fork而来。
2. 调用app_main.cpp文件的AppRuntime.start()方法,在父类AndroidRuntime中:启动Dalvik虚拟机,注册JNI,启动ZygoteInit的main()方法
3. ZygoteInit中预加载类preloadClassed()、预加载资源preloadResources()等,然后启动 startSystemServer
4. SystemServer 首先通过init1()方法加载Libraries中的核心类库,然后又通过init2()方法启动 ServerThread线程,启动 Framework下的各种服务。
5. 然后ActivityManagerService启动Launcher应用,Launcher是一个app,会独占一个进程,Launcher在启动的时候便会通过PackageManagerService把系统中已经安装好的应用程序以快捷图标的形式展示在桌面上,这样用户就可以使用这些应用程序了。

init 进程

3. Dalvik简介和ART简介

  • Dalvik虚拟机与Java虚拟机的区别,其中Dalvik虚拟机不能执行.class文件:

    1. 它们分别具有不同的类文件格式以及指令集。Dalvik虚拟机使用的是dex(Dalvik Executable)格式的类文件,而Java虚拟机使用的是class格式的类文件。由于一个dex文件可以包含若干个类,因此它就可以将各个类中重复的字符串和其它常数只保存一次,从而节省了空间,这样就适合在内存和处理器速度有限的手机系统中使用。
    2. Dalvik虚拟机使用的指令是基于寄存器的,而Java虚拟机使用的指令集是基于堆栈的。
  • Dalvik虚拟机优化措施:

    1. 将多个类文件收集到同一个dex文件中,以便节省空间;
    2. 使用只读的内存映射方式加载dex文件,以便可以多进程共享dex文件,节省程序加载时间;
    3. 提前调整好字节序(byte order)和字对齐(word alignment)方式,使得它们更适合于本地机器,以便提高指令执行速度;
    4. 尽量提前进行字节码验证(bytecode verification),提高程序的加载速度;
    5. 需要重写字节码的优化要提前进行。
  • 使用odex文件加快响应速度
    Apk在安装(installer)时,就会进行验证和优化,目的是为了校验代码合法性及优化代码执行速度,验证和优化后,会产生ODEX文件,运行Apk的时候,直接加载ODEX,避免重复验证和优化,加快了Apk的响应时间。
    odex是OptimizedDEX的缩写,表示经过优化的dex文件。存放在/data/dalvik-cache目录下。由于Android程序的apk文件为zip压缩包格式,Dalvik虚拟机每次加载它们时需要从apk中读取classes.dex文件,这样会耗费很多cpu时间,而采用odex方式优化的dex文件,已经包含了加载dex必须的依赖库文件列表,Dalvik虚拟机只需检测并加载所需的依赖库即可执行相应的dex文件,这大大缩短了读取dex文件所需的时间。

    Android中dex文件的加载与优化流程
    ODEX格式及生成过程

  • Dalvik虚拟机内存:
    Dalvik虚拟机的内存大体上可以分为Java Object Heap、Bitmap Memory和Native Heap三种。其中Bitmap Memory在HoneyComb以及更高的版本中直接是在Java Object Heap中分配了,这样就可以直接接受GC的管理。
    这个Java Object Heap的最大值也就是我们平时所说的Android应用程序进程能够使用的最大内存。
    通过命令行:adb shell getprop | grep heap可查看系统所分配的最大内存。

  • Dalvik和ART(Android runtime)
    在Dalvik下,应用每次运行的时候,字节码都需要通过即时编译器(just in time ,JIT)转换为机器码,这会拖慢应用的运行效率,而在ART 环境中,应用在第一次安装的时候,字节码就会预先编译成机器码,使其成为真正的本地应用。这个过程叫做预编译(AOT,Ahead-Of-Time)。
    ART能够把应用程序的字节码转换为机器码,是Android所使用的一种新的虚拟机。它与Dalvik的主要不同在于:Dalvik采用的是JIT技术,而ART采用Ahead-of-time(AOT)技术。ART同时也改善了性能、垃圾回收(Garbage Collection)、应用程序除错以及性能分析。

    Android 中的Dalvik和ART是什么,有啥区别?
    官方文档:ART and Dalvik
    wiki-Android Runtime
    Android ART运行时无缝替换Dalvik虚拟机的过程分析

  • JIT和AOT
    JIT是在程序运行的过程中进行编译的,而AOT是在程序运行前进行编译的。

4. 应用程序启动分析

这三篇讲的很透彻,一直到Application和MainActivity的创建:

  1. Launcher 接收到点击事件,通过PackageManager获取到应用的信息,通过Binder向 SystemServer(ActivityManagerService 简称AMS 运行在里面) 发起启动应用的请求。
  2. SystemServer(AMS) 请求 Launcher Pause (Launcher 需要保存状态进入后台)
  3. Launcher Pause后, 向 SystemServer(AMS) 发送 Pause 完毕
  4. SystemServer(AMS) 向 Zygote 请求启动一个新进程
  5. Zygote fork出一个新进程,用来启动一个ActivityThread实例,即将要启动的Activity就是在这个ActivityThread实例中运行, 在新进程中执行 ActivityThread 类的 main 方法
  6. ActivityThread通过Binder将一个ApplicationThread类型的Binder对象传递给ActivityManagerService。
  7. ActivityManagerService调用ApplicationThread.scheduleLaunchActivity接口,通知相应的进程执行启动Activity的操作;
  8. ApplicationThread把这个启动Activity的操作转发给ActivityThread,ActivityThread通过ClassLoader导入相应的Activity类,然后把它启动起来。

其中ActivityManagerServices,简称AMS,服务端对象,负责系统中所有Activity的生命周期。ActivityThread是App的真正入口。当开启App之后,会调用main()开始运行,开启消息循环队列,这就是传说中的UI线程或者叫主线程。

/*
    ActivityThread.java中:
*/

public static void main(String[] args) {
    ...
    Environment.initForCurrentUser();
    ...
    Process.setArgV0("<pre-initialized>");
    //创建主线程looper
    Looper.prepareMainLooper();

    ActivityThread thread = new ActivityThread();
    //attach到系统进程
    thread.attach(false);

    if (sMainThreadHandler == null) {
        sMainThreadHandler = thread.getHandler();
    }

    //主线程进入循环状态
    Looper.loop();

    throw new RuntimeException("Main thread loop unexpectedly exited");
}

启动App时通过fork Zygote进程来节约内存:

  • 在Java中,我们知道不同的虚拟机实例会为不同的应用分配不同的内存。如果Android系统也这样,为每一个应用启动不同的Dalvik虚拟机实例,就会消耗大量的内存以及时间。因此,为了克服这个问题,Android系统创造了Zygote。Zygote让Dalvik虚拟机共享代码、低内存占用以及最小的启动时间成为可能。Zygote是一个虚拟器进程,正如我们在前一个步骤所说的在系统引导的时候启动。Zygote预加载以及初始化核心库类。通常,这些核心类一般是只读的,也是Android SDK或者核心框架的一部分。

  • Zygote进程在启动的时候,会创建一个虚拟机实例,并且在这个虚拟机实例将所有的Java核心库都加载起来。每当Zygote进程需要创建一个Android应用程序进程的时候,它就通过复制自身来实现,也就是通过fork系统调用来实现。这些被fork出来的Android应用程序进程,一方面是复制了Zygote进程中的虚拟机实例,另一方面是与Zygote进程共享了同一套Java核心库。这样不仅Android应用程序进程的创建过程很快,而且由于所有的Android应用程序进程都共享同一套Java核心库而节省了内存空间。

  • 一个应用程序进程被Zygote进程孵化出来的时候,不仅会获得Zygote进程中的Dalvik虚拟机实例拷贝,还会与Zygote一起共享Java运行时库,这完全得益于Linux内核的进程创建机制(fork)。这种Zygote孵化机制的优点是不仅可以快速地启动一个应用程序进程,还可以节省整体的内存消耗,缺点是会影响开机速度,但是毕竟Zygote是在开机过程中启动的,总体来说,是利大于弊的,毕竟整个系统只有一个Zygote进程,而可能有无数个应用程序进程,而且我们不会经常去关闭手机,大多数情况下只是让它进入休眠状态。

  • 官方文档:Sharing Memory
    Each app process is forked from an existing process called Zygote. To start a new app process, the system forks the Zygote process then loads and runs the app’s code in the new process. This approach allows most of the RAM pages allocated for framework code and resources to be shared across all app processes.

5. Activity、Window、View关系

在了解应用是如何启动后,来看下Window、Activity、View关系。

Android视图架构详解

  • 在ActivityThread调用了thread.attach(false);方法后,在Activity的attach方法里,系统会创建Activity所属的Window对象并为其设置回调接口,Window对象的创建时通过PolicyManager的makeNewWindow方法实现的。由于Activity实现了Window的Callback接口,因此当Window接收到外界的状态改变就会回调到Activity的方法。CallBack接口中方法很多,比如onAttachedToWindowonDetachedFromWindowdispatchTouchEvent等。

  • Window的真正实现是PhoneWindow,在调用Activity的setContentView时,Activity转发到PhoneWindow对象中的setContentView方法中:

    1. 如果没有DecorView,并把layoutResource添加进DecorView中。DecorView是一个FrameLayout。
    2. 将View添加到DecorView的mContentParent中
    3. 回调Activity的onContentChanged方法通知Activity视图已经发生改变
    4. ActivityThread在handleResumeActivity方法中在调用Activity.onResume()方法后调用makeVisible(),WindowManager把DecorView添加并显示出来,到这里Activity的视图才能被用户看到。
  • 在这一流程中Activity是整个模型的控制单元,Window属于承载模型,负责承载视图,View是视图显示模型。
    其中Window是一个抽象的概念,每一个Window都对应着一个View和ViewRootImpl,Window和View通过ViewRootImpl来建立联系,因为Window并不是实际存在的,它是以View的形式存在。
    View是Android中视图的呈现方式,但是View不能单独存在,它必须附着在Window这个抽象概念上,因为有视图的地方就有Window。

结语:

本篇文章主要介绍Android如何启动、应用程序如何启动,以及Dalvik虚拟机和ART虚拟机,从中也说明了些Android在系统上做了哪些优化内存的操作。
介绍了Activity、Window、View概念后,在进行overDraw等优化时也就能更理解系统本身。
有这些基础概念知识后,再梳理下篇Android如何管理Memory就会方便很多。
因为本篇介绍的东西都大而难,难免会有错误,大家多看看参考链接。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

baiiu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值