Android知识Android开发Android技术知识

Android魔幻之旅(一):应用基础知识

2016-11-15  本文已影响0人  人间入画
本文内容:

Android 应用采用 Java 编程语言编写。Android SDK 工具将您的代码——连同任何数据和资源文件编译到一个 APK:Android 软件包,即带有** .apk**后缀的存档文件中。一个 APK 文件包含 Android 应用的所有内容,它是基于 Android 系统的设备用来安装应用的文件。

安装到设备后,每个 Android 应用都运行在自己的安全沙箱内:

Android 系统可以通过这种方式实现最小权限原则。也就是说,默认情况下,每个应用都只能访问执行其工作所需的组件,而不能访问其他组件。 这样便营造出一个非常安全的环境,在这个环境中,应用无法访问系统中其未获得权限的部分。

不过,应用仍然可以通过一些途径与其他应用共享数据以及访问系统服务:

以上内容阐述了有关 Android 应用在系统内存在方式的基础知识。本文的其余部分将向您介绍以下内容:

应用组件


应用组件是 Android 应用的基本构建基块。每个组件都是一个不同的入口点,系统可以通过它进入您的应用。 并非所有组件都是用户的实际入口点,有些组件相互依赖,但每个组件都以独立实体形式存在,并发挥特定作用 — 每个组件都是唯一的构建基块,有助于定义应用的总体行为。

一共有四种不同的应用组件类型。每种类型都服务于不同的目的,并且具有定义组件的创建和销毁方式的不同生命周期。

以下便是这四种应用组件类型:

Activity

Activity 表示具有用户界面的单一屏幕。例如,电子邮件应用可能具有一个显示新电子邮件列表的 Activity、一个用于撰写电子邮件的 Activity 以及一个用于阅读电子邮件的 Activity。 尽管这些 Activity 通过协作在电子邮件应用中形成了一种紧密结合的用户体验,但每一个 Activity 都独立于其他 Activity 而存在。 因此,其他应用可以启动其中任何一个 Activity(如果电子邮件应用允许)。 例如,相机应用可以启动电子邮件应用内用于撰写新电子邮件的 Activity,以便用户共享图片。Activity促进系统和应用程序之间的以下关键交互:

Service

Service 是一种在后台运行的组件,用于执行长时间运行的操作或为远程进程执行作业。 Service不提供用户界面。 例如,当用户位于其他应用中时,Service可能在后台播放音乐或者通过网络获取数据,但不会阻断用户与 Activity 的交互。 诸如 Activity 等其他组件可以启动服务,让其运行或与其绑定以便与其进行交互。
事实上有两种不同语言的Service告知系统如何管理应用——Started services 和Bound services:

1)Started services 告诉系统需要保持他们的运行直到工作完成,比如用户在后台同步某些数据或者用户离开应用后播放音乐。然而后台同步数据和播放音乐也代表了两种不同类型的StartedService,并且系统将用不同的方式处理他们。

2)Bound service 的运行是因为一些应用(或系统)需要使用service,service在这里的作用基本上是为另一个进程提供一个接口,因此系统得知某些进程之间存在依赖关系。所以如果进程A绑定到进程B中的服务,则系统得知需要保持进程B(及其service)为A运行。此外,如果进程A是用户当前关注的进程,则系统也将过程B视作用户关注的进程。由于它们的灵活性(无论好坏),service已经成为一个真正有用的构建块,用于各种更高级别的系统概念。 动态壁纸,通知监听器,屏幕保护程序,输入法,辅助功能服务和许多其他核心系统功能都构建为应用程序实现的服务,并且在他们运行时被系统绑定。

Broadcast receiver

Broadcast receiver 是使得系统能够在常规用户流之外向应用递送事件的组件,允许应用响应于系统范围的广播通告。因为广播接收机是另一个明确定义的应用程序入口,所以系统甚至可以向当前未运行的应用程序提供广播。
有许多广播就是从系统发出的,例如,广播通知屏幕已关闭,电池电量低,或已捕获一张截图。 应用程序也可以启动广播,例如,让其他应用程序知道某些数据已下载到设备,并可供他们使用。 尽管广播接收器不显示用户界面,但是它们在广播事件发生时可以创建状态栏通知以警告用户。 更常见的是,广播接收机仅仅是到其他组件的网关,并且旨在进行非常少量的工作。

Content provider

内容提供者管理一组共享的应用程序数据,这些数据可以存储在文件中,SQLite数据库中,Web网页上或应用程序可访问的任何其他持久化存储位置。通过内容提供者,如果内容提供者允许的话,其他应用可以查询或修改数据。例如,Android系统提供了一个Content provider用于管理用户的联系人信息。将内容提供者视为数据库上的抽象是很诱人的,因为对于这种常见的情况,内置了很多API和支持。然而,它们具有与系统设计方面不同的核心目的。对于系统,内容提供者是用于发布由URI方案标识的命名数据项的应用的入口点。因此,应用程序可以决定如何将其包含的数据映射到URI命名空间,将这些URI传递给其他实体,这些实体又可以使用它们来访问数据。有一些特定的事情,这允许系统管理一个应用程序:

