陈颂光
全栈工程师,能够独立开发从解释器到网站和桌面/移动端应用的各类软件。
关注我的 GitHub

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 传递到 startActivitystartActivityForResult(当您想让活动返回结果时)来启动活动(或为其安排新任务)。Intent 定义要执行的操作(例如,“查看”或“发送”某个内容),并且可以指定要执行操作的数据的 URI(以及正在启动的组件可能需要了解的信息)。
  • 您可以通过将 Intent 传递到 startService 来启动服务(或对执行中的服务下达新指令)。 或者,您也可以通过将 Intent 传递到 bindService 来绑定到该服务。Intent 定义要执行的操作(例如,“查看”或“发送”某个内容),并且可以指定要执行操作的数据的 URI(以及正在启动的组件可能需要了解的信息)。
  • 您可以通过将 Intent 传递到 sendBroadcastsendOrderedBroadcastsendStickyBroadcast 等方法来发起广播。Intent 只会定义要广播的通知(例如,指示设备电池电量不足的广播只包括指示“电池电量不足”的已知操作字符串)。
  • 您可以通过在 ContentResolver 上调用 query 来对内容提供程序执行查询。

标准意图

Android提供了一些标准的意图用于一些常见的任务:

操作 数据URI MIME类型 其它 要求权限 类别
ACTION_SET_ALARM EXTRA_HOUREXTRA_MINUTESEXTRA_MESSAGEEXTRA_DAYSArrayList)、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(从新纪年开始计算的毫秒数)、TITLEDESCRIPTIONEVENT_LOCATIONEXTRA_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_TYPECommonDataKinds.Email.CONTENT_TYPECommonDataKinds.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_MULTIPLEEXTRA_LOCAL_ONLY类别(可选)   CATEGORY_OPENABLE(只返回可通过 openFileDescriptor 以文件流形式表示的“可打开”文件)
ACTION_OPEN_DOCUMENT`、`ACTION_CREATE_DOCUMENT 与用户应选择的文件类型对应的 MIME 类型 EXTRA_MIME_TYPES(与您的应用请求的文件类型对应的 MIME 类型数组。 当您使用此 extra 时,必须在 setType 中将主 MIME 类型设置为 “/“)、EXTRA_ALLOW_MULTIPLEEXTRA_TITLEEXTRA_LOCAL_ONLY   CATEGORY_OPENABLE(只返回可通过 openFileDescriptor 以文件流形式表示的“可打开”文件)
ACTION_RESERVE_TAXI_RESERVATION    
ACTION_VIEW geo:latitude,longitudegeo:latitude,longitude?z=zoomgeo: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_TYPEEXTRA_MEDIA_ARTIST(必需)、"android.intent.extra.genre"QUERY(必需),Audio.Albums.ENTRY_CONTENT_TYPEEXTRA_MEDIA_ALBUMEXTRA_MEDIA_ARTIST"android.intent.extra.genre"QUERY(必需),"vnd.android.cursor.item/audio"EXTRA_MEDIA_ALBUMEXTRA_MEDIA_ARTIST"android.intent.extra.genre"EXTRA_MEDIA_TITLE(必需)、QUERY(必需),Audio.Playlists.ENTRY_CONTENT_TYPEEXTRA_MEDIA_ALBUMEXTRA_MEDIA_ARTIST"android.intent.extra.genre""android.intent.extra.playlist"EXTRA_MEDIA_TITLEQUERY(必需)    
ACTION_CREATE_NOTE "*/*" EXTRA_NAMEEXTRA_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_SENDTOACTION_SENDACTION_SEND_MULTIPLE sms:<phone_number>smsto:<phone_number>mms:<phone_number>mmsto:<phone_number> "text/plain""image/*""video/*" "subject""sms_body"EXTRA_STREAM(指向要附加的图像或视频的 UriArrayList<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.transformandroid.colorCorrection.gains 以及 TRANSFORM_MATRIXandroid.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”),不区分大小写。例如enfren-rUSfr-rFR
  • 布局方向。它适用于布局、图片或值等任何资源。
    • ldrtl 是指“布局方向从右到左”
    • ldltr 是指“布局方向从左到右”,默认的隐式值 (注:要为应用启用从右到左的布局功能,必须将 supportsRtl 设置为 “true”,并将 targetSdkVersion 设置为 17 或更高版本)。
  • 最小宽度,例如sw320dpsw480dpsw600dpsw720dp等等
  • 资源可用宽度,例如w720dpw1024dp等等
  • 资源可用高度,例如h720dph1024dp等等
  • 屏幕尺寸
    • 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、FWVGA
    • notlong:非宽屏,如 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 级别),如v3v4v7

