Android Developer - Platform Architecture

部分同学可能无法访问https://developer.android.com,将里面的文档贴出来供更多人学习,原链接https://developer.android.com/guide/platform/index.html


平台架构

Android 是一种基于 Linux 的开放源代码软件栈,为广泛的设备和机型而创建。下图所示为 Android 平台的主要组件。



图 1. Android 软件栈。

Linux 内核


Android 平台的基础是 Linux 内核。例如,Android Runtime (ART) 依靠 Linux 内核来执行底层功能,例如线程和低层内存管理。

使用 Linux 内核可让 Android 利用主要安全功能,并且允许设备制造商为著名的内核开发硬件驱动程序。

硬件抽象层 (HAL)


硬件抽象层 (HAL) 提供标准界面,向更高级别的 Java API 框架显示设备硬件功能。HAL 包含多个库模块,其中每个模块都为特定类型的硬件组件实现一个界面,例如相机蓝牙模块。当框架 API 要求访问设备硬件时,Android 系统将为该硬件组件加载库模块。

Android Runtime


对于运行 Android 5.0(API 级别 21)或更高版本的设备,每个应用都在其自己的进程中运行,并且有其自己的 Android Runtime (ART) 实例。ART 编写为通过执行 DEX 文件在低内存设备上运行多个虚拟机,DEX 文件是一种专为 Android 设计的字节码格式,经过优化,使用的内存很少。编译工具链(例如 Jack)将 Java 源代码编译为 DEX 字节码,使其可在 Android 平台上运行。

ART 的部分主要功能包括:

  • 预先 (AOT) 和即时 (JIT) 编译
  • 优化的垃圾回收 (GC)
  • 更好的调试支持,包括专用采样分析器、详细的诊断异常和崩溃报告,并且能够设置监视点以监控特定字段

在 Android 版本 5.0(API 级别 21)之前,Dalvik 是 Android Runtime。如果您的应用在 ART 上运行效果很好,那么它应该也可在 Dalvik 上运行,但反过来不一定

Android 还包含一套核心运行时库,可提供 Java API 框架使用的 Java 编程语言大部分功能,包括一些 Java 8 语言功能

原生 C/C++ 库


许多核心 Android 系统组件和服务(例如 ART 和 HAL)构建自原生代码,需要以 C 和 C++ 编写的原生库。Android 平台提供 Java 框架 API 以向应用显示其中部分原生库的功能。例如,您可以通过 Android 框架的 Java OpenGL API 访问 OpenGL ES,以支持在应用中绘制和操作 2D 和 3D 图形。

如果开发的是需要 C 或 C++ 代码的应用,可以使用 Android NDK 直接从原生代码访问某些原生平台库

Java API 框架


您可通过以 Java 语言编写的 API 使用 Android OS 的整个功能集。这些 API 形成创建 Android 应用所需的构建块,它们可简化核心模块化系统组件和服务的重复使用,包括以下组件和服务:

  • 丰富、可扩展的视图系统,可用以构建应用的 UI,包括列表、网格、文本框、按钮甚至可嵌入的网络浏览器
  • 资源管理器,用于访问非代码资源,例如本地化的字符串、图形和布局文件
  • 通知管理器,可让所有应用在状态栏中显示自定义提醒
  • Activity 管理器,用于管理应用的生命周期,提供常见的导航返回栈
  • 内容提供程序,可让应用访问其他应用(例如“联系人”应用)中的数据或者共享其自己的数据

开发者可以完全访问 Android 系统应用使用的框架 API

系统应用


Android 随附一套用于电子邮件、短信、日历、互联网浏览和联系人等的核心应用。平台随附的应用与用户可以选择安装的应用一样,没有特殊状态。因此第三方应用可成为用户的默认网络浏览器、短信 Messenger 甚至默认键盘(有一些例外,例如系统的“设置”应用)。

系统应用可用作用户的应用,以及提供开发者可从其自己的应用访问的主要功能。例如,如果您的应用要发短信,您无需自己构建该功能,可以改为调用已安装的短信应用向您指定的接收者发送消息。


使用 Java 8 语言功能