Android 系统设计的独特之处在于,任何应用都可以启动其他应用的组件。 例如,如果您想让用户使用设备的相机拍摄照片,很可能有另一个应用可以执行该操作,那么您的应用就可以利用该应用,而不是开发一个 Activity 来自行拍摄照片。 您不需要集成甚至链接到该相机应用的代码,而是只需启动拍摄照片的相机应用中的 Activity。 完成拍摄时,系统甚至会将照片返回您的应用,以便您使用。对用户而言,就好像相机真正是您应用的组成部分。

当系统启动某个组件时,会启动该应用的进程(如果尚未运行),并实例化该组件所需的类。 例如,如果您的应用启动相机应用中拍摄照片的 Activity,则该 Activity 会在属于相机应用的进程,而不是您的应用的进程中运行。因此,与大多数其他系统上的应用不同,Android 应用并没有单一入口点(例如,没有 main()函数)。

由于系统在单独的进程中运行每个应用,且其文件权限会限制对其他应用的访问,因此您的应用无法直接启动其他应用中的组件, 但 Android 系统却可以。因此,要想启动其他应用中的组件,您必须向系统传递一则消息,说明您想启动特定组件的 Intent

启动组件

四种组件类型中的三种——Activity、服务和广播接收器通过名为 Intent 的异步消息进行启动。Intent 会在运行时将各个组件相互绑定(您可以将 Intent 视为从其他组件请求操作的信使),无论组件属于您的应用还是其他应用。

Intent 使用 Intent 对象创建,它定义的消息用于启动特定组件(显式Intent)或特定类型的组件(隐式Intent)。

对于 Activity 和服务, Intent 定义要执行的操作(例如,“查看”或“发送”某个内容),并且可以指定要执行操作的数据的 URI(以及正在启动的组件可能需要了解的信息)。例如, Intent 传达的请求可以是启动一个显示图像或打开网页的 Activity。在某些情况下,您可以启动 Activity 来接收结果,在这种情况下,Activity 也会在Intent中返回结果。例如,您可以发出一个 Intent,让用户选取某位联系人并将其返回给您。返回的 Intent 包含一个指向所选联系人的 URI。

对于广播接收器, Intent 只会定义要广播的通知。例如,指示设备电池电量不足的广播只包括指示“电池电量不足”的已知操作字符串。

与activities,services和broadcast receivers不同,content providers不由Intent启动,而是在一个ContentResolvers发出请求后启动。Content providers通过content resolvers处理所有直接事务,使得通过内容提供者执行事务的组件可以无需执行事务,而是改为在ContentResolver对象上调用方法。这会在内容提供者与请求信息的组件之间留出一个抽象层(以确保安全)。

每种类型的组件有不同的启动方法:

清单文件


在 Android 系统启动应用组件之前,系统必须通过读取应用的 AndroidManifest.xml文件(“清单”文件)确认组件存在。 您的应用必须在此文件中声明其所有组件,该文件必须位于应用项目目录的根目录中。
除了声明应用的组件外,清单文件还有许多其他作用,如:

声明组件

清单文件的主要任务是告知系统有关应用组件的信息。例如,清单文件可以像下面这样声明 Activity:

<?xml version="1.0" encoding="utf-8"?>
<manifest ... >
    <application android:icon="@drawable/app_icon.png" ... >
        <activity android:name="com.example.project.ExampleActivity"
                  android:label="@string/example_label" ... >
        </activity>
        ...
    </application>
</manifest>

在 <application> 元素中,android:icon 属性指向标识应用的图标所对应的资源。
在 <activity> 元素中,android:name 属性指定 Activity 子类的完全限定类名,android:label 属性指定用作 Activity 的用户可见标签的字符串。

您必须通过以下方式声明所有应用组件:

您包括在源代码中,但未在清单文件中声明的 Activity、服务和内容提供程序对系统不可见,因此也永远不会运行。 不过,广播接收器可以在清单文件中声明或在代码中动态创建(如 BroadcastReceiver 对象)并通过调用 registerReceiver() 在系统中注册。

声明组件功能

如上文启动组件中所述,您可以使用 Intent 来启动 Activity、服务和广播接收器。 您可以通过在 Intent 中显式命名目标组件(使用组件类名)来执行此操作。 不过,Intent 的真正强大之处在于隐式 Intent 概念。 隐式 Intent 的作用无非是描述要执行的操作类型(还可选择描述您想执行的操作所针对的数据),让系统能够在设备上找到可执行该操作的组件,并启动该组件。 如果有多个组件可以执行 Intent 所描述的操作,则由用户选择使用哪一个组件。

