03. 应用项目工程结构和资源

2021/5/3 Android 开发基础

如果您是首次接触 Android,并希望开始编写代码,请从下面构建项目开始了解相关知识。

# 构建项目

本节将介绍如何使用 Android Studio 创建新的 Android 项目,并介绍该项目中的相关的一些工程文件。

如需创建新的 Android 项目,请按以下步骤操作:

  1. 安装最新版本的 Android Studio (opens new window)
  2. Welcome to Android Studio 窗口中,点击 Create New Project

创建一个新的应用项目

  1. Select a Project Template 窗口中,选择 Empty Activity,然后点击 Next

选择项目模板

  1. 在 Configure your project 窗口中,完成以下操作:

    • Name 字段中输入项目的名称。
    • Package name 字段中输入项目包名。
    • 如果您想将项目放在其他文件夹中,请更改其 Save 位置。
    • Language 下拉菜单中选择 JavaKotlin
    • Minimum SDK 字段中选择您的应用支持的最低 Android 版本。
    • 如果您的应用需要旧版库支持,请选中 Use legacy android.support libraries 复选框。
    • 其他选项保持原样。
  2. 点击 Finish

填写项目相关信息和配置

  1. 经过一段时间的处理后,Android Studio 创建的项目主窗口会出现。

项目工程主界面

# 项目结构

通过上面的步骤创建了一个新的Android 应用项目,下面花一点时间了解一下其中的重要的文件。

首先,确保已打开 Project 窗口(依次选择 View > Tool Windows > Project),并从该窗口顶部的下拉列表中选择 Android 视图。随后,您可以看到以下主要的项目结构:

项目结构

# 主要的结构

如果选择 Project 视图,您可以看到更多文件和目录。其中最重要的目录如下:

  • module-name/ 项目工程目录。

  • build/ 包含构建输出。

  • libs/ 包含专用库。

  • src/ 包含相应模块在以下子目录中的所有代码和资源文件:

    • androidTest/ 包含在 Android 设备上运行的插桩测试的代码。如需了解详情,请参阅 Android 测试文档 (opens new window)

    • main/ 包含“主”源代码集文件:所有构建变体共享的 Android 代码和资源(其他构建变体的文件位于同级目录中,例如“debug”构建类型的文件位于 src/debug/ 中)。

      • AndroidManifest.xml 描述应用及其各个组件的性质。如需了解详情,请参阅 AndroidManifest.xml (opens new window) 文档。

      • java/ 包含 Java 源代码。

      • jni/ 包含使用 Java 原生接口 (JNI) 的原生代码。如需了解详情,请参阅 Android NDK 文档 (opens new window)

      • res/ 包含应用资源,例如可绘制对象文件、布局文件和界面字符串。如需了解详情,请参阅应用资源 (opens new window)

      • assets/ 包含应按原样编译为 .apk 文件的文件。您可以使用 URI 按照与典型文件系统相同的方式导航此目录,并使用 AssetManager 以字节流的形式读取文件。例如,此目录非常适合存储纹理和游戏数据。

    • test/ 包含在主机 JVM 上运行的本地测试代码。

    • build.gradle(模块) 这定义了特定于模块的构建配置。

build.gradle(项目) 这定义了适用于所有模块的构建配置。该文件是项目不可或缺的一部分,因此您应该将其与所有其他源代码一起保留在修订版本控制系统中。 如需了解其他构建文件,请参阅配置构建 (opens new window)

# 重要的文件

  • app > src > main > java > xxx > MainActivity

    这是主 activity。它是应用的入口点。当您构建和运行应用时,系统会启动此 Activity 的实例并加载其布局。

  • app > src > main > res > layout > activity_main.xml

    此 XML 文件定义了 activity 界面 (UI) 的布局。它包含一个 TextView 元素,其中具有“Hello, World!”文本

  • app > src > main> AndroidManifest.xml

    清单文件 (opens new window)描述应用的基本特性并定义其每个组件。

  • build.gradle

    有两个使用此名称的文件:一个针对项目“Project”,另一个针对应用模块“Module: app”。每个模块均有自己的 build.gradle 文件,但此项目当前仅有一个模块。使用每个模块的 build.gradle 文件控制 Gradle 插件 (opens new window)构建应用的方式。如需详细了解此文件,请参阅配置 build (opens new window)

# 应用资源概览

资源是指代码使用的附加文件和静态内容,例如位图、布局定义、界面字符串、动画说明等。