Android 支持所有 Java 7 语言功能,以及一部分 Java 8 语言功能(具体因平台版本而异)。本页介绍您可以使用的新语言功能、如何正确配置项目以使用这些功能,以及您可能遇到的任何已知问题。

:在为 Android 开发应用时,可以选择使用 Java 8 语言功能。您可以将项目的源和目标兼容性值保留为 Java 7,但仍须使用 JDK 8 进行编译。

支持 Java 8 语言功能需要一个名为 Jack 的新编译。Jack 仅在 Android Studio 2.1 和更高版本上才受支持。因此,如果要使用 Java 8 语言功能,则需使用 Android Studio 2.1 开发应用。

如果您已经安装了 Android Studio,请通过点击 Help > Check for Update(在 Mac 上,点击 Android Studio > Check for Updates)来确保您已更新到最新版本。如果您的工作站尚未安装 IDE,可在此下载 Android Studio

支持的 Java 8 语言功能和 API


Android 并非支持所有 Java 8 语言功能。不过,以下功能在开发面向 Android 7.0(API 级别 24)的应用时可用:

:类型注解信息仅在编译时可用,而在运行时不可用。

要在 Android 的较早版本中测试 Lambda 表达式、方法引用和类型注解,请前往您的 build.gradle 文件,将 compileSdkVersion 和 targetSdkVersion设置为 23 或更低。您仍需要启用 Jack 工具链以使用这些 Java 8 功能。

此外,也可使用以下 Java 8 语言 API:

启用 Java 8 功能和 Jack 工具链


要使用新的 Java 8 语言功能,还需使用新的 Jack 工具链。新的 Android 工具链将 Java 源语言编译成 Android 可读取的 Dalvik 可执行文件字节码,且有其自己的 .jack 库格式,在一个工具中提供了大多数工具链功能:重新打包、压缩、模糊化以及 Dalvik 可执行文件分包。

以下是构建 Android Dalvik 可执行文件可用的两种工具链的对比:

  • 旧版 javac 工具链:
    javac (.java → .class) → dx (.class → .dex)
  • 新版 Jack 工具链:
    Jack (.java → .jack → .dex)

配置 Gradle

要为您的项目启用 Java 8 语言功能和 Jack,请在模块级别的 build.gradle 文件中输入以下内容:

android {
  ...
  defaultConfig {
    ...
    jackOptions {
      enabled true
    }
  }
  compileOptions {
    sourceCompatibility JavaVersion.VERSION_1_8
    targetCompatibility JavaVersion.VERSION_1_8
  }
}

已知问题

Instant Run 目前不能用于 Jack,在使用新的工具链时将被停用。

由于 Jack 在编译应用时不生成中间类文件,依赖这些文件的工具目前不能用于 Jack。下面是一些工具示例:

  • 对类文件进行操作的 Lint 检测工具
  • 需要应用类文件的工具和库(例如使用 JaCoCo 进行仪器测试中)

如果您在使用 Jack 的过程中发现其他问题,请提交错误


在 Android Runtime (ART) 上验证应用行为

Android Runtime (ART) 是运行 Android 5.0(API 级别 21)及更高版本的设备的默认运行时。 此运行时提供了多种可改善 Android 平台和应用的性能和流畅度的功能。 您可以在 ART 简介中找到关于 ART 新功能的更多信息。

不过,部分适合 Dalvik 的技术并不适用于 ART。本文档可帮助您了解在迁移现有应用,使其与 ART 兼容时需要注意的事项。 大多数应用在使用 ART 运行时都能正常工作。

解决垃圾回收 (GC) 问题


在 Dalvik 中,应用常常发现显式调用 System.gc() 非常有用,可促进垃圾回收 (GC)。对 ART 而言这种做法的必要性低得多,尤其是当您需要通过垃圾回收来预防出现 GC_FOR_ALLOC 类型或减少碎片时。 您可以通过调用 System.getProperty("java.vm.version") 来验证正在使用哪种运行时。 如果使用的是 ART,则该属性值将是 "2.0.0" 或更高。

而且,Android 开源项目 (AOSP) 中正在开发一种紧凑型垃圾回收器,以改善内存管理。 因此,您应该避免使用与紧凑型 GC 不兼容的方法(例如保存对象实例数据的指针)。 这对于使用 Java 原生接口 (JNI) 的应用而言尤其重要。 如需了解详细信息,请参阅预防 JNI 问题