警告:如果您使用 Intent 启动service,请通过使用显式 Intent 确保您的应用程序是安全的。 使用隐式 Intent 启动service是一个安全隐患,因为您无法确定什么service将响应 Intent,并且用户无法看到哪个服务启动。 从Android 5.0(API级别21)开始,如果您使用隐式 Intent 调用bindService(),系统将抛出异常。不要为您的服务声明Intent Filter。

系统通过将接收到的 Intent 与设备上的其他应用的清单文件中提供的 intent filters进行比较来确定可以响应 Intent 的组件。

当您在应用的清单文件中声明 Activity 时,可以选择性地加入声明 Activity 功能的 Intent Filter,以便响应来自其他应用的 Intent。 您可以通过将 <intent-filter> 元素作为组件声明元素的子项进行添加来为您的组件声明 Intent Filter。

例如,如果您开发的电子邮件应用包含一个用于撰写新电子邮件的 Activity,则可以像下面这样声明一个 Intent Filter来响应“send” Intent(以发送新电子邮件):

<manifest ... >
    ...
    <application ... >
        <activity android:name="com.example.project.ComposeEmailActivity">
            <intent-filter>
                <action android:name="android.intent.action.SEND" />
                <data android:type="*/*" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>
    </application>
</manifest>

然后,如果另一个应用创建了一个包含ACTION_SEND 操作的 Intent,并将其传递到 startActivity(),则系统可能会启动您的 Activity,以便用户能够草拟并发送电子邮件。

声明组件要求

Android系统可支持多种设备。不同的设备所具备的功能是不一致的。为了避免用户将应用安装在其不支持的设备上,比如缺少应用所必需的功能,需要在清单文件中详细列出应用所要求的硬件和软件功能。一般来说这些都是资讯类的信息,Android系统不会去读取它。而Android应用商店(例如Google Play)会根据清单文件列明的硬件要求,在用户搜索应用时过滤应用,或者在用户尝试安装应用时提示用户。

例如,如果您的应用需要相机,并使用 Android 2.1(API 级别 7)中引入的 API,您应该像下面这样在清单文件中以要求形式声明这些信息:

<manifest ... >
    <uses-feature android:name="android.hardware.camera.any"
                  android:required="true" />
    <uses-sdk android:minSdkVersion="7" android:targetSdkVersion="19" />
    ...
</manifest>

现在,没有相机且 Android 版本低于 2.1 的设备将无法从 Google Play 安装您的应用。

不过,您也可以声明您的应用使用相机,但并不要求必须使用。 在这种情况下,您的应用必须将 required 属性设置为 "false",并在运行时检查设备是否具有相机,然后根据需要停用任何相机功能。

应用资源


Android 应用并非只包含代码 — 它还需要与源代码分离的资源,如图像、音频文件以及任何与应用的视觉呈现有关的内容。 例如,您应该通过 XML 文件定义 Activity 用户界面的动画、菜单、样式、颜色和布局。 使用应用资源能够在不修改代码的情况下轻松地更新应用的各种特性,并可通过提供备用资源集让您能够针对各种设备配置(如不同的语言和屏幕尺寸)优化您的应用。

对于您的 Android 项目中包括的每一项资源,SDK 构建工具都会定义一个唯一的整型 ID,您可以利用它来引用应用代码或 XML 中定义的其他资源中的资源。 例如,如果您的应用包含一个名为 logo.png 的图像文件(保存在 res/drawable/ 目录中),则 SDK 工具会生成一个名为 R.drawable.logo 的资源 ID,您可以利用它来引用该图像并将其插入您的用户界面。

提供与源代码分离的资源的其中一个最重要优点在于,您可以提供针对不同设备配置的备用资源。 例如,通过在 XML 中定义 UI 字符串,您可以将字符串翻译为其他语言,并将这些字符串保存在单独的文件中。 然后,Android 系统会根据向资源目录名称追加的语言限定符(如为法语字符串值追加 res/values-fr/)和用户的语言设置,对您的 UI 应用相应的语言字符串。

Android 支持许多不同的备用资源限定符。限定符是一种加入到资源目录名称中,用来定义这些资源适用的设备配置的简短字符串。 再举一例,您应该经常会根据设备的屏幕方向和尺寸为 Activity 创建不同的布局。 例如,当设备屏幕为纵向(长型)时,您可能想要一种垂直排列按钮的布局;但当屏幕为横向(宽型)时,应按水平方向排列按钮。 要想根据方向更改布局,您可以定义两种不同的布局,然后对每个布局的目录名称应用相应的限定符。 然后,系统会根据当前设备方向自动应用相应的布局。

上一篇下一篇

猜你喜欢

热点阅读