注意配置可能在运行过程中发生变化,例如设备方向发生变化。这时默认会重启应用,但在状态太大(如有位图)或者难以精确恢复(如涉及网络连接)时可以尝试以下变通方法:

  • 扩展 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 子组件均为TextViewViewSwitcher
TextView 显示文本
TimePicker 显示日中的时间
ToggleButton 显示两个状态的按钮
Toolbar 工具条
VideoView 播放视频
ViewFlipper 在两组件切换时有动画效果
ViewSwitcher 显示两个子组件中一个
ZoomControls 显示一组用于缩放的组件

以下类对象虽然不在组件层次中,但仍可用于与用户交互:

用途
ListPopupWindow 显示一列表选项的弹窗
Toast 显示简短信息
PopupWindow 弹窗
PopupMenu 模态弹出菜单

存储

用户首选项

如果想保存简单类型的键值对,可以使用用户首选项:

  1. 调用活动的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)
    • 若要写入数据
      1. 调用 edit() 以获取SharedPreferences.Editor
      2. 调用
        • 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)
      3. 调用 commit() 提交新值

内部存储

可以直接在设备的内部存储中保存文件。默认情况下,保存到内部存储的文件是应用的私有文件,其他应用(和用户)不能访问这些文件。 当用户卸载应用时,这些文件也会被移除。

  • 要创建私有文件并写入到内部存储:
    1. 使用文件名称和操作模式调用ContextopenFileOutput(String name, int mode)方法,其中模式可以为MODE_PRIVATEMODE_APPENDMODE_WORLD_READABLEMODE_WORLD_WRITEABLE。 这将返回一个 FileOutputStream
    2. 使用 write() 写入到文件
    3. 使用 close() 关闭流式传输
  • 要从内部存储读取文件:
    1. 调用 openFileInput(String name),这将返回一个 FileInputStream
    2. 使用 read() 读取文件字节。
    3. 然后使用 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。

您可以使用 SQLiteDatabasequery() 方法来执行 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) 测量温度

使用传感器的常规流程为:

  1. 确定传感器,如可用((SensorManager) getSystemService(Context.SENSOR_SERVICE)).getSensorList(Sensor.TYPE_ALL)获取可用的Sensor列表,或者用getDefaultSensor(int type)方法取得默认传感器(没有则null)。
  2. 确定传感器的能力,如可调用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() `
  3. 注册回调方法,如调用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对象:

  1. Camera.open(int)方法取得Camera对象
  2. getParameters()方法取得设置信息
  3. 需要时用修改上一步对象并调用Camera对象的setParameters(Camera.Parameters)
  4. 调用setDisplayOrientation(int)
  5. 调用setPreviewDisplay(SurfaceHolder)以存放预览数据
  6. 调用startPreview()启动预览
    • 拍照时调用takePicture(Camera.ShutterCallback, Camera.PictureCallback, Camera.PictureCallback, Camera.PictureCallback)
    • 录像时
      1. 调用unlock()
      2. 调用MediaRecorder.setCamera(Camera)
      3. 调用reconnect()
  7. 如果需要继续拍照,回到第6步
  8. 调用stopPreview()停止预览
  9. 调用release()释放相机

网络

Android的网络API与Java类似,但要注意UI线程中是不能直接使用网络的,一般应把网络操作放到AsyncTask中。另外可以用类android.net.ConnectivityManager的方法了解网络连通性,可以用类NetworkInfo了解正在使用电话网络还是Wifi。

其它

功能 入口类
定位 android.location.LocationManager
蓝牙 android.bluetooth.BluetoothManager
USB设备 android.hardware.usb.UsbManager

下一步

如对细节有疑问,可以查阅API文档

关键词 java