预防 JNI 问题


ART 的 JNI 比 Dalvik 的 JNI 更为严格一些。使用 CheckJNI 模式来捕获常见问题是一种特别实用的方法。 如果您的应用使用 C/C++ 代码,您应该阅读以下文章:

使用 CheckJNI 调试 Android JNI

检查 JNI 代码中的垃圾回收问题

ART 在 Android 开源项目 (AOSP) 有正在开发中的紧凑型垃圾回收器。 一旦该紧凑型垃圾回收器投入使用,便可在内存中移动对象。 如果您使用 C/C++ 代码,请勿执行与紧凑型 GC 不兼容的操作。 我们对 CheckJNI 进行了增强,以识别一些潜在的问题(如 ICS 中的 JNI 局部引用更改中所述)。

需要特别注意的一个方面是 Get...ArrayElements() 和 Release...ArrayElements() 函数的使用。 在包含非紧凑型 GC 的运行时中,Get...ArrayElements() 函数通常返回支持数组对象的实际内存的引用。 如果对其中一个返回的数组元素执行更改,数组对象本身将被更改(并且 Release...ArrayElements() 的参数往往会被忽略)。 但如果正在使用的是紧凑型 GC,则 Get...ArrayElements() 函数可能返回内存的副本。 如果您在使用紧凑型 GC 的情况下误用引用方法,可能会导致内存崩溃或其他问题。 例如:

  • 如果您对返回的数组元素执行任何更改,则在完成更改后必须调用相应的 Release...ArrayElements() 函数,以确保您所做的更改已正确地复制回基础数组对象。
  • 在您释放内存数组元素时,必须根据所做的更改使用相应的模式:
    • 如果您没有对数组元素执行任何更改,请使用 JNI_ABORT 模式,该模式会释放内存,而不将更改复制回基础数组元素。
    • 如果您对数组执行了更改,并且不再需要该引用,请使用代码 0(它将更新数组对象并释放内存副本)。
    • 如果您对您想要提交的数组执行了更改,并且您希望保留该数组的副本,请使用 JNI_COMMIT(它将更新基础数组对象并保留该副本)。
  • 调用 Release...ArrayElements() 时,将返回最初由 Get...ArrayElements() 返回的相同指针。 例如,递增原始指针(以扫描所有返回的数组元素),然后将递增的指针传递至 Release...ArrayElements() 是不安全的做法。 传递此修改后的指针可能导致释放错误的内存,进而导致内存崩溃。

错误处理

ART 的 JNI 会在多种情况下引发错误,而 Dalvik 则不然。(同样地,您可以通过使用 CheckJNI 执行测试来捕获大量此种情况)。

例如,如果使用不存在的方法(可能由于该方法已被 ProGuard 等工具移除)调用 RegisterNatives,ART 现在会正确地引发 NoSuchMethodError

08-12 17:09:41.082 13823 13823 E AndroidRuntime: FATAL EXCEPTION: main
08-12 17:09:41.082 13823 13823 E AndroidRuntime: java.lang.NoSuchMethodError:
    no static or non-static method
    "Lcom/foo/Bar;.native_frob(Ljava/lang/String;)I"
08-12 17:09:41.082 13823 13823 E AndroidRuntime:
    at java.lang.Runtime.nativeLoad(Native Method)
08-12 17:09:41.082 13823 13823 E AndroidRuntime:
    at java.lang.Runtime.doLoad(Runtime.java:421)
08-12 17:09:41.082 13823 13823 E AndroidRuntime:
    at java.lang.Runtime.loadLibrary(Runtime.java:362)
08-12 17:09:41.082 13823 13823 E AndroidRuntime:
    at java.lang.System.loadLibrary(System.java:526)

如果不使用任何方法调用 RegisterNatives,ART 也会记录错误(在 logcat 中可见):

W/art     ( 1234): JNI RegisterNativeMethods: attempt to register 0 native
methods for <classname>

