1.3 Android应用程序的结构
1.3.1 Android应用程序的代码组成
Android应用程序包含了工程文件、代码和各种资源,主要由Java语言编写,每一个应用程序将被编译成Android的一个Java应用程序包(*.apk)。
一般情况下,Android应用程序由以下4种组件构成:
活动(Activity);
服务(Service);
广播接收器(BroadcastReceiver);
内容提供者(ContentProvider)。
一个Android应用程序是一个包(Package),包中可能包含一个或者多个Android组件(component)。
提示:Android应用程序包当中也有一个应用程序的概念,称之为application。相比4种组件,这个概念在Android中对程序的运行时影响不大。
Android应用程序一般包含在一个单一的文件夹中,即每一个Android应用程序是一个独立的工程,包含了以下文件。
Android.mk:工程编译文件,作用类似于Makefile,在SDK开发中不需要;
AndroidManifest.xml:工程描述文件,在其中定义了各种组件;
src目录:Java源代码,按照Java包的方式来组织目录结构,包括各个Java类的源代码;
res目录:资源文件,包含XML文件、图片、原始数据文件等,其中表示界面的布局(Layout)文件比较重要;
assets目录:资产文件,将原样复制到目标的应用程序包当中。
1.3.2 Android应用示例
SkeletonApp是Android中一个应用程序的骨架,这个程序包含了两个按钮和菜单,两个按钮分别用于清除编辑文本框中的内容,菜单的功能和两个按钮是相同的,菜单是Android中的标准UI元素。SkeletonApp程序的运行结果如图1-35所示。
图1-35 SkeletonApp程序的运行结果(左:启动界面;右:使用菜单)
1.源代码工程结构
SkeletonApp工程的源文件的结构(没有包含相对独立的用于测试的代码)按照目录树的方式如下所示:
SkeletonApp/ |-- Android.mk 工程管理文件 |-- AndroidManifest.xml 工程描述文件 |-- res [资源文件目录] | |-- drawable
| | `-- violet.jpg 图片文件 | |-- layout | | `-- skeleton_activity.xml [布局文件目录] | `-- values | |-- colors.xml 颜色资源文件 | |-- strings.xml 字符串资源文件 | `-- styles.xml 样式资源文件 |-- src [Java源代码目录] `-- com `-- example `-- android `-- skeletonapp `-- SkeletonActivity.java
Java源代码SkeletonActivity.java中构建了菜单、按钮的动作等功能,其中最核心部分如下所示:
package com.example.android.skeletonapp; // 定义应用程序的包名 public class SkeletonActivity extends Activity { // ......省略部分内容 @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.skeleton_activity); // 设置其中的布局文件 // ......省略部分内容 } }
在SkeletonApp中,资源目录res中的values目录中除了strings.xml文件,还包含了colors.xml和styles.xml文件,这两种文件也是Android中的标准资源文件。
colors.xml文件的内容如下所示:
<?xml version="1.0" encoding="utf-8"?> <resources> <!— 可以通过Resources.getColor()得到。--> <color name="red">#f00</color> <!--可以通过Resources.getDrawable()得到 --> <drawable name="semi_black">#80000000</drawable> </resources>
styles.xml文件用于定义程序中的不同样式,其中的内容如下所示:
<?xml version="1.0" encoding="utf-8"?> <resources> <style name="ActionButton"> <item name="android:layout_width">wrap_content</item> <item name="android:layout_height">wrap_content</item> <item name="android:textAppearance"> @style/TextAppearance.ActionButton</item> </style> <style name="TextAppearance" parent="android:TextAppearance"> </style> <style name="TextAppearance.ActionButton"> <item name="android:textStyle">italic</item> </style> </resources>
资源目录res还包含了drawable目录,表示可以绘制的内容,这里的violet.jpg是一个JPEG格式的文件。
在res/layout目录中的布局文件skeleton_activity.xml中的部分内容引用了以上资源,内容如下所示:
<LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:gravity="center_horizontal" android:orientation="horizontal" android:background="@drawable/semi_black"> <Button android:id="@+id/back" style="@style/ActionButton" android:text="@string/back" /> <ImageView android:id="@+id/image" android:layout_width="wrap_content" android:layout_height="wrap_content" android:paddingLeft="4dip" android_paddingRight="4dip" android:src="@drawable/violet" /> <Button android:id="@+id/clear" style="@style/ActionButton" android:text="@string/clear" android:textColor="@color/red" /> </LinearLayout>
布局文件中引用了上面的资源,颜色可以作为字体的颜色,style/ActionButton作为按钮的样式,drawable/semi_black表示了背景的内容,drawable/violet表示引用violet.jpg图片作为图像的内容。例如根据上面styles.xml文件中的定义,两个按钮上的字体为斜体,第二个按钮的字体颜色为红色。
2.SDK环境中的编译结构
以上的SkeletonApp应用程序包在SDK环境中经过编译之后将生成一些额外的文件。除去源代码中已经具有的文件以下,自动生成的内容的目录结构如下所示:
SkeletonApp |-- bin [目标文件目录] | |-- classes.dex | |-- com [根据目录生成字节码] | | `-- example | | `-- android | | `-- skeletonapp | | |-- R$attr.class [R*.class为资源对应字节码] | | |-- R.class | | |-- R$color.class | | |-- R$drawable.class | | |-- R$id.class | | |-- R$layout.class | | |-- R$string.class | | |-- R$style.class | | |-- SkeletonActivity$1.class [主要Java源文件的字节码] | | |-- SkeletonActivity$2.class | | `-- SkeletonActivity.class | |-- resources.ap_ | `-- SkeletonApp.apk |-- default.properties [属性文件] |-- gen [自动生成Java文件的目录] `-- com
`-- example `-- android `-- skeletonapp `-- R.java
在SDK环境中,自动生成的文件包括以下几项内容。
default.properties:工程的设置信息;
gen目录:包含资源文件和AIDL文件生成的源代码文件,其中资源文件生成的源代码文件为R.java;
bin目录:为根据类名生成的各个Java标准字节码文件(*.class),classes.dex文件为经过转化的运行于Android系统的特定字节码。XXX.apk为最终生成的APK包,不同工程根据名字所生成的包名不同,例如SkeletonApp.apk。
3.APK包的结构
Android中程序的编译结构基本类似,SkeletonApp工程生成的应用程序包SkeletonApp.apk,本质上是一个无压缩率的ZIP包。经过解压缩后,可以看到其中包含了下面的一些内容:
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处理成为压缩的文本文件。
1.3.3 应用程序生成运行过程
1.应用程序包生成的过程
在编译Android应用程序的过程中,Java源代码使用Sun JDK将Java源程序编译成Java字节码文件(多个后缀名为.class的文件),这一步骤和标准的Java一致,然后通过Android自带的工具软件dex把所有的字节码文件转成DEX文件(单一文件classes.dex)。
AndroidManifest.xml文件经过Android打包工具(aapt)处理后形成二进制格式AndroidManifest.xml文件,实质的内容与以前相同。
各个资源文件也经过aapt处理,其中布局等文本文件处理成二进制文件,图片等文件保持不变。
最后将这三个部分组合成一个应用程序包(*.apk)。AndroidManifest.xml描述文件、Java源文件、资源文件是Android应用程序的三个部分;在编译之前的工程中是这三个部分,在编译之后APK包依然是由这三个部分组成的。
Android应用程序的编译过程如图1-36所示。
图1-36 Android应用程序的编译过程
如图1-36所示,Android源文件经过了标准的Java编译器的编译,又经过了dx工具的处理,标准的Java字节码作为整个Android编译的中间过程,最终生成的DEX文件(classes.dex)是一个单一文件,将工程中所有的Java源代码文件对应的字节码集成在一起。资源文件和AndroidManifest.xml文件通过aapt工具进行处理。
除此之外,assets目录及所有内容,将会原样保存,放入到目标APK包中,作为预编译的内容存在。在程序中,可以直接访问其中的文件。
2.应用程序包安装运行的过程
在运行时,APK包将首先进行“安装”,也就是将其中的DEX文件进行优化,优化后的文件被保存到缓存区域,生成格式为DEY的优化文件,然后Dalvik虚拟机将运行这些DEY文件。如果应用程序包文件不发生变化,DEY文件不会被重新生成;在应用程序包发生更新的情况下,将重新由DEX生成DEY。
Android和标准Java的JAR包中字节码的不同之处在于,标准Java每个源文件对应字节码文件一个Class文件,而Android将所有Java字节码生成一个DEX文件。