您应始终外部化应用资源(例如图像和代码中的字符串),以便单独对其进行维护。此外,您还应为特定设备配置提供备用资源,方法是将其进行分组并放入专门命名的资源目录中。在运行时,Android 会根据当前配置使用合适的资源。例如,您可能需根据屏幕尺寸提供不同的界面布局,或根据语言设置提供不同的字符串。

外部化应用资源后,您便可使用在项目 R 类中生成的资源 ID 来访问这些资源。本文档会介绍如何对 Android 项目中的资源进行分组,如何为特定的设备配置提供备用资源,以及如何从您的应用代码或其他 XML 文件访问这些资源。

# 第三方库

app > libs 目录中存放需要使用的第三方的二进制库文件,如JAR,AAR等库文件。

# 业务代码

代码资源主要还是放在 app > main 目录下,目前常见有:

  • java 目录

    包含 Java 源代码文件(以软件包名称分隔各文件,包括 JUnit 测试代码)。

  • aidl 目录

    存放 AIDL 接口相关的文件。

  • jni/cpp 目录

    存放包含使用 Java 原生接口 (JNI) 的原生代码,Native 相关开发的C、C++的相关代码文件。

# res资源

包含所有非代码资源(例如 XML 布局、界面字符串和位图图像),这些资源划分到相应的子目录中。如需详细了解所有可能的资源类型,请参阅提供资源 (opens new window)

项目 res/ 支持的资源目录:

目录 资源类型
animator/ 用于定义属性动画 (opens new window)的 XML 文件。
anim/ 用于定义渐变动画 (opens new window)的 XML 文件。(属性动画也可保存在此目录中,但为了区分这两种类型,属性动画首选 animator/ 目录。)
color/ 用于定义颜色状态列表的 XML 文件。请参阅颜色状态列表资源 (opens new window)
drawable/ 位图文件(.png.9.png.jpg.gif)或编译为以下可绘制对象资源子类型的 XML 文件: 位图文件、九宫格(可调整大小的位图)、 状态列表 、形状、动画可绘制对象、其他可绘制对象请参阅: Drawable 资源 (opens new window)
mipmap/ 适用于不同启动器图标密度的可绘制对象文件。如需了解有关使用 mipmap/ 文件夹管理启动器图标的详细信息,请参阅管理项目概览 (opens new window)
layout/ 用于定义用户界面布局的 XML 文件。请参阅布局资源 (opens new window)
menu/ 用于定义应用菜单(如选项菜单、上下文菜单或子菜单)的 XML 文件。请参阅菜单资源 (opens new window)
raw/ 需以原始形式保存的任意文件。如要使用原始 InputStream 打开这些资源,请使用资源 ID(即 R.raw.filename)调用 Resources.openRawResource()

但是,如需访问原始文件名和文件层次结构,则可以考虑将某些资源保存在 assets/ 目录(而非 res/raw/)下。assets/ 中的文件没有资源 ID,因此您只能使用 AssetManager 读取这些文件。
values/ 包含字符串、整型数和颜色等简单值的 XML 文件。
其他 res/ 子目录中的 XML 资源文件会根据 XML 文件名定义单个资源,而 values/ 目录中的文件可描述多个资源。对于此目录中的文件,<resources> 元素的每个子元素均会定义一个资源。例如,<string> 元素会创建 R.string 资源,<color> 元素会创建 R.color 资源。

由于每个资源均使用自己的 XML 元素进行定义,因此您可以随意命名文件,并在某个文件中放入不同的资源类型。但是,您可能需要将独特的资源类型放在不同的文件中,使其一目了然。例如,对于可在此目录中创建的资源,下面给出了相应的文件名约定:
arrays.xml:资源数组(类型数组 (opens new window))。
colors.xml:颜色值 (opens new window)
dimens.xml:尺寸值 (opens new window)
strings.xml:字符串值 (opens new window)
styles.xml:样式 (opens new window)
请参阅字符串资源 (opens new window)样式资源 (opens new window)更多资源类型 (opens new window)
xml/ 可在运行时通过调用 Resources.getXML() 读取的任意 XML 文件。各种 XML 配置文件(如可搜索配置 (opens new window))都必须保存在此处。
font/ 带有扩展名的字体文件(如 .ttf.otf.ttc),或包含 <font-family> 元素的 XML 文件。如需详细了解作为资源的字体,请参阅 XML 中的字体 (opens new window)

提示

切勿将资源文件直接保存在 res/ 目录内,因为这样会造成编译错误。

# 访问应用资源

在应用中提供资源后,您可通过引用其资源 ID 来应用该资源。所有资源 ID 都在您项目的 R 类中进行定义,该类由 aapt 工具自动生成。

