Android开发概览
Android是目前在手机等移动设备上最流行的操作系统,其中的应用主要用Java语言写成,因此对于Java程序员,转型手机开发相对容易。
开发环境
强烈建议使用官方的Android Studio进行开发,支持包括Windows、Linux和Mac OS X在内的常见平台,它提供了基于IntelliJ的IDE,还包括Android模拟器等工具。安装Android Studio相当简单,在Windows下如常运行exe安装程序,在Linux下解压下运行bin/studio.sh
即可,详情参见安装说明,由于首次使用前会另外下载大量库和映像需要网络畅通。
许多劣质书籍和网上教程还在讲早已过时的Eclipse ADT插件,切勿上当
完成安装后打开Android Studio,后可以选择开发软件项目:
- 创建一个Android Studio项目
- 从文件系统打开一个Android Studio项目
- 从版本控制系统取得一个项目
- 调试或分析APK
- 导入其它项目
- 导入样例项目
在Android Studio中可以使用IntelliJ的常规IDE功能编辑代码,以下我们只谈Android特定的功能:
- 可以通过“新建”向导按模板生成一些常见类型的文件
- 可以通过“设计”视图可视化地编辑用户界面
- 可以通过“主题编辑器”可视化地编辑用户界面主题
- 在“运行”菜单中可以在模拟器中运行、测试、调试、分析应用,甚至可以热代码替换。
- 在“构建”菜单中可选择生成(签名的)APK(Android 软件包)。带有 .apk 后缀的存档文件 APK带有编译后代码连同任何数据和资源文件,作为应用的安装文件。
基本概念
组件
Android 应用由一些组件组成,有四种用于不同目的组件类型,它们有不同的创建和销毁方式(生命周期)。
- 活动表示具有用户界面的单一屏幕,作为
Activity
的子类实现。例如,电子邮件应用可能具有一个显示新电子邮件列表的活动、一个用于撰写电子邮件的活动以及一个用于阅读电子邮件的活动。 尽管这些活动通过协作在电子邮件应用中形成了一种紧密结合的用户体验,但每一个活动都独立于其他活动而存在。因此,其他应用可以启动其中任何一个活动(如果电子邮件应用允许)。 例如,相机应用可以启动电子邮件应用内用于撰写新电子邮件的活动,以便用户共享图片。 - 服务是一种在后台运行的组件,用于执行长时间运行的操作或为远程进程执行作业,作为
Service
的子类实现。服务不提供用户界面。 例如,当用户位于其他应用中时,服务可能在后台播放音乐或者通过网络获取数据,但不会阻断用户与活动的交互。诸如活动等其他组件可以启动服务,让其运行或与其绑定以便与其进行交互。 - 内容提供程序管理一组共享的应用数据,作为
ContentProvider
的子类实现。您可以将数据存储在文件系统、SQLite 数据库、网络上或您的应用可以访问的任何其他永久性存储位置。 其他应用可以通过内容提供程序查询数据,甚至修改数据(如果内容提供程序允许)。 例如,Android 系统可提供管理用户联系人信息的内容提供程序。 因此,任何具有适当权限的应用都可以查询内容提供程序的某一部分(如ContactsContract.Data
),以读取和写入有关特定人员的信息。内容提供程序也适用于读取和写入您的应用不共享的私有数据。 例如,记事本示例应用使用内容提供程序来保存笔记。 - 广播接收器是一种用于响应系统范围广播通知的组件,作为
BroadcastReceiver
的子类实现,其中覆盖了void onReceive(Context context, Intent intent)
方法。 许多广播都是由系统发起的,例如通知屏幕已关闭、电池电量不足或已拍摄照片的广播;应用也可以发起广播,例如通知其他应用某些数据已下载至设备,并且可供其使用。 尽管广播接收器不会显示用户界面,但它们可以创建状态栏通知,在发生广播事件时提醒用户。 但广播接收器更常见的用途只是作为通向其他组件的“通道”,设计用于执行极少量的工作。 例如,它可能会基于事件发起一项服务来执行某项工作。
意图
Android 系统设计的独特之处在于,任何应用都可以启动其他应用的组件。 例如,如果您想让用户使用设备的相机拍摄照片,很可能有另一个应用可以执行该操作,那么您的应用就可以利用该应用,而不是开发一个活动来自行拍摄照片。 您不需要集成甚至链接到该相机应用的代码,而是只需启动拍摄照片的相机应用中的活动。 完成拍摄时,系统甚至会将照片返回您的应用,以便您使用。对用户而言,就好像相机真正是您应用的组成部分。
当系统启动某个组件时,会启动该应用的进程(如果尚未运行),并实例化该组件所需的类。 例如,如果您的应用启动相机应用中拍摄照片的活动,则该活动会在属于相机应用的进程,而不是您的应用的进程中运行。因此,与大多数其他系统上的应用不同,Android 应用并没有单一入口点如经典的main
函数。
由于系统在单独的进程中运行每个应用,且其文件权限会限制对其他应用的访问,因此您的应用无法直接启动其他应用中的组件, 但 Android 系统却可以。因此,要想显式启动其他应用中的组件(活动、服务或广播接收器),您必须向系统传递一则异步消息,即说明您想启动特定组件的 Intent
对象,系统随后便会为您启动该组件。
每种类型的组件有不同的启动方法:
- 您可以通过将 Intent 传递到
startActivity
或startActivityForResult
(当您想让活动返回结果时)来启动活动(或为其安排新任务)。Intent 定义要执行的操作(例如,“查看”或“发送”某个内容),并且可以指定要执行操作的数据的 URI(以及正在启动的组件可能需要了解的信息)。 - 您可以通过将 Intent 传递到
startService
来启动服务(或对执行中的服务下达新指令)。 或者,您也可以通过将 Intent 传递到bindService
来绑定到该服务。Intent 定义要执行的操作(例如,“查看”或“发送”某个内容),并且可以指定要执行操作的数据的 URI(以及正在启动的组件可能需要了解的信息)。 - 您可以通过将 Intent 传递到
sendBroadcast
、sendOrderedBroadcast
或sendStickyBroadcast
等方法来发起广播。Intent 只会定义要广播的通知(例如,指示设备电池电量不足的广播只包括指示“电池电量不足”的已知操作字符串)。 - 您可以通过在
ContentResolver
上调用query
来对内容提供程序执行查询。
标准意图
Android提供了一些标准的意图用于一些常见的任务:
操作 | 数据URI | MIME类型 | 其它 | 要求权限 | 类别 |
---|---|---|---|---|---|
ACTION_SET_ALARM |
无 | 无 | EXTRA_HOUR 、EXTRA_MINUTES 、EXTRA_MESSAGE 、EXTRA_DAYS (ArrayList )、EXTRA_RINGTONE (URI)、EXTRA_VIBRATE (布尔)、EXTRA_SKIP_UI (布尔) |
com.android.alarm.permission.SET_ALARM |
|
ACTION_SET_TIMER |
无 | 无 | EXTRA_LENGTH (以秒为单位的定时器定时长度)、EXTRA_MESSAGE (用于标识定时器的自定义消息)、EXTRA_SKIP_UI (是否跳过确认 UI启动) |
com.android.alarm.permission.SET_ALARM |
|
ACTION_SHOW_ALARMS |
无 | 无 | |||
ACTION_INSERT |
Events.CONTENT_URI |
"vnd.android.cursor.dir/event" |
EXTRA_EVENT_ALL_DAY (是否为全天事件)、EXTRA_EVENT_BEGIN_TIME (从纪年开始计算的毫秒数)、EXTRA_EVENT_END_TIME (从新纪年开始计算的毫秒数)、TITLE 、DESCRIPTION 、EVENT_LOCATION 、EXTRA_EMAIL (以逗号分隔的受邀者电子邮件地址列表) |
||
ACTION_IMAGE_CAPTURE 或ACTION_VIDEO_CAPTURE |
无 | 无 | EXTRA_OUTPUT (保存到的 Uri 位置) |
||
INTENT_ACTION_STILL_IMAGE_CAMERA |
无 | 无 | 无 | ||
INTENT_ACTION_VIDEO_CAMERA |
无 | 无 | 无 | ||
ACTION_PICK |
无 | Contacts.CONTENT_TYPE |
|||
ACTION_PICK |
无 | CommonDataKinds.Phone.CONTENT_TYPE 、CommonDataKinds.Email.CONTENT_TYPE 、CommonDataKinds.StructuredPostal.CONTENT_TYPE 等等 |
|||
ACTION_VIEW |
content:<URI> |
无 | |||
ACTION_EDIT |
content:<URI> |
该类型是从联系人 URI 推断得出。 | ContactsContract.Intents.Insert 中定义的一个或多个 extra |
||
ACTION_INSERT |
无 | Contacts.CONTENT_TYPE |
ContactsContract.Intents.Insert 中定义的一个或多个 extra |
||
ACTION_SENDTO(适用于不带附件)、ACTION_SEND(适用于带一个附件)、ACTION_SEND_MULTIPLE(适用于带多个附件) |
无 | “text/plain”、”/” | Intent.EXTRA_EMAIL (包含所有“主送”收件人电子邮件地址的字符串数组)、Intent.EXTRA_CC (包含所有“抄送”收件人电子邮件地址的字符串数组)、Intent.EXTRA_BCC (包含所有“密件抄送”收件人电子邮件地址的字符串数组)、Intent.EXTRA_SUBJECT (包含电子邮件主题的字符串)、 Intent.EXTRA_TEXT (包含电子邮件正文的字符串)、Intent.EXTRA_STREAM (指向附件的 Uri或包含多个 Uri 对象的 ArrayList) |
||
ACTION_GET_CONTENT |
无 | 与用户应选择的文件类型对应的 MIME 类型 | EXTRA_ALLOW_MULTIPLE 、EXTRA_LOCAL_ONLY 类别(可选) |
CATEGORY_OPENABLE (只返回可通过 openFileDescriptor 以文件流形式表示的“可打开”文件) |
|
ACTION_OPEN_DOCUMENT`、`ACTION_CREATE_DOCUMENT |
无 | 与用户应选择的文件类型对应的 MIME 类型 | EXTRA_MIME_TYPES (与您的应用请求的文件类型对应的 MIME 类型数组。 当您使用此 extra 时,必须在 setType 中将主 MIME 类型设置为 “/“)、EXTRA_ALLOW_MULTIPLE 、EXTRA_TITLE 、EXTRA_LOCAL_ONLY |
CATEGORY_OPENABLE (只返回可通过 openFileDescriptor 以文件流形式表示的“可打开”文件) |
|
ACTION_RESERVE_TAXI_RESERVATION |
无 | 无 | 无 | ||
ACTION_VIEW |
geo:latitude,longitude 、geo:latitude,longitude?z=zoom 、geo:0,0?q=lat,lng(label) 、geo:0,0?q=my+street+address |
无 | |||
ACTION_VIEW |
file:<URI> 、content:<URI> 、http:<URL> |
"audio/*" 、"application/ogg" 、"application/x-ogg" 、"application/itunes" |
|||
INTENT_ACTION_MEDIA_PLAY_FROM_SEARCH |
无 | 无 | MediaStore.EXTRA_MEDIA_FOCUS (必需),"vnd.android.cursor.item/*" 时QUERY (必需),Audio.Genres.ENTRY_CONTENT_TYPE 时"android.intent.extra.genre" (必需)、QUERY (必需),Audio.Artists.ENTRY_CONTENT_TYPE 时EXTRA_MEDIA_ARTIST (必需)、"android.intent.extra.genre" 、QUERY (必需),Audio.Albums.ENTRY_CONTENT_TYPE 时EXTRA_MEDIA_ALBUM 、EXTRA_MEDIA_ARTIST 、"android.intent.extra.genre" 、QUERY (必需),"vnd.android.cursor.item/audio" 时EXTRA_MEDIA_ALBUM 、EXTRA_MEDIA_ARTIST 、"android.intent.extra.genre" 、EXTRA_MEDIA_TITLE (必需)、QUERY (必需),Audio.Playlists.ENTRY_CONTENT_TYPE 时EXTRA_MEDIA_ALBUM 、EXTRA_MEDIA_ARTIST 、"android.intent.extra.genre" 、"android.intent.extra.playlist" 、EXTRA_MEDIA_TITLE 、QUERY (必需) |
||
ACTION_CREATE_NOTE |
无 | "*/*" |
EXTRA_NAME 、EXTRA_TEXT |
||
ACTION_DIAL |
tel:<phone-number> 或voicemail:<phone-number> |
无 | |||
ACTION_CALL |
tel:<phone-number> 或voicemail:<phone-number> |
无 | android.permission.CALL_PHONE |
||
ACTION_WEB_SEARCH |
无 | 无 | SearchManager.QUERY | ||
ACTION_SETTINGS |
无 | 无 | 无 | ||
ACTION_WIRELESS_SETTINGS |
无 | 无 | 无 | ||
ACTION_AIRPLANE_MODE_SETTINGS |
无 | 无 | 无 | ||
ACTION_WIFI_SETTINGS |
无 | 无 | 无 | ||
ACTION_APN_SETTINGS |
无 | 无 | 无 | ||
ACTION_BLUETOOTH_SETTINGS |
无 | 无 | 无 | ||
ACTION_DATE_SETTINGS |
无 | 无 | 无 | ||
ACTION_LOCALE_SETTINGS |
无 | 无 | 无 | ||
ACTION_INPUT_METHOD_SETTINGS |
无 | 无 | 无 | ||
ACTION_DISPLAY_SETTINGS |
无 | 无 | 无 | ||
ACTION_SECURITY_SETTINGS |
无 | 无 | 无 | ||
ACTION_LOCATION_SOURCE_SETTINGS |
无 | 无 | 无 | ||
ACTION_INTERNAL_STORAGE_SETTINGS |
无 | 无 | 无 | ||
ACTION_MEMORY_CARD_SETTINGS |
无 | 无 | 无 | ||
ACTION_SENDTO 或 ACTION_SEND 或 ACTION_SEND_MULTIPLE |
sms:<phone_number> 、smsto:<phone_number> 、mms:<phone_number> 、mmsto:<phone_number> |
"text/plain" 、"image/*" 、"video/*" |
"subject" 、"sms_body" 、EXTRA_STREAM (指向要附加的图像或视频的 Uri 或ArrayList<Uri> ) |
||
ACTION_VIEW |
http:<URL> 或https:<URL> |
"text/plain" 、"text/html" 、"application/xhtml+xml" 、"application/vnd.wap.xhtml+xml" |
安全性
安装到设备后,每个 Android 应用都运行在自己的安全沙箱内:
- Android 操作系统是一种多用户 Linux 系统,其中的每个应用都是一个不同的用户;
- 默认情况下,系统会为每个应用分配一个唯一的 Linux 用户 ID(该 ID 仅由系统使用,应用并不知晓)。系统为应用中的所有文件设置权限,使得只有分配给该应用的用户 ID 才能访问这些文件;
- 每个进程都具有自己的虚拟机 (VM),因此应用代码是在与其他应用隔离的环境中运行;
- 默认情况下,每个应用都在其自己的 Linux 进程内运行。Android 会在需要执行任何应用组件时启动该进程,然后在不再需要该进程或系统必须为其他应用恢复内存时关闭该进程。
Android 系统可以通过这种方式实现最小权限原则。也就是说,默认情况下,每个应用都只能访问执行其工作所需的组件,而不能访问其他组件。 这样便营造出一个非常安全的环境,在这个环境中,应用无法访问系统中其未获得权限的部分。
不过,应用仍然可以通过一些途径与其他应用共享数据以及访问系统服务:
- 可以安排两个应用共享同一 Linux 用户 ID,在这种情况下,它们能够相互访问彼此的文件。 为了节省系统资源,可以安排具有相同用户 ID 的应用在同一 Linux 进程中运行,并共享同一 VM(应用还必须使用相同的证书签署)。
- 应用可以请求权限(如访问用户的联系人、短信、可装载存储装置 (SD 卡)、相机、蓝牙等),只有在用户明确授予这些权限时应用才能使用权限。
以下是一些正常权限:
ACCESS_LOCATION_EXTRA_COMMANDS
ACCESS_NETWORK_STATE
ACCESS_NOTIFICATION_POLICY
ACCESS_WIFI_STATE
BLUETOOTH
BLUETOOTH_ADMIN
BROADCAST_STICKY
CHANGE_NETWORK_STATE
CHANGE_WIFI_MULTICAST_STATE
CHANGE_WIFI_STATE
DISABLE_KEYGUARD
EXPAND_STATUS_BAR
GET_PACKAGE_SIZE
INSTALL_SHORTCUT
INTERNET
KILL_BACKGROUND_PROCESSES
MANAGE_OWN_CALLS
MODIFY_AUDIO_SETTINGS
NFC
READ_SYNC_SETTINGS
READ_SYNC_STATS
RECEIVE_BOOT_COMPLETED
REORDER_TASKS
REQUEST_COMPANION_RUN_IN_BACKGROUND
REQUEST_COMPANION_USE_DATA_IN_BACKGROUND
REQUEST_DELETE_PACKAGES
REQUEST_IGNORE_BATTERY_OPTIMIZATIONS
SET_ALARM
SET_WALLPAPER
SET_WALLPAPER_HINTS
TRANSMIT_IR
USE_FINGERPRINT
VIBRATE
WAKE_LOCK
WRITE_SYNC_SETTINGS
以下权限只容许与声明极限者由同一张证书签署的应用使用:
BIND_ACCESSIBILITY_SERVICE
BIND_AUTOFILL_SERVICE
BIND_CARRIER_SERVICES
BIND_CHOOSER_TARGET_SERVICE
BIND_CONDITION_PROVIDER_SERVICE
BIND_DEVICE_ADMIN
BIND_DREAM_SERVICE
BIND_INCALL_SERVICE
BIND_INPUT_METHOD
BIND_MIDI_DEVICE_SERVICE
BIND_NFC_SERVICE
BIND_NOTIFICATION_LISTENER_SERVICE
BIND_PRINT_SERVICE
BIND_SCREENING_SERVICE
BIND_TELECOM_CONNECTION_SERVICE
BIND_TEXT_SERVICE
BIND_TV_INPUT
BIND_VISUAL_VOICEMAIL_SERVICE
BIND_VOICE_INTERACTION
BIND_VPN_SERVICE
BIND_VR_LISTENER_SERVICE
BIND_WALLPAPER
CLEAR_APP_CACHE
MANAGE_DOCUMENTS
READ_VOICEMAIL
REQUEST_INSTALL_PACKAGES
SYSTEM_ALERT_WINDOW
WRITE_SETTINGS
WRITE_VOICEMAIL
以下是有明显隐私风险的权限组和其中的危险权限(对于Android 5.1(API级别22)或以下,又或targetSdkVersion
是22或以下,系统会在安装应用时向用户请求权限,否则在运行时才请求。):
CALENDAR
READ_CALENDAR
WRITE_CALENDAR
CAMERA
CAMERA
CONTACTS
READ_CONTACTS
WRITE_CONTACTS
GET_ACCOUNTS
LOCATION
ACCESS_FINE_LOCATION
ACCESS_COARSE_LOCATION
MICROPHONE
RECORD_AUDIO
PHONE
READ_PHONE_STATE
READ_PHONE_NUMBERS
CALL_PHONE
ANSWER_PHONE_CALLS
READ_CALL_LOG
WRITE_CALL_LOG
ADD_VOICEMAIL
USE_SIP
PROCESS_OUTGOING_CALLS
SENSORS
BODY_SENSORS
SMS
SEND_SMS
RECEIVE_SMS
READ_SMS
RECEIVE_WAP_PUSH
RECEIVE_MMS
STORAGE
READ_EXTERNAL_STORAGE
WRITE_EXTERNAL_STORAGE
可以通过ContextCompat.checkSelfPermission()
、ActivityCompat.requestPermissions()
方法检查和请求权限。
项目结构
对于新手,我们的建议是从观察样例项目或现有其它现有项目(不妨在GitHub之类的地方寻找你感兴趣的成功项目)开始,慢慢熟悉项目的典型结构后尝试作出修改。经验告诉我们,让新手从空白项目开始会带来极大的迷茫,完成不知道从哪下手,即使坚持下去也往往会偏离最佳实践。
项目文件夹组织如下:
模块名称/
:每个项目由若干个模块组成,默认模块名为app
build/
:包含构建输出libs/
:包含私有库src/
:包含模块的所有代码和资源文件main/
:包含产品的源文件AndroidManifest.xml
说明应用及其每个组件的属性java/
包含 Java 源代码jni/
包含使用 Java 原生接口 (JNI) 的原生代码gen/
包含 Android Studio 生成的 Java 文件,例如R.java
文件以及从 AIDL 文件创建的接口res/
包含应用资源,例如可绘制对象文件、布局文件和 UI 字符串资源类型-配置/
或资源类型/
包含特定类型的资源
assets/
包含应原封不动地编译到.apk
文件中的文件,可以使用 URI 像浏览典型文件系统一样浏览此目录,以及使用 AssetManager 以字节流形式读取文件。例如,此位置非常适合纹理和游戏数据。
test/
:包含在开发主机JVM上运行的测试代码androidTest/
:包含在Android设备上运行的测试代码
build.gradle
:定义模块特定的构建配置
build.gradle
:定义适用于所有模块的构建配置
项目清单
清单文件AndroidManifest.xml
文件向 Android 系统提供应用的必要信息,系统必须知道这些信息才可运行应用:
- 软件包名称充当应用的唯一标识符
- 描述应用的各个组件,包括构成应用的活动、服务、广播接收器和内容提供程序。它还为实现每个组件的类命名并发布其功能,例如它们可以处理的 Intent 消息。这些声明向 Android 系统告知有关组件以及可以启动这些组件的条件的信息
- 确定托管应用组件的进程
- 列出
Instrumentation
类,这些类可在应用运行时提供分析和其他信息。这些声明只会在应用处于开发阶段时出现在清单中,在应用发布之前将移除 - 确定应用需要的用户权限,如互联网访问权限或对用户联系人的读取权限
- 根据应用使用的 API,声明应用所需的最低 API 级别
- 声明应用使用或需要的硬件和软件功能,如相机、蓝牙服务或多点触摸屏幕
- 应用需要链接的 API 库(Android 框架 API 除外),如 Google 地图库
<?xml version="1.0" encoding="utf-8"?>
<!-- 根元素。可选属性:
android:sharedUserId="string"
android:sharedUserLabel="string resource"
android:versionCode="integer"
android:versionName="string"
android:installLocation=["auto" | "internalOnly" | "preferExternal"]
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="包名">
<!-- 应用正确运行所必须获取的权限。可选属性:
android:maxSdkVersion="integer"
-->
<uses-permission android:name="权限名" />
<!-- 应用提供的权限。可选属性:
android:description="string resource"
android:icon="drawable resource"
android:label="string resource"
android:name="string"
android:permissionGroup="string"
android:protectionLevel=["normal" | "dangerous" | "signature" | ...]
-->
<permission />
<!-- 应用声明的权限树基名。可选属性:
android:icon="drawable resource"
android:label="string resource" ]
android:name="string"
-->
<permission-tree />
<!-- 应用声明的权限组。可选属性:
android:description="string resource"
android:icon="drawable resource"
android:label="string resource"
android:name="string"
-->
<permission-group />
<!-- 应用应用与系统交互的Instrumentation类。可选属性:
android:functionalTest=["true" | "false"]
android:handleProfiling=["true" | "false"]
android:icon="drawable resource"
android:label="string resource"
android:name="string"
android:targetPackage="string"
android:targetProcesses="string"
-->
<instrumentation />
<!-- 应用的API级别兼容性。可选属性:
android:minSdkVersion="integer"
android:targetSdkVersion="integer"
android:maxSdkVersion="integer"
-->
<uses-sdk />
<!-- 应用要求的配置。可选属性:
android:reqFiveWayNav=["true" | "false"]
android:reqHardKeyboard=["true" | "false"]
android:reqKeyboardType=["undefined" | "nokeys" | "qwerty" | "twelvekey"]
android:reqNavigation=["undefined" | "nonav" | "dpad" | "trackball" | "wheel"]
android:reqTouchScreen=["undefined" | "notouch" | "stylus" | "finger"]
-->
<uses-configuration />
<!-- 应用可能使用的功能。可选属性:
android:name="string"
android:required=["true" | "false"]
android:glEsVersion="integer"
-->
<uses-feature />
<!-- 应用适用的屏幕大小。可选属性:
android:resizeable=["true"| "false"]
android:smallScreens=["true" | "false"]
android:normalScreens=["true" | "false"]
android:largeScreens=["true" | "false"]
android:xlargeScreens=["true" | "false"]
android:anyDensity=["true" | "false"]
android:requiresSmallestWidthDp="integer"
android:compatibleWidthLimitDp="integer"
android:largestWidthLimitDp="integer"
-->
<supports-screens />
<!-- 应用适用的屏幕配置。必要属性:
android:screenSize=["small" | "normal" | "large" | "xlarge"]
android:screenDensity=["ldpi" | "mdpi" | "hdpi" | "xhdpi"
| "280" | "360" | "420" | "480" | "560" ]
-->
<compatible-screens />
<!-- 应用支持的GL纹理压缩格式。可选属性:
android:name="string"
-->
<supports-gl-texture />
<!-- 应用属性,必须有且是menifest最后一个元素。可选属性:
android:allowTaskReparenting=["true" | "false"]
android:allowBackup=["true" | "false"]
android:allowClearUserData=["true" | "false"]
android:backupAgent="string"
android:backupInForeground=["true" | "false"]
android:banner="drawable resource"
android:debuggable=["true" | "false"]
android:description="string resource"
android:directBootAware=["true" | "false"]
android:enabled=["true" | "false"]
android:extractNativeLibs=["true" | "false"]
android:fullBackupContent="string"
android:fullBackupOnly=["true" | "false"]
android:hasCode=["true" | "false"]
android:hardwareAccelerated=["true" | "false"]
android:icon="drawable resource"
android:isGame=["true" | "false"]
android:killAfterRestore=["true" | "false"]
android:largeHeap=["true" | "false"]
android:label="string resource"
android:logo="drawable resource"
android:manageSpaceActivity="string"
android:name="string"
android:networkSecurityConfig="xml resource"
android:permission="string"
android:persistent=["true" | "false"]
android:process="string"
android:restoreAnyVersion=["true" | "false"]
android:requiredAccountType="string"
android:resizeableActivity=["true" | "false"]
android:restrictedAccountType="string"
android:supportsRtl=["true" | "false"]
android:taskAffinity="string"
android:testOnly=["true" | "false"]
android:theme="resource or theme"
android:uiOptions=["none" | "splitActionBarWhenNarrow"]
android:usesCleartextTraffic=["true" | "false"]
android:vmSafeMode=["true" | "false"]
-->
<application>
<!-- 声明可运行的Activity类。可选属性:
android:allowEmbedded=["true" | "false"]
android:allowTaskReparenting=["true" | "false"]
android:alwaysRetainTaskState=["true" | "false"]
android:autoRemoveFromRecents=["true" | "false"]
android:banner="drawable resource"
android:clearTaskOnLaunch=["true" | "false"]
android:configChanges=["mcc", "mnc", "locale",
"touchscreen", "keyboard", "keyboardHidden",
"navigation", "screenLayout", "fontScale",
"uiMode", "orientation", "screenSize",
"smallestScreenSize"]
android:documentLaunchMode=["intoExisting" | "always" |
"none" | "never"]
android:enabled=["true" | "false"]
android:excludeFromRecents=["true" | "false"]
android:exported=["true" | "false"]
android:finishOnTaskLaunch=["true" | "false"]
android:hardwareAccelerated=["true" | "false"]
android:icon="drawable resource"
android:label="string resource"
android:launchMode=["standard" | "singleTop" |
"singleTask" | "singleInstance"]
android:maxRecents="integer"
android:multiprocess=["true" | "false"]
android:name="string"
android:noHistory=["true" | "false"]
android:parentActivityName="string"
android:permission="string"
android:process="string"
android:relinquishTaskIdentity=["true" | "false"]
android:resizeableActivity=["true" | "false"]
android:screenOrientation=["unspecified" | "behind" |
"landscape" | "portrait" |
"reverseLandscape" | "reversePortrait" |
"sensorLandscape" | "sensorPortrait" |
"userLandscape" | "userPortrait" |
"sensor" | "fullSensor" | "nosensor" |
"user" | "fullUser" | "locked"]
android:stateNotNeeded=["true" | "false"]
android:supportsPictureInPicture=["true" | "false"]
android:taskAffinity="string"
android:theme="resource or theme"
android:uiOptions=["none" | "splitActionBarWhenNarrow"]
android:windowSoftInputMode=["stateUnspecified",
"stateUnchanged", "stateHidden",
"stateAlwaysHidden", "stateVisible",
"stateAlwaysVisible", "adjustUnspecifi ed",
"adjustResize", "adjustPan"]
-->
<activity>
<!-- 声明活动可响应的意图。可选属性:
android:icon="drawable resource"
android:label="string resource"
android:priority="integer"
-->
<intent-filter>
<!-- 可响应的动作。可选属性:
android:name="string"
-->
<action />
<!-- 响应的类别。可选属性:
android:name="string"
-->
<category />
<!-- 可响应的数据。可选属性:
android:scheme="string"
android:host="string"
android:port="string"
android:path="string"
android:pathPattern="string"
android:pathPrefix="string"
android:mimeType="string"
-->
<data />
</intent-filter>
<!-- 声明元数据。可选属性:
android:name="string"
android:resource="resource specification"
android:value="string"
-->
<meta-data />
</activity>
<!-- 声明活动别名。可选属性:
android:enabled=["true" | "false"]
android:exported=["true" | "false"]
android:icon="drawable resource"
android:label="string resource"
android:name="string"
android:permission="string"
android:targetActivity="string"
-->
<activity-alias>
<intent-filter> . . . </intent-filter>
<meta-data />
</activity-alias>
<!-- 声明可运行的Service类。可选属性:
android:description="string resource"
android:directBootAware=["true" | "false"]
android:enabled=["true" | "false"]
android:exported=["true" | "false"]
android:icon="drawable resource"
android:isolatedProcess=["true" | "false"]
android:label="string resource"
android:name="string"
android:permission="string"
android:process="string"
-->
<service>
<intent-filter> . . . </intent-filter>
<meta-data/>
</service>
<!-- 声明BroadcastReceiver类。可选属性:
android:directBootAware=["true" | "false"]
android:enabled=["true" | "false"]
android:exported=["true" | "false"]
android:icon="drawable resource"
android:label="string resource"
android:name="string"
android:permission="string"
android:process="string"
-->
<receiver>
<intent-filter> . . . </intent-filter>
<meta-data />
</receiver>
<!-- 声明ContentProvider类。可选属性:
android:authorities="list"
android:directBootAware=["true" | "false"]
android:enabled=["true" | "false"]
android:exported=["true" | "false"]
android:grantUriPermissions=["true" | "false"]
android:icon="drawable resource"
android:initOrder="integer"
android:label="string resource"
android:multiprocess=["true" | "false"]
android:name="string"
android:permission="string"
android:process="string"
android:readPermission="string"
android:syncable=["true" | "false"]
android:writePermission="string"
-->
<provider>
<!-- 本内容提供器有权访问的应用数据子集。可选属性:
android:path="string"
android:pathPattern="string"
android:pathPrefix="string"
-->
<grant-uri-permission />
<meta-data />
<!-- 访问应用数据子集所需权限。可选属性:
android:path="string"
android:pathPrefix="string"
android:pathPattern="string"
android:permission="string"
android:readPermission="string"
android:writePermission="string"
-->
<path-permission />
</provider>
<!-- 需要链接的共享库(android.*包不用)。可选属性:
android:name="string"
android:required=["true" | "false"]
-->
<uses-library />
</application>
</manifest>
由于一些属性值如描述和图标应当本地化,有关属性可以用@包:类型/名称
或@类型/名称
引用资源。类似地,可以用?包:类型/名称
或?类型/名称
引用主题。另外,字符串属性可以用双反斜杠转义字符,例如用\\n
表示换行符或使用\\uxxxx
表示 Unicode 字符。
以下是部分功能列表:
名称 | 功能 |
---|---|
android.hardware.audio.low_latency |
应用使用设备的低延迟时间音频管道,该管道可以减少处理声音输入或输出时的滞后和延迟。 |
android.hardware.audio.output |
应用使用设备的音响设备、音频耳机插孔、蓝牙流式传输能力或类似机制传输声音。 |
android.hardware.audio.pro |
应用使用设备的高端音频功能和性能能力。 |
android.hardware.microphone |
应用使用设备的麦克风记录音频。 |
android.hardware.bluetooth |
应用使用设备的蓝牙功能,通常是为了与其他支持蓝牙的设备通信。 |
android.hardware.bluetooth_le |
应用使用设备的低功耗蓝牙无线电功能。 |
android.hardware.camera |
应用使用设备的后置相机。只有前置相机的设备不会列出该功能,因此如果您的应用可与任何朝向的相机通信,请改用 android.hardware.camera.any 功能。 |
android.hardware.camera.any |
应用使用设备的其中一个相机或用户为设备连接的外置相机。 如果您的应用不要求相机必须是后置式,请使用此值来替代 android.hardware.camera 。 |
android.hardware.camera.autofocus |
应用使用设备相机支持的自动对焦功能。应用通过使用该功能暗示其还使用 android.hardware.camera 功能,除非这个父功能在声明时使用了 android:required="false" 。 |
android.hardware.camera.capability.manual_post_processing |
应用使用设备相机支持的 MANUAL_POST_PROCESSING 功能。您的应用可以通过该功能替换相机的自动白平衡功能。 使用 android.colorCorrection.transform 、android.colorCorrection.gains 以及 TRANSFORM_MATRIX 的 android.colorCorrection.mode 。 |
android.hardware.camera.capability.manual_sensor |
应用使用设备相机支持的 MANUAL_SENSOR 功能。该功能隐含对自动曝光锁定(android.control.aeLock) 的支持,该支持可以让相机的曝光时间和灵敏度一直固定在特定值。 |
android.hardware.camera.capability.raw |
应用使用设备相机支持的 RAW 功能。该功能暗示设备可以保存 DNG(原始)文件,并且设备的相机提供您的应用直接处理这些原始图像所需的 DNG 相关元数据。 |
android.hardware.camera.external |
应用与用户为设备连接的外置相机通信。 但该功能不能保证外置相机可供您的应用使用。 |
android.hardware.camera.flash |
应用使用设备相机支持的闪光功能。应用通过使用该功能暗示其还使用 android.hardware.camera 功能,除非这个父功能在声明时使用了 android:required="false" 。 |
android.hardware.camera.front |
应用使用设备的前置相机。应用通过使用该功能暗示其还使用 android.hardware.camera 功能,除非这个父功能在声明时使用了 android:required="false" 。 |
android.hardware.camera.level.full |
应用使用设备的至少一个相机提供的 FULL 级图像捕捉支持。 提供 FULL 支持的相机可提供快速捕捉功能、逐帧控制和手动后期处理控制。 |
android.hardware.type.automotive |
应用设计为在车辆内的一组屏幕上显示其 UI。 用户使用硬按钮、触摸、旋转控制器以及类鼠标界面与应用进行交互。 车辆的屏幕通常出现在车辆的中控台或仪表板中。 这些屏幕的尺寸和分辨率通常有限。切记,由于用户是在驾车时使用这类应用 UI,应用必须尽量不要让驾驶员分心。 |
android.hardware.type.watch |
应用设计为在手表上显示其 UI。手表佩戴在身体(例如手腕)上。 用户在很近的距离与设备互动。 |
android.hardware.fingerprint |
应用使用设备的生物识别硬件读取指纹。 |
android.hardware.gamepad |
应用捕获来自设备本身或其连接的手柄的游戏控制器输入。 |
android.hardware.consumerir |
应用使用设备的红外线 (IR) 功能,通常是为了与其他消费 IR 设备通信。 |
android.hardware.location |
应用使用设备上的一项或多项功能来确定位置,例如 GPS 位置、网络位置或基站位置。 |
android.hardware.location.gps |
应用使用从设备上的全球定位系统 (GPS) 接收器获得的精确位置坐标。应用通过使用该功能暗示其还使用 android.hardware.location 功能,除非这个父功能在声明时使用了属性 android:required="false" 。 |
android.hardware.location.network |
应用使用从设备上支持的基于网络的地理定位系统获得的粗略位置坐标。应用通过使用该功能暗示其还使用 android.hardware.location 功能,除非这个父功能在声明时使用了属性 android:required="false" 。 |
android.hardware.nfc |
应用使用设备的近距离无线通信 (NFC) 功能。 |
android.hardware.opengles.aep |
应用使用设备上安装的 OpenGL ES Android 扩展包。 |
android.hardware.sensor.accelerometer |
应用使用从设备的加速计读取的运动信息来检测设备的当前方向。 例如,应用可以使用加速计读数来确定何时在纵向与横向方向之间切换。 |
android.hardware.sensor.ambient_temperature |
应用使用设备的外界(环境)温度传感器。例如,天气应用可以报告室内或室外温度。 |
android.hardware.sensor.barometer |
应用使用设备的气压计。例如,天气应用可以报告气压。 |
android.hardware.sensor.compass |
应用使用设备的磁力计(罗盘)。例如,导航应用可以用户当前面朝的方向。 |
android.hardware.sensor.gyroscope |
应用使用设备的陀螺仪来检测旋转和倾斜,从而形成一个六轴方向系统。 通过使用该传感器,应用可以更顺利地检测其是否需要在纵向与横向方向之间切换。 |
android.hardware.sensor.hifi_sensors |
应用使用设备的高保真 (Hi-Fi) 传感器。例如,游戏应用可以检测用户的高精度移动。 |
android.hardware.sensor.heartrate |
应用使用设备的心率监测器。例如,健身应用可以报告用户心率随时间的变化趋势。 |
android.hardware.sensor.heartrate.ecg |
应用使用设备的超声波心动图 (ECG) 心率传感器。例如,健身应用可以报告有关用户心率的更详细信息。 |
android.hardware.sensor.light |
应用使用设备的光传感器。例如,应用可以根据环境光照条件显示两种不同配色方案中的一种。 |
android.hardware.sensor.proximity |
应用使用设备的近程传感器。例如,电话应用可以在其检测到用户握持的设备贴近身体时关闭设备的屏幕。 |
android.hardware.sensor.relative_humidity |
应用使用设备的相对湿度传感器。例如,天气应用可以利用湿度来计算和报告当前露点。 |
android.hardware.sensor.stepcounter |
应用使用设备的计步器。例如,健身应用可以报告用户需要走多少步才能达到每天的计步目标。 |
android.hardware.sensor.stepdetector |
应用使用设备的步测器。例如,健身应用可以利用每步的间隔时间来推测用户正在进行的锻炼类型。 |
android.hardware.screen.landscape |
|
android.hardware.screen.portrait |
应用要求设备使用纵向或横向方向。 如果您的应用同时支持这两种方向,则无需声明任一功能。默认情况下假定两种方向均非要求的方向,这样您的应用就可以安装在支持一种或同时支持两种方向的设备上。 不过,如果应用的任何活动利用android:screenOrientation 属性请求在特定方向下运行,则此声明意味着您的应用要求该方向。 例如,如果您使用"landscape" 、"reverseLandscape" 或 "sensorLandscape" 声明 android:screenOrientation ,则您的应用将只能安装在支持横向方向的设备上。最佳做法是,您仍应使用 <uses-feature> 元素来声明对该方向的要求。 如果您使用 android:screenOrientation 为活动声明了某个方向,但实际并无此要求,可通过使用 <uses-feature> 元素并加入 android:required="false" 声明该方向来停用这一要求。为实现后向兼容性,任何运行 Android 3.1(API 级别 12)或更低版本的设备都同时支持横向和纵向方向。 |
android.hardware.telephony |
应用使用设备的电话功能,例如提供数据通信服务的无线电话。 |
android.hardware.telephony.cdma |
应用使用码分多址接入 (CDMA) 无线电话系统。应用通过使用该功能暗示其还使用 android.hardware.telephony 功能,除非这个父功能在声明时使用了 android:required="false" 。 |
android.hardware.telephony.gsm |
应用使用全球移动通信系统 (GSM) 无线电话系统。应用通过使用该功能暗示其还使用 android.hardware.telephony 功能,除非这个父功能在声明时使用了 android:required="false" 。 |
android.hardware.faketouch |
应用使用基本的触摸交互事件,例如点按和拖动。声明为必需时,此功能表示应用只兼容模拟触摸屏(“假触摸”界面)或实际具有触摸屏的设备。带有假触摸界面的设备为用户提供模拟部分触摸屏功能的用户输入系统。 例如,鼠标或遥控器可以驱动屏幕光标。 如果您的应用需要基本的点击式交互(换言之,它在只使用方向键控制器的情况下无法正常工作),则应声明该功能。 由于这是最低水平的触摸交互,因此您还可以在提供更复杂触摸界面的设备上使用声明该功能的应用。注:默认情况下应用需要 android.hardware.touchscreen 。 如果您希望自己的应用可供提供假触摸界面的设备使用,则必须显式声明不要求提供触摸屏。 |
android.hardware.faketouch.multitouch.distinct |
应用在假触摸界面上区分两个或更多个“手指”的触摸轨迹。 这是android.hardware.faketouch 功能的一个超集。 声明为必需时,此功能表示应用只兼容模拟区分两个或更多个手指的触摸轨迹或实际具有触摸屏的设备。不同于 android.hardware.touchscreen.multitouch.distinct 所定义的区分式多点触摸,通过假触摸界面支持区分式多点触摸的输入设备并不支持所有双指手势,因为输入会转换成屏幕上的光标移动。 也就是说,在此类设备上的单指手势移动光标,双指划动触发单指触摸事件,而其他双指手势则触发相应的双指触摸事件。提供双指触摸触控板进行光标移动的设备可支持该功能。 |
android.hardware.faketouch.multitouch.jazzhand |
应用在假触摸界面上区分五个或更多个“手指”的触摸轨迹。 这是 android.hardware.faketouch 功能的一个超集。 声明为必需时,此功能表示应用只兼容模拟区分五个或更多个手指的触摸轨迹或实际具有触摸屏的设备。不同于 android.hardware.touchscreen.multitouch.jazzhand 所定义的区分式多点触摸,通过假触摸界面支持单手多点触摸的输入设备并不支持所有五指手势,因为输入会转换成屏幕上的光标移动。 也就是说,在此类设备上的单指手势移动光标,多指手势触发单指触摸事件,而其他多指手势则触发相应的多指触摸事件。提供五指触摸触控板进行光标移动的设备可支持该功能。 |
android.hardware.touchscreen |
应用利用设备的触摸屏功能来实现比基本触摸事件交互性更强的手势,例如滑屏。 这是 android.hardware.faketouch 功能的一个超集。默认情况下,您的应用需要该功能。因此,您的应用不可供默认情况下只提供模拟触摸界面(“假触摸”)的设备使用。 如果您希望自己的应用可供提供假触摸界面的设备(甚至只提供方向键控制器的设备)使用,则必须通过在声明 android.hardware.touchscreen 时加入 android:required="false" 来显式声明不要求提供触摸屏。 如果您的应用使用(但并不需要)真触摸屏界面,则应添加此声明。如果您的应用确实需要触摸界面(以便执行滑屏之类的更高级触摸手势),则您无需声明任何触摸界面功能,因为它们在默认情况下是必需功能。 不过,最好还是显式声明您的应用使用的所有功能。如果您需要进行更复杂的触摸交互(例如多指手势),则应声明您的应用使用高级触摸屏功能。 |
android.hardware.touchscreen.multitouch |
应用使用设备的基本两点式多点触摸功能(例如实现张合手势的功能),但应用不需要独立追踪触摸轨迹。 这是 android.hardware.touchscreen 功能的一个超集。应用通过使用该功能暗示其还使用 android.hardware.touchscreen 功能,除非这个父功能在声明时使用了 android:required="false" 。 |
android.hardware.touchscreen.multitouch.distinct |
应用使用设备的高级多点触摸功能来独立追踪两点或更多点的轨迹。 该功能是android.hardware.touchscreen.multitouch 功能的一个超集。应用通过使用该功能暗示其还使用 android.hardware.touchscreen.multitouch 功能,除非这个父功能在声明时使用了 android:required="false" 。 |
android.hardware.touchscreen.multitouch.jazzhand |
应用使用设备的高级多点触摸功能来独立追踪五点或更多点的轨迹。 该功能是android.hardware.touchscreen.multitouch 功能的一个超集。应用通过使用该功能暗示其还使用 android.hardware.touchscreen.multitouch 功能,除非这个父功能在声明时使用了 android:required="false" 。 |
android.hardware.usb.accessory |
应用表现为 USB 设备,与 USB 主机相连。 |
android.hardware.usb.host |
应用使用与设备相连的 USB 附件。设备充当 USB 主机。 |
android.hardware.wifi |
应用使用设备上的 802.11 网络 (Wi-Fi) 功能。 |
android.hardware.wifi.direct |
应用使用设备上的 Wi-Fi Direct 网络功能。 |
android.software.sip |
应用使用会话发起协议 (SIP) 服务。通过使用 SIP,应用可以支持互联网电话操作,例如视频会议和即时消息传递。 |
android.software.sip.voip |
应用使用基于 SIP 的互联网语音协议 (VoIP) 服务。通过使用 VoIP,应用可以支持实时互联网电话操作,例如双向视频会议。应用通过使用该功能暗示其还使用 android.software.sip 功能,除非这个父功能在声明时使用了 android:required="false" 。 |
android.software.webview |
应用显示来自互联网的内容。 |
android.software.input_methods |
应用使用新的输入法,该输入法由开发者在 InputMethodService 中定义。 |
android.software.backup |
应用加入处理备份和恢复操作的逻辑。 |
android.software.device_admin |
应用通过设备管理员来强制执行设备规范。 |
android.software.managed_users |
应用支持二级用户和托管配置文件。 |
android.software.securely_removes_users |
应用可永久性移除用户及其相关数据。 |
android.software.verified_boot |
应用加入处理设备验证启动功能结果的逻辑,该逻辑可检测设备的配置在重新启动操作期间是否发生了变化。 |
android.software.midi |
应用利用乐器数字化接口 (MIDI) 协议连接到乐器或输出声音。 |
android.software.print |
应用加入打印设备上所显示文档的命令。 |
android.software.leanback |
应用呈现专为在大屏幕(例如电视)上观看而设计的 UI。 |
android.software.live_tv |
应用流式传输直播电视节目。 |
android.software.app_widgets |
应用使用或提供应用小部件,并且只应安装在带有可供用户嵌入应用小部件的主屏幕或类似位置的设备上。 |
android.software.home_screen |
应用起到替代设备主屏幕的作用。 |
android.software.live_wallpaper |
应用使用或提供包含动画的壁纸。 |
资源
为了适应本地化和设置大小等配置,很多值不宜在代码中写死,而应该分离到资源中,这样就可以在运行时自动选用最合适的版本。代码引用资源的方法有:
- Java代码中可用
R.资源类型.资源名
引用,其中R
类在构建时自动生成 - XML中可用
@资源类型/资源名
引用
资源类型
常见的资源类型有:
animator
:用于定义属性动画的 XML 文件。anim
:定义渐变动画的 XML 文件。color
用于定义颜色状态列表的 XML 文件。请参阅颜色状态列表资源drawable
用于位图文件(.png、.9.png、.jpg、.gif)或编译为以下可绘制对象资源子类型(位图文件、九宫格(可调整大小的位图)、状态列表、形状、动画可绘制对象、其他可绘制对象)的 XML 文件mipmap
适用于不同启动器图标密度的可绘制对象文件。layout
用于定义用户界面布局的 XML 文件。menu
用于定义应用菜单(如选项菜单、上下文菜单或子菜单)的 XML 文件。请参阅菜单资源。raw
要以原始形式保存的任意文件。要使用原始InputStream
打开这些资源,请使用资源 ID(即R.raw.filename
)调用Resources.openRawResource()
。但是,如需访问原始文件名和文件层次结构,则可以考虑将某些资源保存在 assets/ 目录下(而不是 res/raw/)。assets/ 中的文件没有资源 ID,因此您只能使用AssetManager
读取这些文件。xml
可以在运行时通过调用Resources.getXML()
读取的任意 XML 文件。各种 XML 配置文件(如可搜索配置)都必须保存在此处。
一个例外是values
目录,其中文件名如资源类型.xml
的XML 文件可以包含多个字符串、整型数和颜色等简单值作为资源,名称由name
属性给出:
- arrays.xml
,用于资源数组(类型化数组)。
- colors.xml
:颜色值。
- dimens.xml
:尺寸值。
- strings.xml
:字符串值。
- styles.xml
:样式。
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="string_name">text_string</string>
<string-array name="string_array_name">
<item>text_string</item>
</string-array>
<plurals
name="plural_name">
<item
quantity=["zero" | "one" | "two" | "few" | "many" | "other"]
>text_string</item>
</plurals>
<bool name="bool_name">[true | false]</bool>
<color name="color_name">hex_color</color>
<!-- 单位可用dp(密度无关,160dpi时与1px同)、sp(密度无关且随字体大小变化)、pt(1/72寸)、px(像素)、mm(毫米)、in(寸) -->
<dimen name="dimension_name">dimension</dimen>
<item type="id" name="id_name" />
<integer name="integer_name">integer</integer>
<integer-array
name="integer_array_name">
<item>integer</item>
</integer-array>
<array name="integer_array_name">
<item>resource</item>
</array>
<style
name="style_name"
parent="@[package:]style/style_to_inherit">
<item
name="[package:]style_property_name"
>style_value</item>
</style>
</resources>
以下简单介绍部分常见类型对象的XML表示:
<?xml version="1.0" encoding="utf-8"?>
<ViewGroup
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@[+][package:]id/resource_name"
android:layout_height=["dimension" | "match_parent" | "wrap_content"]
android:layout_width=["dimension" | "match_parent" | "wrap_content"]
[ViewGroup-specific attributes] >
<View
android:id="@[+][package:]id/resource_name"
android:layout_height=["dimension" | "match_parent" | "wrap_content"]
android:layout_width=["dimension" | "match_parent" | "wrap_content"]
[View-specific attributes] >
<requestFocus/>
</View>
<ViewGroup >
<View />
</ViewGroup>
<include layout="@layout/layout_resource"/>
</ViewGroup>
<?xml version="1.0" encoding="utf-8"?>
<font-family>
<font
android:font="@[package:]font/font_to_include"
android:fontStyle=["normal" | "italic"]
android:fontWeight="weight_value" />
</font-family>
<?xml version="1.0" encoding="utf-8"?>
<font-family
android:fontProviderAuthority="authority"
android:fontProviderPackage="package"
android:fontProviderQuery="query"
android:fontProviderCerts="@[package:]array/array_resource" />
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
<!-- 颜色可用以下形式:#RGB、#ARGB、#RRGGBB、#AARRGGBB -->
<item
android:color="hex_color"
android:state_pressed=["true" | "false"]
android:state_focused=["true" | "false"]
android:state_selected=["true" | "false"]
android:state_checkable=["true" | "false"]
android:state_checked=["true" | "false"]
android:state_enabled=["true" | "false"]
android:state_window_focused=["true" | "false"] />
</selector>
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@[+][package:]id/resource_name"
android:title="string"
android:titleCondensed="string"
android:icon="@[package:]drawable/drawable_resource_name"
android:onClick="method name"
android:showAsAction=["ifRoom" | "never" | "withText" | "always" | "collapseActionView"]
android:actionLayout="@[package:]layout/layout_resource_name"
android:actionViewClass="class name"
android:actionProviderClass="class name"
android:alphabeticShortcut="string"
android:alphabeticModifiers=["META" | "CTRL" | "ALT" | "SHIFT" | "SYM" | "FUNCTION"]
android:numericShortcut="string"
android:numericModifiers=["META" | "CTRL" | "ALT" | "SHIFT" | "SYM" | "FUNCTION"]
android:checkable=["true" | "false"]
android:visible=["true" | "false"]
android:enabled=["true" | "false"]
android:menuCategory=["container" | "system" | "secondary" | "alternative"]
android:orderInCategory="integer" />
<group android:id="@[+][package:]id/resource name"
android:checkableBehavior=["none" | "all" | "single"]
android:visible=["true" | "false"]
android:enabled=["true" | "false"]
android:menuCategory=["container" | "system" | "secondary" | "alternative"]
android:orderInCategory="integer" >
<item />
</group>
<item >
<menu>
<item />
</menu>
</item>
</menu>
<?xml version="1.0" encoding="utf-8"?>
<bitmap
xmlns:android="http://schemas.android.com/apk/res/android"
android:src="@[package:]drawable/drawable_resource"
android:antialias=["true" | "false"]
android:dither=["true" | "false"]
android:filter=["true" | "false"]
android:gravity=["top" | "bottom" | "left" | "right" | "center_vertical" |
"fill_vertical" | "center_horizontal" | "fill_horizontal" |
"center" | "fill" | "clip_vertical" | "clip_horizontal"]
android:mipMap=["true" | "false"]
android:tileMode=["disabled" | "clamp" | "repeat" | "mirror"] />
配置
可指定配置以便使用最合适资源,多个配置可以用-
分隔表示交:
- 移动国家代码和设备 SIM 卡中的移动网络代码,例如
mcc310
是指美国的任一运营商,mcc310-mnc004
是指美国的 Verizon 公司,mcc208-mnc00
是指法国的 Orange 公司。 - 语言和区域,其中语言用两个字母组成的 ISO 639-1 语言代码,可以选择后跟两个字母组成的 ISO 3166-1-alpha-2 区域码(前带小写字母“r”),不区分大小写。例如
en
、fr
、en-rUS
、fr-rFR
。 - 布局方向。它适用于布局、图片或值等任何资源。
ldrtl
是指“布局方向从右到左”ldltr
是指“布局方向从左到右”,默认的隐式值 (注:要为应用启用从右到左的布局功能,必须将 supportsRtl 设置为 “true”,并将 targetSdkVersion 设置为 17 或更高版本)。
- 最小宽度,例如
sw320dp
、sw480dp
、sw600dp
、sw720dp
等等 - 资源可用宽度,例如
w720dp
、w1024dp
等等 - 资源可用高度,例如
h720dp
、h1024dp
等等 - 屏幕尺寸
small
:尺寸类似于低密度 QVGA 屏幕的屏幕。小屏幕的最小布局尺寸约为 320x426 dp 单位。例如,QVGA 低密度屏幕和 VGA 高密度屏幕。normal
:尺寸类似于中等密度 HVGA 屏幕的屏幕。标准屏幕的最小布局尺寸约为 320x470 dp 单位。例如,WQVGA 低密度屏幕、HVGA 中等密度屏幕、WVGA 高密度屏幕。large
:尺寸类似于中等密度 VGA 屏幕的屏幕。 大屏幕的最小布局尺寸约为 480x640 dp 单位。 例如,VGA 和 WVGA 中等密度屏幕。xlarge
:明显大于传统中等密度 HVGA 屏幕的屏幕。超大屏幕的最小布局尺寸约为 720x960 dp 单位。在大多数情况下,屏幕超大的设备体积过大,不能放进口袋,最常见的是平板式设备。 API 级别 9 中的新增配置。
- 屏幕纵横比
long
:宽屏,如 WQVGA、WVGA、FWVGAnotlong
:非宽屏,如 QVGA、HVGA 和 VGA
- 圆形屏幕
round
:圆形屏幕,例如圆形可穿戴式设备notround
:方形屏幕,例如手机或平板电脑
- 屏幕方向
port
:设备处于纵向(垂直)land
:设备处于横向(水平)
- UI 模式
car
:设备正在车载手机座上显示desk
:设备正在桌面手机座上显示television
:设备正在电视上显示,为用户提供“十英尺”体验,其 UI 位于远离用户的大屏幕上,主要面向方向键或其他非指针式交互appliance
:设备用作不带显示屏的装置watch
:设备配有显示屏,戴在手腕上
- 夜间模式
night
:夜间notnight
:白天
- 屏幕像素密度,前六个主要密度之间的缩放比为 3:4:6:8:12:16
ldpi
:低密度屏幕;约为 120dpi。mdpi
:中等密度(传统 HVGA)屏幕;约为 160dpi。hdpi
:高密度屏幕;约为 240dpi。xhdpi
:超高密度屏幕;约为 320dpi。此项为 API 级别 8 中新增配置xxhdpi
:超超高密度屏幕;约为 480dpi。此项为 API 级别 16 中新增配置xxxhdpi
:超超超高密度屏幕使用(仅限启动器图标,请参阅“支持多种屏幕”中的注释);约为 640dpi。 此项为 API 级别 18 中新增配置nodpi
:它可用于您不希望缩放以匹配设备密度的位图资源。tvdpi
:密度介于 mdpi 和 hdpi 之间的屏幕;约为 213dpi。它并不是“主要”密度组, 主要用于电视,而大多数应用都不需要它。对于大多数应用而言,提供 mdpi 和 hdpi 资源便已足够,系统将根据需要对其进行缩放。anydpi
:此限定符适合所有屏幕密度,其优先级高于其他限定符。 这对于矢量可绘制对象很有用。
- 触摸屏类型
notouch
:设备没有触摸屏。finger
:设备有一个专供用户通过手指直接与其交互的触摸屏。
- 键盘可用性
keysexposed
:设备具有可用的键盘。如果设备启用了软键盘(不无可能),那么即使硬键盘没有展示给用户,哪怕设备没有硬键盘,也可以使用此限定符。 如果没有提供或已经禁用软键盘,则只有在显示硬键盘时才会使用此限定符。keyshidden
:设备具有可用的硬键盘,但它处于隐藏状态,且设备没有启用软键盘。keyssoft
:设备已经启用软键盘(无论是否可见)。
- 主要文本输入法
nokeys
:设备没有用于文本输入的硬按键。qwerty
:设备具有标准硬键盘(无论是否对用户可见)。12key
:设备具有 12 键硬键盘(无论是否对用户可见)。
- 导航键可用性
navexposed
:导航键可供用户使用。navhidden
:导航键不可用(例如,位于密封盖子后面)。
- 主要非触摸导航方法
nonav
:除了使用触摸屏以外,设备没有其他导航设施。dpad
:设备具有用于导航的方向键。trackball
:设备具有用于导航的轨迹球。wheel
:设备具有用于导航的方向盘(不常见)。
- 平台版本(API 级别),如
v3
、v4
、v7
。
注意配置可能在运行过程中发生变化,例如设备方向发生变化。这时默认会重启应用,但在状态太大(如有位图)或者难以精确恢复(如涉及网络连接)时可以尝试以下变通方法:
- 扩展
Fragment
类并声明对有状态对象的引用,在创建片段后调用setRetainInstance(true)
并把它加到活动,最后在重启活动后使用FragmentManager
检索片段。 - 在清单中活动的
android:configChanges
属性加入要监控的配置变更,这样在变更时就不会重启而是调用onConfigurationChanged
,于是可以手动处理变更。
活动
生命周期
活动的生命周期中不同阶段会调用Activity
的不同回调方法:
onCreate()
在首次创建活动时调用。 您应该在此方法中执行所有正常的静态设置 — 创建视图、将数据绑定到列表等等。 系统向此方法传递一个Bundle
对象,其中包含活动的上一状态,不过前提是捕获了该状态(请参阅后文的保存活动状态)。始终后接onStart()
。onRestart()
在活动已停止并即将再次启动前调用。始终后接onStart()
。onStart()
在活动即将对用户可见之前调用。如果活动转入前台,则后接onResume()
,如果活动转入隐藏状态,则后接onStop()
。onResume()
在活动即将开始与用户进行交互之前调用。 此时,活动处于活动堆栈的顶层,并具有用户输入焦点。始终后接onPause()
。onPause()
当系统即将开始继续另一个活动时调用。 此方法通常用于确认对持久性数据的未保存更改、停止动画以及其他可能消耗 CPU 的内容,诸如此类。 它应该非常迅速地执行所需操作,因为它返回后,下一个活动才能继续执行。如果活动返回前台,则后接onResume()
,如果活动转入对用户不可见状态,则后接onStop()
。另外,进程可能被杀死而不再调用其它方法。onStop()
在活动对用户不再可见时调用。如果活动被销毁,或另一个 活动(一个现有活动或新活动)继续执行并将其覆盖,就可能发生这种情况。如果活动恢复与用户的交互,则后接onRestart()
,如果活动被销毁,则后接onDestroy()
。另外,进程可能被杀死而不再调用其它方法。onDestroy()
在活动被销毁前调用。这是活动将收到的最后调用。 当活动结束(有人对活动调用了finish()
),或系统为节省空间而暂时销毁该活动实例时,可能会调用它。 您可以通过isFinishing()
方法区分这两种情形。
在一些情况如内存不足或屏幕方向改变等配置变更,会引致应用重启,从而导致活动的字段值丢失。为了保存UI状态(如表单内容),可覆盖 onSaveInstanceState()
方法以便把状态保存到Bundle
,并覆盖onRestoreInstanceState()
方法以便恢复状态,注意应当调用默认实现以便处理UI状态。
用户界面
android的用户界面组件继承View
类,其中继承ViewGroup
的组件预期用作放置其它组件的容器。
通常来说活动在onCreate
方法中设置活动用户界面的根组件:
- 通过调用
setContentView(视图);
可以把根组件设置为指定View
对象。构造View
对象的方法与通常用Java编写Swing/AWT或Java FX界面类似 - 通过调用
setContentView(R.layout.布局名);
可以把根组件设置为指定XML文件指定的组件。XML文件中组件用元素表示(元素名为组件类名),组件的字段用属性表示,通常用Android Studio的设计视图自动生成。
以下列出一些其它常用的View
子类:
组件 | 用途 |
---|---|
ActionMenuView |
一串菜单选项 |
AutoCompleteTextView |
能显示自动补全的可编辑文本视图 |
Button |
用户能按以触发操作的按钮 |
CalendarView |
用于显示和选取日期的组件 |
CheckBox |
有两个状态的按钮 |
CheckedTextView |
可选中的文本视图 |
Chronometer |
计时器 |
DatePicker |
用于显示日期的组件 |
EditText |
让用户编辑文本的组件 |
ExpandableListView |
垂直滚动的双层列表 |
FrameLayout |
堆叠地布局子组件 |
GridLayout |
成网格地布局子组件 |
GridView |
在滚动网格中显示子组件 |
HorizontalScrollView |
滚动容器 |
ImageButton |
显示图片的按钮 |
ImageSwitcher |
在两个ImageView 间切换 |
ImageView |
显示图像 |
LinearLayout |
水平或垂直地布局子组件 |
ListView |
显示一组垂直可滚动的组件 |
MediaController |
MediaPlayer 的控制条 |
MultiAutoCompleteTextView |
能显示子串自动补全的可编辑文本视图 |
NumberPicker |
可在指定范围选取数值的组件 |
ProgressBar |
进度条 |
QuickContactBadge |
快速联系按钮 |
RadioButton |
两状态按钮 |
RatingBar |
用星形显示评价 |
RelativeLayout |
按给定的子组件间关系与子组件和容器的关系来布局子组件 |
ScrollView |
可滚动容器 |
SearchView |
搜索框 |
SeekBar |
进度条加上可拖动的指标 |
Space |
用于空白 |
Spinner |
让用户选取多个选项之一,同时只显示一个 |
StackView |
显示多个子组件之一 |
Switch |
两状态开关 |
TabHost |
标签页容器 |
TableLayout |
子组件布局为秆和列 |
TableRow |
水平地布局水组件 |
TabWidget |
显示一组标签 |
TextClock |
以字符串格式显示当前日期和或时间 |
TextSwitcher |
子组件均为TextView 的ViewSwitcher |
TextView |
显示文本 |
TimePicker |
显示日中的时间 |
ToggleButton |
显示两个状态的按钮 |
Toolbar |
工具条 |
VideoView |
播放视频 |
ViewFlipper |
在两组件切换时有动画效果 |
ViewSwitcher |
显示两个子组件中一个 |
ZoomControls |
显示一组用于缩放的组件 |
以下类对象虽然不在组件层次中,但仍可用于与用户交互:
类 | 用途 |
---|---|
ListPopupWindow |
显示一列表选项的弹窗 |
Toast |
显示简短信息 |
PopupWindow |
弹窗 |
PopupMenu |
模态弹出菜单 |
存储
用户首选项
如果想保存简单类型的键值对,可以使用用户首选项:
- 调用活动的
getPreferences(int mode)
取得SharedPreferences
对象 -
- 若需要读取数据,调用:
abstract Map<String, ?> getAll()
abstract boolean getBoolean(String key, boolean defValue)
abstract float getFloat(String key, float defValue)
abstract int getInt(String key, int defValue)
abstract long getLong(String key, long defValue)
abstract String getString(String key, String defValue)
abstract Set<String> getStringSet(String key, Set<String> defValues)
- 若要写入数据
- 调用
edit()
以获取SharedPreferences.Editor
- 调用
putBoolean(String key, boolean value)
putFloat(String key, float value)
putInt(String key, int value)
putLong(String key, long value)
putString(String key, String value)
putStringSet(String key, Set<String> values)
remove(String key)
- 调用
commit()
提交新值
- 调用
- 若需要读取数据,调用:
内部存储
可以直接在设备的内部存储中保存文件。默认情况下,保存到内部存储的文件是应用的私有文件,其他应用(和用户)不能访问这些文件。 当用户卸载应用时,这些文件也会被移除。
- 要创建私有文件并写入到内部存储:
- 使用文件名称和操作模式调用
Context
的openFileOutput(String name, int mode)
方法,其中模式可以为MODE_PRIVATE
、MODE_APPEND
、MODE_WORLD_READABLE
或MODE_WORLD_WRITEABLE
。 这将返回一个FileOutputStream
。 - 使用
write()
写入到文件 - 使用
close()
关闭流式传输
- 使用文件名称和操作模式调用
- 要从内部存储读取文件:
- 调用
openFileInput(String name)
,这将返回一个FileInputStream
。 - 使用
read()
读取文件字节。 - 然后使用
close()
关闭流式传输。
- 调用
- 要获取在其中存储内部文件的文件系统目录的绝对路径,可以调用
getFilesDir()
- 要在您的内部存储空间内创建(或打开现有的)目录,可以调用
getDir()
- 要删除保存在内部存储的文件,可以调用
deleteFile()
- 要返回您的应用当前保存的一系列文件,可以调用
fileList()
如果您想要缓存一些数据,而不是永久存储这些数据,应该使用 getCacheDir()
来打开一个 File
,它表示您的应用应该将临时缓存文件保存到的内部目录。当设备的内部存储空间不足时,Android 可能会删除这些缓存文件以回收空间。 但您不应该依赖系统来为您清理这些文件, 而应该始终自行维护缓存文件,使其占用的空间保持在合理的限制范围内(例如 1 MB)。 当用户卸载您的应用时,这些文件也会被移除。
使用数据库
Android 提供了对 SQLite 数据库的完全支持。应用中的任何类(不包括应用外部的类)均可按名称访问您所创建的任何数据库。
创建新 SQLite 数据库的推荐方法是创建 SQLiteOpenHelper
的子类并覆盖 onCreate()
方法,在此方法中,您可以执行 SQLite 命令以创建数据库中的表。 例如:
public class DictionaryOpenHelper extends SQLiteOpenHelper {
private static final int DATABASE_VERSION = 2;
private static final String DICTIONARY_TABLE_NAME = "dictionary";
private static final String DICTIONARY_TABLE_CREATE =
"CREATE TABLE " + DICTIONARY_TABLE_NAME + " (" +
KEY_WORD + " TEXT, " +
KEY_DEFINITION + " TEXT);";
DictionaryOpenHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(DICTIONARY_TABLE_CREATE);
}
}
然后您可以使用已定义的构造函数获取 SQLiteOpenHelper
实现的实例。 要从数据库执行写入和读取操作,请分别调用 getWritableDatabase()
和 getReadableDatabase()
。二者都会返回一个表示数据库的 SQLiteDatabase
对象,并提供用于 SQLite 操作的方法。
Android 没有实施标准 SQLite 概念之外的任何限制。我们推荐包含一个可用作唯一 ID 的自动增量值关键字段,以便快速查找记录。 私有数据不要求这样做,但如果您实现了一个内容提供程序,则必须包含使用 BaseColumns._ID
常量的唯一 ID。
您可以使用 SQLiteDatabase
的 query()
方法来执行 SQLite 查询,这些方法可接受各种查询参数,例如要查询的表、投影、选择、列、分组和其他参数。 对于复杂的查询,例如需要列别名的查询,应该使用 SQLiteQueryBuilder
,它将提供多种便捷的方法来构建查询。
每个 SQLite 查询都会返回一个指向该查询找到的所有行的 Cursor
。 您始终可以使用 Cursor
机制来浏览数据库查询结果,以及读取行和列。
设备
传感器
Android设备可能具备不同的传感器,例如:
设备 | 实现 | 说明 | 用途 |
---|---|---|---|
TYPE_ACCELEROMETER |
硬件 | 三个方向的加速度(ms-2) | 检测运动(如摇摆、倾斜) |
TYPE_AMBIENT_TEMPERATURE |
硬件 | 外界温度 (°C) | 检测气温 |
TYPE_GRAVITY |
软件/硬件 | 三个方向的重力加速度(ms-2) | 检测运动(如摇摆、倾斜) |
TYPE_GYROSCOPE |
硬件 | 三个方向的转动速度(rad/s) | 三个方向的旋转 |
TYPE_LIGHT |
硬件 | 外界照度(lx) | 控制屏幕亮度 |
TYPE_LINEAR_ACCELERATION |
软件/硬件 | 三个方向的除去重力后加速度(ms-2) | 监察沿单一方向的加速度 |
TYPE_MAGNETIC_FIELD |
硬件 | 三个方向的外界磁场(μT) | 指南针 |
TYPE_ORIENTATION |
软件 | 设备沿三个方向的旋转角度 | 决定设备位置 |
TYPE_PRESSURE |
硬件 | 外界气压(hPa或mbar) | 测量气压变化 |
TYPE_PROXIMITY |
硬件 | 屏幕与对象间的近似距离(cm) | 测量电话与人耳的距离 |
TYPE_RELATIVE_HUMIDITY |
硬件 | 相对湿度 (%) | 测量湿度 |
TYPE_ROTATION_VECTOR |
软件/硬件 | 设备的旋转向量 | 运动和旋转检测 |
TYPE_TEMPERATURE |
硬件 | 设备温度(°C) | 测量温度 |
使用传感器的常规流程为:
- 确定传感器,如可用
((SensorManager) getSystemService(Context.SENSOR_SERVICE)).getSensorList(Sensor.TYPE_ALL)
获取可用的Sensor
列表,或者用getDefaultSensor(int type)
方法取得默认传感器(没有则null
)。 - 确定传感器的能力,如可调用
Sensor
对象的方法:int getFifoMaxEventCount()
-
int getFifoReservedEventCount()-
int getHighestDirectReportRateLevel()-
int getId()-
int getMaxDelay()-
float getMaximumRange()-
int getMinDelay()-
String getName()-
float getPower()-
int getReportingMode()-
float getResolution()-
String getStringType()-
int getType()-
String getVendor()-
int getVersion()-
boolean isAdditionalInfoSupported()-
boolean isDirectChannelTypeSupported(int sharedMemType)-
boolean isDynamicSensor()-
boolean isWakeUpSensor() `
- 注册回调方法,如调用
SensorManager
对象的方法boolean registerListener(SensorEventListener listener, Sensor sensor, int samplingPeriodUs, int maxReportLatencyUs, Handler handler)
(注意后三个参数是可选的),其中SensorEventListener
中声明了以下方法:void onAccuracyChanged(Sensor sensor, int accuracy)
在精度发生变化时被调用void onSensorChanged(SensorEvent event)
在读取到新测量值时被调用,其中SensorEvent
对象中的字段包含了测量信息:int accuracy
提供精度Sensor sensor
提供传感器long timestamp
提供时间戵float[] values
提供了测量值,例如对于有三个方向(分别为设备默认方向下向右,向上和指出屏幕)分量的测量,数组的三个值分别为三个分量
相机
对于常规的拍摄,通常建议使用Intent启动系统意图而非直接控制相机。但如果确有需要,可以直接操作android.hardware.Camera
对象:
- 用
Camera.open(int)
方法取得Camera
对象 - 用
getParameters()
方法取得设置信息 - 需要时用修改上一步对象并调用
Camera
对象的setParameters(Camera.Parameters)
- 调用
setDisplayOrientation(int)
- 调用
setPreviewDisplay(SurfaceHolder)
以存放预览数据 - 调用
startPreview()
启动预览 -
- 拍照时调用
takePicture(Camera.ShutterCallback, Camera.PictureCallback, Camera.PictureCallback, Camera.PictureCallback)
- 录像时
- 调用
unlock()
- 调用
MediaRecorder.setCamera(Camera)
- 调用
reconnect()
- 调用
- 拍照时调用
- 如果需要继续拍照,回到第6步
- 调用
stopPreview()
停止预览 - 调用
release()
释放相机
网络
Android的网络API与Java类似,但要注意UI线程中是不能直接使用网络的,一般应把网络操作放到AsyncTask
中。另外可以用类android.net.ConnectivityManager
的方法了解网络连通性,可以用类NetworkInfo
了解正在使用电话网络还是Wifi。
其它
功能 | 入口类 |
---|---|
定位 | android.location.LocationManager |
蓝牙 | android.bluetooth.BluetoothManager |
USB设备 | android.hardware.usb.UsbManager |
下一步
如对细节有疑问,可以查阅API文档。