每个android应用都处于各自的安全沙盒中,并受以下android安全功能的保护:
- Android操作系统是一种多用户linux系统,其中的每个应用都是一个不同的用户。
- 默认情况下,系统会为每个应用分配一个唯一的linux用户id(该id仅由系统使用,应用并不知晓)。系统会为应用中的所有文件设置权限,使得只有分配给该应用的用户id才能访问这些文件。
- 每个进程都拥有自己的虚拟机(VM),因此应用代码独立于其他应用而运行。
- 默认情况下,每个应用都在其自己的linux进程内运行。Android系统会在需要执行任何应用组件时启动该进程,然后当不再需要该进程或系统必须为其他应用恢复内存时,其便会关闭该进程。
Android系统实现了最小权限原则。换言之,默认情况下,每个应用只能访问执行其工作所需的组件,而不能访问其他组件。这样便能创建非常安全的环境,在此环境中,应用无法访问其未获得权限的系统部分。不过,应用仍可通过一些途径与其他应用共享数据以及访问系统服务:
- 可以安排两个应用共享同一linux用户id,在此情况下,二者便能访问彼此的文件。为节省系统资源,也可安排拥有相同用户id的应用在同一linux进程中运行,并共享同一VM。应用还必须使用相同的证书进行签名。
- 应用可以请求访问设备数据(如用户的联系人、短信消息、可装载存储装置(SD卡)、相机、蓝牙等)的权限。用户必须明确授予这些权限。
共有四种不同的应用组件类型:
Activity
是所有android应用程序的门面,凡是在应用中看得到的东西,都是放在Activity
中的。Service
会在后台默默地运行,即使用户退出了应用,Service
仍然是可以继续运行的。BroadcastReceiver
允许应用接收来自各处的广播消息,比如电话、短信等,当然,应用也可以向外发出广播消息。ContentProvider
则为应用程序之间共享数据提供了可能,比如想要读取系统通讯录中的联系人,就需要通过ContentProvider
来实现。
在四种组件类型中,有三种(
Activity
、服务和广播接收器)均通过异步消息Intent
进行启动。Intent
会在运行时对各个组件进行互相绑定。可以将Intent
视为从其他组件(无论该组件是属于自己的应用还是其他应用)请求操作的信使。
需要使用Intent
对象来创建Intent
,该对象通过定义消息来启动特定的组件(显式Intent
)或特定的组件类型(隐式Intent
)。
对于
Activity
和服务,Intent
会定义要执行的操作(例如,查看或发送某内容),并且可指定待操作数据的URI,以及正在启动的组件可能需要了解的信息。例如,Intent
可能会传达对Activity
的请求,以便显示图像或打开网页。在某些情况下,可以通过启动Activity
来接收结果,这样Activity
还会返回Intent
中的结果。例如,可以发出一个Intent
,让用户选取某位联系人并将其返回。返回的Intent
包含指向所选联系人的URI。
对于广播接收器,Intent
只会定义待广播的通知。例如,指示设备电池电量不足的广播只包含一个已知的指示电池电量不足的操作字符串。
与Activity
、服务和广播接收器不同,内容提供程序并非由Intent
启动。相反,它们会在成为ContentResolver
的请求目标时启动。内容解析程序会通过内容提供程序处理所有直接事务,因此通过提供程序执行事务的组件便无需执行事务,而是改为在ContentResolver
对象上调用方法。这会在内容提供程序与请求信息的组件之间留出一个抽象层,以确保安全。
每种组件都有不同的启动方法:
- 如要启动
Activity
,可以向startActivity()
或startActivityForResult()
(想让Activity
返回结果时)传递Intent
,或者为其安排新任务。- 在android 5.0(API级别21)及更高版本中,可以使用
JobScheduler
类来调度操作。对于早期android版本,可以通过向startService()
传递Intent
来启动服务(或对执行中的服务下达新指令)。也可以通过向bindService()
传递Intent
来绑定到该服务。- 可以通过向
sendBroadcast()
、sendOrderedBroadcast()
或sendStickyBroadcast()
等方法传递Intent
来发起广播。- 可以通过在
ContentResolver
上调用query()
,对内容提供程序执行查询。如需了解有关
Intent
用法的详细信息,请参阅Intent和Intent过滤器文档。
在android系统启动应用组件之前,系统必须通过读取应用的清单文件(
AndroidManifest.xml
)确认组件存在。应用必须在此文件中声明其所有组件,该文件必须位于应用项目目录的根目录中。
除了声明应用的组件外,清单文件还有许多其他作用,如:
- 确定应用需要的任何用户权限,如互联网访问权限或对用户联系人的读取权限。
- 根据应用使用的API,声明应用所需的最低API级别。
- 声明应用使用或需要的硬件和软件功能,如相机、蓝牙服务或多点触摸屏幕。
- 声明应用需要链接的API库(android框架API除外),如google地图库。
清单文件的主要任务是告知系统应用组件的相关信息。例如,清单文件可按如下所示声明
Activity
:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.android.project">
<application android:icon="@drawable/app_icon.png" ... >
<activity android:name=".ExampleActivity"
android:label="@string/example_label" ... >
activity>
...
application>
manifest>
- 在
元素中,
android:icon
属性指向标识应用的图标所对应的资源。- 在
元素中,
android:name
属性指定Activity
子类的完全限定类名(结合元素中的
package
属性可以省略包名),android:label
属性指定Activity
中用户可见标签的字符串。- 使用以下元素来声明所有的应用组件:
Activity
相关的元素。
- 服务相关的
元素。
- 广播接收器相关的
元素。
- 内容提供程序相关的
元素。
如果未在清单文件中声明源代码中包含的
Activity
、服务以及内容提供程序,则这些组件对系统不可见,因此也永远不会运行。不过,可以通过BroadcastReceiver
对象的形式,在清单中声明或在代码中动态创建广播接收器;以及通过调用registerReceiver()
,在系统中注册广播接收器。
如需详细了解如何为应用构建清单文件,请参阅AndroidManifest.xml文件文档。
如上文启动组件中所述,可以使用
Intent
来启动Activity
、服务和广播接收器。通过在Intent
中显式命名目标组件(使用组件类名)来使用Intent
。还可以使用隐式Intent
,通过它来描述要执行的操作类型和待操作的数据(可选)。借助隐式Intent
,系统能够在设备上找到可执行该操作的组件,并启动该组件。如果有多个组件可以执行Intent
所描述的操作,则由用户选择使用哪一个组件。
注意:如果使用Intent
来启动服务,请使用显式Intent
来确保应用的安全性。使用隐式Intent
启动服务存在安全隐患,因为无法确定哪些服务将响应Intent
,且用户无法看到哪些服务已启动。从android 5.0(API级别21)开始,如果使用隐式Intent
调用bindService()
,系统会抛出异常。
通过将收到的
Intent
与设备上其他应用的清单文件中提供的Intent
过滤器进行比较,系统便可识别能响应Intent
的组件。
在应用的清单文件中声明Activity
时,可以选择性地加入Intent
过滤器,以便响应来自其他应用的Intent
。可以将元素作为组件声明元素的子项进行添加,从而组件声明
Intent
过滤器。
例如,如果构建的电子邮件应用包含用于撰写新电子邮件的Activity
,则可通过声明Intent
过滤器来响应发送相关的Intent
(目的是发送新电子邮件),如下方示例所示:
<manifest ... >
...
<application ... >
<activity android:name="com.example.project.ComposeEmailActivity">
<intent-filter>
<action android:name="android.intent.action.SEND" />
<data android:type="*/*" />
<category android:name="android.intent.category.DEFAULT" />
intent-filter>
activity>
application>
manifest>
如果另一个应用创建包含
ACTION_SEND
操作的Intent
并将其传递到startActivity()
,则系统可能会启动你的Activity
,以便用户能够发送电子邮件。
Android设备多种多样,但并非所有设备都提供相同的特性和功能。以防将你的应用安装在缺少应用所需特性的设备上,必须通过在清单文件中声明设备和软件要求,为该应用支持的设备类型明确定义一个配置文件。其中的大多数声明只是为了提供信息,系统并不会读取它们,但goole play等外部服务会读取它们,以便在用户通过其设备搜索应用时为用户提供过滤功能。
例如,如果你的应用需要相机功能,并使用android 2.1(API级别7)中引入的API,则必须在清单文件中声明以下要求:
<manifest ... >
<uses-feature android:name="android.hardware.camera.any"
android:required="true" />
<uses-sdk android:minSdkVersion="7" android:targetSdkVersion="19" />
...
manifest>
通过示例中所述的声明,没有相机且android版本低于2.1的设备将无法从google play安装你的应用。不过,可以声明应用使用相机,但并不要求必须使用。在此情况下,必须将
required
属性设置为false
,并在运行时检查设备是否拥有相机,然后根据需要停用任何相机功能。
如需详细了解如何管理应用与不同设备的兼容性,请参阅设备兼容性文档。
Android应用并非仅包含代码,它还需要与源代码分离的资源,如图像、音频文件以及任何与应用的视觉呈现有关的内容。例如,可以通过XML文件定义
Activity
界面的动画、菜单、样式、颜色和布局。借助应用资源,无需修改代码即可轻松更新应用的各种特性。通过提供备用资源集,可以针对各种设备配置(如不同的语言和屏幕尺寸)优化应用。
对于在android项目中加入的每一项资源,SDK构建工具均会定义唯一的整型id,可以利用此id来引用资源,这些资源或来自应用代码,或来自XML中定义的其他资源。例如,如果应用包含名为logo.png
的图像文件(保存在res/drawable/
目录中),则SDK工具会生成名为R.drawable.logo
的资源id,可以利用它来引用该图像,并将其插入界面。
如果提供与源代码分离的资源,则其中最重要的一个优点在于,可以提供适用于不同设备配置的备用资源。例如,通过在XML中定义界面字符串,可以将字符串翻译为其他语言,并将这些字符串保存在单独的文件中。然后,android系统会根据向资源目录名称追加的语言限定符(如为法语字符串值追加res/values-fr/
)和用户的语言设置,对界面应用相应的语言字符串。
Android支持许多不同的备用资源限定符。限定符是资源目录名称中加入的短字符串,用于定义这些资源适用的设备配置。例如,应根据设备的屏幕方向和尺寸为Activity
创建不同的布局。当设备屏幕为纵向时,可能想要一种垂直排列按钮的布局;但当屏幕为横向时,可以按水平方向排列按钮。如要根据方向更改布局,可以定义两种不同的布局,然后对每个布局的目录名称应用相应的限定符。然后,系统会根据当前设备方向自动应用相应的布局。
如需详细了解可以在应用中加入的不同资源类型以及如何针对不同设备配置创建备用资源,请阅读提供资源。如要详细了解最佳做法和设计生产质量的稳健应用,请参阅应用架构指南。