编译应用时,aapt 会生成 R 类,其中包含 res/ 目录中所有资源的资源 ID。每个资源类型都有对应的 R 子类(例如,R.drawable 对应所有可绘制对象资源),而该类型的每个资源都有对应的静态整型数(例如,R.drawable.icon)。该整型数就是可用来检索资源的资源 ID。

尽管资源 ID 是在 R 类中指定的,但您完全不需要在该类中查找资源 ID。资源 ID 始终由以下部分组成:

  • 资源类型:每个资源都被分到一个“类型”组中,例如 stringdrawablelayout。如需了解有关不同类型的详细信息,请参阅资源类型 (opens new window)
  • 资源名称,它是不包括扩展名的文件名;或是 XML android:name 属性中的值(如资源是字符串等简单值)。

访问资源的方法有两种:代码中使用xml文件中使用

# 在代码中访问资源

您可以以方法参数的形式传递资源 ID,进而在代码中使用资源。例如,您可以设置一个 ImageView,从而借助 setImageResource() 使用 res/drawable/myimage.png 资源:

ImageView imageView = (ImageView) findViewById(R.id.myimageview);
imageView.setImageResource(R.drawable.myimage);

您还可利用 Resources 中的方法检索个别资源,并且您可通过 getResources() 获得该资源的实例。

语法:

代码中引用资源的语法:

[<package_name>.]R.<resource_type>.<resource_name>

  • <package_name> 是资源所在包的名称(如果引用的资源来自您自己的资源包,则不需要)。
  • <resource_type> 是资源类型的 R 子类。
  • <resource_name> 是不带扩展名的资源文件名,或 XML 元素中的 android:name 属性值(若资源是简单值)。

如需了解有关各资源类型及其引用方法的详细信息,请参阅资源类型 (opens new window)

用例:

许多方法可接受资源 ID 参数,您可利用 Resources 中的方法检索资源。您可以通过 Context.getResources() 获得 Resources 的实例。

以下是一些在代码中访问资源的示例:

// Load a background for the current screen from a drawable resource
getWindow().setBackgroundDrawableResource(R.drawable.my_background_image) ;

// Set the Activity title by getting a string from the Resources object, because
//  this method requires a CharSequence rather than a resource ID
getWindow().setTitle(getResources().getText(R.string.main_title));

// Load a custom layout for the current screen
setContentView(R.layout.main_screen);

// Set a slide in animation by getting an Animation from the Resources object
flipper.setInAnimation(AnimationUtils.loadAnimation(this,
        R.anim.hyperspace_in));

// Set the text on a TextView object using a resource ID
TextView msgTextView = (TextView) findViewById(R.id.msg);
msgTextView.setText(R.string.hello_message);

注意:

切勿手动修改 R.java 文件 — 在您编译项目时,aapt 工具会生成该文件。下次编译时,所有更改都会被覆盖。

# 在 XML 中访问资源

您可以使用对现有资源的引用,为某些 XML 属性和元素定义值。创建布局文件时,为给您的微件提供字符串和图像,您会经常这样做。

例如,如果您为布局添加 Button,则应为按钮文本使用字符串资源 (opens new window)

<Button
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:text="@string/submit" />

语法:

XML 资源中引用资源的语法:

@[<package_name>:]<resource_type>/<resource_name>

  • <package_name> 是资源所在包的名称(如果引用的资源来自相同资源包,则不需要)
  • <resource_type> 是资源类型的 R 子类
  • <resource_name> 是不带扩展名的资源文件名,或 XML 元素中的 android:name 属性值(若资源是简单值)。

如需了解有关各资源类型及其引用方法的详细信息,请参阅资源类型 (opens new window)

用例:

在某些情况下,您必须使用资源作为 XML 中的值(例如,对微件应用可绘制图像),但您也可在 XML 中任何接受简单值的地方使用资源。例如,如果您拥有以下资源文件,其中包括一个颜色资源 (opens new window)和一个字符串资源 (opens new window)

<?xml version="1.0" encoding="utf-8"?>
<resources>
   <color name="opaque_red">#f00</color>
   <string name="hello">Hello!</string>
</resources>

在此情况下,您无需在资源引用中指定包名称,因为资源来自您自己的资源包。如要引用系统资源,则您需要加入包名称。

例如:

<?xml version="1.0" encoding="utf-8"?>
<EditText xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:textColor="@android:color/secondary_text_dark"
    android:text="@string/hello" />

请注意:您应始终使用字符串资源,以便将您的应用本地化为其他语言。如需了解有关创建备用资源(例如本地化字符串)的信息,请参阅提供备用资源 (opens new window)。如需查看将应用本地化为其他语言的完整指南,请参阅本地化 (opens new window)