此外,JNI 函数 GetFieldID() 和GetStaticFieldID() 现在会正确地引发 NoSuchFieldError,而不是仅仅返回 null。 类似地,GetMethodID() 和 GetStaticMethodID() 现在会正确地引发 NoSuchMethodError。这可能会导致 CheckJNI 由于未处理的异常或引发至原生代码的 Java 调用函数的异常而失败。 这让使用 CheckJNI 模式测试 ART 兼容型应用变得格外重要。

ART 预期 JNI CallNonvirtual...Method() 方法(例如 CallNonvirtualVoidMethod())的用户按照 JNI 规范的要求,使用该方法的声明类而不是子类。

预防堆栈大小问题


Dalvik 具有单独的原生代码堆栈和 Java 代码堆栈,并且默认的 Java 堆栈大小为 32KB,默认的原生堆栈大小为 1MB。 ART 具有统一的堆栈以改善局部性。 通常情况下,ART Thread 堆栈大小应该与 Dalvik 堆栈大小近乎相同。 但如果您显式设置了堆栈大小,则可能需要针对 ART 中运行的应用重新访问这些值。

  • 在 Java 中,查看用于指定显式堆栈大小的 Thread 构造函数的调用。 例如,如果发生 StackOverflowError,您将需要增加该大小。
  • 在 C/C++ 中,查看如何将 pthread_attr_setstack() 和 pthread_attr_setstacksize() 用于同时通过 JNI 运行 Java 代码的线程。 以下是某个应用在 pthread 过小的情况下尝试调用 JNI AttachCurrentThread() 时记录的错误示例:
    F/art: art/runtime/thread.cc:435]
        Attempt to attach a thread with a too-small stack (16384 bytes)

对象模型更改


Dalvik 错误地允许子类覆盖包私有的方法。ART 在这类情况下会发出警告:

Before Android 4.1, method void com.foo.Bar.quux()
would have incorrectly overridden the package-private method in
com.quux.Quux

如果您希望在另一个包中覆盖某个类的方法,请将该方法声明为 public 或 protected

Object 现在包含私有字段。对于反射其类层次中的字段的应用,应小心避免尝试查看 Object 的字段。 例如,如果您正在向上迭代某个作为串行化框架一部分的类层次,以下情况下请停止迭代操作

Class.getSuperclass() == java.lang.Object.class
而不是继续操作,直至该方法返回  null

如果没有任何参数,代理 InvocationHandler.invoke() 现在将会收到 null,而不是空数组。 之前记录过此行为,但在 Dalvik 中未得到正确处理。 之前版本的 Mockito 难以处理这一问题,因此在使用 ART 测试时请使用更新的 Mockito 版本。

修复 AOT 编译问题


ART 的提前 (AOT) Java 编译应适用于所有标准 Java 代码。 编译由 ART 的 dex2oat 工具执行,如果您在安装时遇到任何与 dex2oat 有关的问题,请联系我们(请参阅报告问题),以便我们能够尽快将其修复。 需要注意的几个问题:

  • ART 会在安装时执行比 Dalvik 更严格的字节代码验证。 Android 构建工具生成的代码应该没有问题。但一些后期处理工具(尤其是执行模糊处理的工具)可能会生成被 Dalvik 容忍而被 ART 拒绝的无效文件。 我们已经与工具供应商合作,查找并修复此类问题。 在许多情况下,获取最新版本的工具并重新生成 DEX 文件可以修复这些问题。
  • 一些被 ART 验证器标记的典型问题包括:
    • 无效的控制流
    • 失衡的 moniterenter/moniterexit
    • 0 长度参数类型列表大小
  • 一些应用对 /system/framework/data/dalvik-cache 中或 DexClassLoader 的优化输出目录中的安装的 .odex 文件格式具有依赖性。 这些文件现在是 ELF 文件,而不是 DEX 文件的扩展形式。 尽管 ART 努力遵循与 Dalvik 相同的命名和锁定规则,但应用不能依赖于文件格式,因为该格式可能未经通知便发生更改。

    报告问题


    如果您遇到任何不是由于应用 JNI 问题而导致的问题,请通过位于 https://code.google.com/p/android/issues/list 的 Android 开源项目问题跟踪器报告这些问题。请包含 "adb bugreport" 和 Google Play 商店中的应用链接(如果可用)。 否则,如果可能,请附加用于重现该问题的 APK。 请注意,这些问题(包括附件)是公开可见的。



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值