第 4 章 Android 应用程序示例
4.6 SkeletonApp 的编译结构
Android 中程序的编译结构基本类似,SkeletonApp 的应用程序包 SkeletonApp.apk 经过解压缩后,包含了 下面的一些内容:
SkeletonApp.apk/
|-- AndroidManifest.xml (经过 aapt 处理的工程描述文件)
|-- META-INF
| |-- CERT.RSA
| |-- CERT.SF
| `-- MANIFEST.MF
|-- classes.dex (Dalvik 的字节码)
|-- res
| |-- drawable
| | `-- violet.jpg (保持原状的图片文件)
| `-- layout
| `-- skeleton_activity.xml (经过 aapt 处理的布局文件)
`-- resources.arsc
在这里 drawable 中图片文件保持原状,layout 中的布局文件经过 aapt 处理成为压缩的文本文件,其他的资 源文件在最终的程序包中,不再单独存在。
第 5 章 Android应用程序的内容
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
5.1 Android 应用程序的感念性描述 5.2 应用程序包含的各个文件
5.3 使用 am 工具启动 Android 应用程序
5.1 Android应用程序的概念性描述
Android 应用程序包含了工程文件、代码和各种资源,主要由 Java 语言编写,每一个应用程序将被编译成 Android 的一个 Java 应用程序包(*.apk)。
由于 Android 系统本身是基于 Linux 操作系统运行的,因此 Android 应用程序也运行于 Linux 环境中,它 们具有以下的特点:
在默认情况下,每一个应用程序运行于它们的 Linux 进程中;
每个进程具有自己的虚拟机(VM),所以每个应用程序运行于独立的环境中;
在默认情况下,每一个应用程序具有唯一的 Linux 用户 ID。通过设置权限让应用程序只对用户和应用程 序本身可见,也有一些方法可以把它们暴露给其他的应用程序。
5.1.1.应用程序的组成部分
一般情况下,Android 应用程序由以下 4 种组件构成:
活动(Activity);
广播接收器(BroadcastReceiver);
服务(Service);
内容提供者(Content Provider)。
一个 Android 应用程序是一个包(Package),包中可能包含一个或者多个 Android 组件(component)。
(1)活动(Activity)
活动是最基本的 Android 应用程序组件,在应用程序中,一个活动通常就是一个单独的用户界面。每一个 活动都被实现为一个独立的类,并且从活动(Activity)基类中继承而来,活动类将会显示由视图(View)控 件组成的用户接口,并对事件(Event)做出响应。大多数的应用程序都会有多个用户界面,因此便会有多个相 应的活动。
Android 的一个活动一般对应界面中的一个屏幕显示,可以理解成一个界面,每一个活动在界面上可以包 含按钮、文本框等多种可视的 UI 元素。
(2)广播接收器(BroadcastReceiver)
广播接收器用于让应用程序对一个外部事件做出响应。例如:电话呼入事件、数据网络可用通知或者到了 晚上时进行通知。
(3)服务(Service)
一个服务是一个具有一段较长生命周期但没有用户界面的程序。例如:一个正在从播放列表中播放歌曲的 媒体播放器在后台运行。
(4)内容提供者(Content Provider)
应用程序能够将它们的数据保存到文件或 SQLite 数据库中,甚至是任何有效的设备中。当需要将数据与其 他的应用共享时,内容提供者将会很有用。一个内容提供者类实现了一组标准的方法,从而能够让其他应用程序 保存或读取此内容提供者处理的各种数据类型。
5.1.2.应用程序的生命周期
Android 系统中的不同组件具有不同的生命周期。Android 根据每个进程中运行的组件以及组件的状态把进 程放入一个重要性分级(importance hierarchy)中。Android 进程的重要性分级,可以理解成执行的优先级。
Android 进程的类型包括(按重要性分级排序):
(1)前台(Foreground)进程
与用户当前正在做的事情密切相关,不同的应用程序组件能够通过不同的方法使它的宿主进程移到前台。
当下面任何一个条件满足时,都可以考虑将进程移到前台。
进程正在屏幕的最前端运行一个与用户交互的 Activity(它的 onResume()方法被调用);
进程有一个正在运行的 BroadcastReceiver(它的 BroadcastReceiver.onReceive()方法正在执行);
进程有一个 Service,并且在 Service 的某个方法(Service.onCreate()、Service.onStart()或者 Service.onDestroy())
内有正在执行的代码。
(2)可见(Visible)进程
它有一个可以被用户从屏幕上看到的 Activity,但不在前台——其 onPause()方法被调用。例如:如果前台 的 Activity 是一个对话框,以前的 Activity 隐藏在对话框之后,就可能出现这种进程。这样的进程很重要,一 般不允许被杀死,除非为了保证前台进程的运行不得不这样做。
(3)服务(Service)进程
有一个已经用 startService() 方法启动的 Service,虽然这些进程用户无法直接看到,但它们做的事情却是用 户所关心的(例如:后台 MP3 回放或后台网络数据的上传/下载)。因此,系统将一直运行这些进程,除非内 存不足以维持所有的前台进程和可见进程。
(4)后台(Background)进程
拥有一个当前用户看不到的 Activity(它的 onStop()方法被调用),这些进程对用户体验没有直接的影响。
如果它们正确执行了 Activity 生命周期,系统可以在任意时刻杀死进程来回收内存,并提供给前面 3 种类型的 进程使用。系统中通常有很多这样的进程在运行,因此要将这些进程保存在 LRU 列表中,以确保当内存不足 时用户最近看到的进程最后一个被杀死。
(5)空(Empty)进程
不包含任何处于活动状态的应用程序组件。保留这种进程的唯一原因是,当下次应用程序的某个组件需要 运行时,不需要重新创建进程,这样可以提高启动速度。
以上所说的“进程”是从系统运行的角度考虑的,各种不同的进程可以理解成 Android 的各种组件的不同 状态机(state machine)。如果从应用程序的代码以及运行情况考虑,可以关注 Android 的各种组件相对应的生 命周期。
1. 活动的生命周期
活动是 Android 中最重要、最基础的组件,用户在界面上看到的一个个可以切换的屏幕界面就是 Android 中的活动。活动的生命周期如图 1 所示。
图 1 活动(Activity)的生命周期
运行活动的情景:当一个活动被启动时,活动中的 onCreate()、onStart()和 onResume()这 3 个方法被依次调 用,活动对应的界面出现在屏幕上。
活动被“覆盖”的情景:Android 的活动一般都占据一个完整的屏幕,从当前活动启动另外一个活动时,另 一个活动将被启动到前台(Foreground),当前活动转入后台(Background),这时活动的 onPasuse()方法将 被调用,活动转入后台运行。如果活动变为不可见,还将调用 onStop()方法。在转入后台时,onStop()是否 被调用取决于活动是否被完全覆盖,在新的活动有透明部分时,转入后台的活动依然“可见”,其他情况下
(较多数的情况)活动均进入不可见状态(被完全覆盖)。
活动被恢复的情景:当界面上最前面的活动退出后,它所覆盖的活动将被恢复,这时 onResume()方法将被 调用,活动重新转入前台运行。
活动完全退出的情景:当使用回退(Back)按钮退出活动时,onDestroy()方法将被调用,活动关闭。如果 系统缺少内存时,也会杀死(kill)后台的活动,其中优先杀死不可见的活动,可见的活动一般不会被杀死。
2. 服务的生命周期
服务可以长时间运行,它的特点是没有可视化界面,服务的生命周期如图 2 所示。
图 2 服务(Service)的生命周期
使用 StartService 运行服务的情景:使用这种方法启动服务,服务的 onCreate()和 onStart()这两个方法将被 调用,服务会在后台运行直到退出,退出时将调用 onDestroy()方法。
使用 bindService 运行服务的情景:使用这种方法启动服务,调用者(也就是服务的客户端)将获得和服务 交互的类,通过其调用时服务的相关内容会处于活动状态。
3. 广播接收器的生命周期
广播接收器有一个单一的回调方法 onReceive(),当广播消息到达接收器时,Android 将调用这个方法,并 传递给包含在这个消息中的 Intent 对象。
广播接收器只有在这个方法的执行过程中才处于活动状态,当 onReceive()返回后,广播接收器将不再处于 活动状态。广播接收器的功能类似于一个回调函数,只是单次运行时处于活动状态。
5.2 应用程序包含的各个文件
Android 应用程序一般包含在一个单一的文件夹中,即每一个 Android 应用程序是一个独立的工程,包含了 以下文件:
Android.mk:统一工程文件,在 SDK 开发中可以不需要;
AndroidManifest.xml:工程描述文件,在其中定义了各种组件;
Java 源代码:按照 Java 包的方式来组织目录结构,包括各个 Java 类的源代码;
资源文件:包含 XML 文件、图片、原始数据文件等,其中表示界面情况的布局(Layout)文件比较重要。
在编译 Android 应用程序的过程中,Java 源代码使用 Sun JDK 将 Java 源程序编译成 Java 字节码文件(多 个后缀名为.class 的文件),这一步骤和标准的 Java 一致,然后通过 Android 自带的工具软件 dex 把所有的字 节码文件转成 dex 文件(单一文件 classes.dex)。
AndroidManifest.xml 文件经过 Android 打包工具(aapt)处理后形成二进制格式 AndroidManifest.xml 文件, 码作为整个 Android 编译的中间过程,最终生成的 dex 文件(classes.dex)是一个单一文件,将工程中所有的 Java 源代码文件对应的字节码集成在一起。资源文件和 AndroidManifest.xml 文件通过 aapt 工具进行处理。
在运行时,APK 包将首先进行“安装”,也就是将其中的 dex 文件进行优化,优化后的文件被保存到缓存
am instrument [-r] [-e <ARG_NAME> <ARG_VALUE>] [-p <PROF_FILE>]
[-w] <COMPONENT>
使用 am start 是其中的一个功能,INTENT 使用的选项如下所示:
[-a <ACTION>] [-d <DATA_URI>] [-t <MIME_TYPE>]
[-c <CATEGORY> [-c <CATEGORY>] ...]
[-e|--es <EXTRA_KEY> <EXTRA_STRING_VALUE> ...]
[--ez <EXTRA_KEY> <EXTRA_BOOLEAN_VALUE> ...]
[-e|--ei <EXTRA_KEY> <EXTRA_INT_VALUE> ...]
[-n <COMPONENT>] [-f <FLAGS>] [<URI>]
主要的参数是使用-a 指定使用的动作(action),使用-d 指定数据(data),使用 URI 的格式,使用-n 指 定组件。
例如:使用 am 启动应用程序的格式如下所示:
# am start -n {包名}/{包名}.活动名
启动 Android 设置工具的命令如下所示:
# am start -n com.android.settings/com.android.settings.Settings
启动 Android 计算器程序的命令如下所示:
# am start -n com.android.calculator2/com.android.calculator2.Calculator
启动 Android 录音机程序的命令如下所示:
# am start -n com.android.soundrecorder/com.android.soundrecorder.SoundRecorder
启动 Android 照相机程序的命令如下所示:
# am start -n com.android.camera/com.android.camera.Camera
启动 Android 摄像机程序的命令如下所示:
# am start -n com.android.camera/com.android.camera.VideoCamera
启动 Android 音乐浏览器的命令如下所示:
# am start -n com.android.music/com.android.music.MusicBrowserActivity
启动 Android 视频浏览器的命令如下所示:
# am start -n com.android.music/com.android.music.VideoBrowserActivity
启动 Android 网络浏览器等的命令如下所示:
# am start -n com.android.browser/com.android.browser.BrowserActivity
在上面的程序中,有些程序位于同一个包中,例如:音乐浏览器和视频浏览器都在 Music 包中,照相机和 摄像机都在 Camera 包中。
在上面的程序中,有些程序位于同一个包中,例如:音乐浏览器和视频浏览器都在 Music 包中,照相机和 摄像机都在 Camera 包中。