您甚至可以在 XML 中使用资源创建别名。例如,您可以创建一个可绘制对象资源,将其作为另一个可绘制对象资源的别名:

<?xml version="1.0" encoding="utf-8"?>
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
    android:src="@drawable/other_drawable" />

这听起来多余,但对使用备用资源可能很有帮助。阅读更多关于创建别名资源 (opens new window)的内容。

# 引用样式属性

利用样式属性资源,您可以在当前应用的主题背景中引用某个属性的值。借助引用样式属性,在自定义界面元素的外观时,您无需采用提供硬编码值这种方式,您可以通过为其设置样式,以匹配当前主题背景提供的标准变体来达成目的。引用样式属性的实质作用是,“在当前主题背景中使用此属性定义的样式”。

如要引用样式属性,名称语法几乎与普通资源格式完全相同,区别在于您需将 at 符号 (@) 改为问号 (?),并且资源类型部分为可选项。

语法:

引用样式属性语法:

?[<package_name>:][<resource_type>/]<resource_name>

例如,以下代码展示了如何通过引用属性来设置文本颜色设,使其匹配系统主题背景的“基本”文本颜色:

<EditText id="text"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:textColor="?android:textColorSecondary"
    android:text="@string/hello_world" />

在以上代码中,android:textColor 属性指定当前主题背景中某个样式属性的名称。Android 现在会将应用于 android:textColorSecondary 样式属性的值用作此微件中 android:textColor 的值。由于系统资源工具知道此环境中肯定存在某个属性资源,因此您无需显式声明类型(类型应为 ?android:attr/textColorSecondary)— 您可以将 attr 类型排除在外。

# 访问原始文件

尽管并不常见,但您的确有可能需要访问原始文件和目录。如果确有需要,则将文件保存在 res/ 中并没有用,因为从 res/ 读取资源的唯一方法是使用资源 ID。您可以改为将资源保存在 assets/ 目录中。

保存在 assets/ 目录中的文件没有资源 ID,因此您无法通过 R 类或在 XML 资源中引用它们。您可以改为采用类似普通文件系统的方式查询 assets/ 目录中的文件,并利用 AssetManager 读取原始数据。

不过,如果您只需要读取原始数据(例如视频文件或音频文件)的能力,则可将文件保存在 res/raw/ 目录中,并利用 openRawResource() 读取字节流。

# 访问平台资源

Android 包含许多标准资源,例如样式、主题背景和布局。如要访问这些资源,请通过 android 包名称限定您的资源引用。例如,您可以将 Android 提供的布局资源用于 ListAdapter 中的列表项:

setListAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, myarray));

在上例中,simple_list_item_1 是平台为 ListView 中的项目所定义的布局资源。您可以使用该资源,而不必自行创建列表项布局。

# 利用资源提供最佳设备兼容性

为使应用支持多种设备配置,请务必为应用使用的每种资源类型提供默认资源,这一点非常重要。

例如,如果应用支持多种语言,请始终包含不带语言和区域限定符 (opens new window)values/ 目录(用于保存字符串)。相反,如果您将所有字符串文件放入带有语言和区域限定符的目录中,且设备语言已设为您的字符串所不支持的语言,则应用在该设备上运行时将崩溃。但是,只要提供默认 values/ 资源,应用便会正常运行(即使用户不理解该语言,但总比崩溃要好)。

同样,如果您根据屏幕方向提供不同的布局资源,则应选择一个方向作为默认方向。例如,不要在 layout-land/layout-port/ 中分别提供横向和纵向的布局资源,而是保留其一作为默认设置,例如:layout/ 用于横向,layout-port/ 用于纵向。

提供默认资源至关重要,这不仅是因为应用可能会在超出预期的配置上运行,也因为新版 Android 有时会添加旧版本不支持的配置限定符。如果您使用新的资源限定符,但希望维持对旧版 Android 的代码兼容性,则当旧版 Android 运行应用时,应用将在无默认资源的情况下崩溃,因为此时它无法使用以新限定符命名的资源。例如,如果您将 minSdkVersion (opens new window) 设置为 4,并使用夜间模式 (opens new window)nightnotnight,API 级别 8 中的新增配置)限定所有可绘制对象资源,则 API 级别 4 设备无法访问可绘制对象资源,而且会崩溃。在此情况下,您可能希望 notnight 成为默认资源,所以您应排除该限定符,使可绘制对象资源位于 drawable/drawable-night/ 中。

因此,为提供最佳的设备兼容性,请始终为应用正确运行所需的资源提供默认资源。然后,请使用配置限定符为特定的设备配置创建备用资源。