AndroidApplication Fundamentals(Android应用基础)
Android应用采用Java编程语言来编写,AndroidSDK工具编译我们的代码,连同任何数据和资源文件一起打包成一个APK(一个Android package),后缀名为.apk的文件时归档文件。一个APK文件包含一个Android 应用的所有内容,且是Android设备用来安装应用的文件。
一旦安装到设备中,每个Android应用都工作在自己安全的沙箱中(securitysandbox):
(1) Android操作系统是多用户的linux系统,每个应用都是一个不同的用户。
(2) 默认情况下,系统为每个应用分配一个唯一的linux用户ID(这个ID只能被系统使用,对于应用来说是未知的)。系统为一个应用的所有文件设置权限,所以只有被非配了用户ID的应用才能够访问这些文件。
(3) 每个进程都有自己的虚拟机(VM),所以一个应用的代码运行在和其他应用相互隔离的空间中。
(4) 默认情况下,每个应用运行在它自己的linux进程中,任何应用的组件(比如activity)需要被执行时Android就会开启进程,并在不在需要或是系统为其他应用必须回收内存是关闭这个进程。
基于上面这些特点,Android系统实现了最小权限原则,也就是说,每个应用默认只能访问它需要用来实现功能的组件。这样可以创造一个非常安全的环境,在此环境下,一个应用不能访问没有经过授权的系统其他部分。
但是,有几种方式可以让一个应用共享数据给其他应用,和一个应用可以访问系统服务:
(1) 为了让不同应用可以互相访问彼此的文件,系统可以安排两个应用共享同一个linux用户ID。为了节省系统资源,拥有相同用户ID的应用程序运行在相同的linux进程和共享相同的VM(应用程序也必须使用相同的证书)。
(2) 一个应用能够请求访问设备数据的权限,比如用户的联系方式(contacts)、短信信息(SMS message)、存储卡(SD卡)、camera、蓝牙、等等,所有应用的权限必须在安装时由用户来授权
上面的这些描述覆盖了一个Android应用如何在系统中存在的基础知识,这个文档的剩余部分为我们介绍:
(1) 定义了我们应用程序的核心框架组件(core framework components)。
(2) 我们在应用的manifest文件声明组件(比如activity等)和需要的设备特征(比如拍照)。
(3) 资源独立于应用代码,并允许我们的应用程序优雅地优化其行为以适用于多种设备配置。
1. 应用组件(App Components)
应用程序组件式一个Android应用程序的基本构建块,每个组件是个不同点,系统可以通过它们进入我们的应用程序(Each component is a different point through which the system canenter your app)。不是所有的组件都是用户的实际入口点,而是一些组件相互依赖,但是每一个作为自己的实体存在和扮演一个特定的角色(每个组件是唯一的构建块,有助于定义我们APP的整体行为)。
Android系统支持4个不同的APP组件,每种类型组件有明显的目的和定义了组件如何被创建和销毁的不同的生命周期。
四种APP组件类型如下:
(1) Activities
Activity我们理解为活动,一个Activity代表用户界面的一个独立屏幕,比如,一个邮件APP有一个activity来显示新邮件列表,另一个activity用于写一封邮件,并且还有一个activity用于读邮件。虽然这些activities协同工作以构成邮件APP的紧密结合的用户体验,但是每个activity是相互独立的。因此,一个不同的APP可以启动这些activities(只要邮件APP允许这样做)。比如,一个拍照APP为了用户能够分享照片,它能够启动邮件APP的组件来写新邮件。
一个activity作为Activity的子类来实现,我们可以通过开发者指南的Activities部分来学习更多关于activity的功能
(http://developer.android.com/reference/android/app/Activity.html)。
(2) Services
Servies理解为服务,services组件运行在后台以执行长时间的操作或实话执行远程操作。一个service不提供用户界面,比如,当用户在另一个APP时,一个service可在后台播放音乐,或是从网络获取数据而不阻止用户和一个activity交互。另一种组件,比如activity可以启动service来让它运行,或是activity为了和sevice交互而绑定它。一个serice是作为Service的自来的实现。
(3) Content providers
内容提供者,一个content provider管理一组共享的APP数据,我们可以保存这些数据到文件系统、SQLite数据库、web,或是保存在我们的应用程序能够访问的任何其他永久保存的位置。通过content provider,其他APP能够查询或甚至是修改这些共享的数据(如果content provider允许这么操作)。比如,Android系统提供一个content provider来管理用户联系人信息,因此,有适当权限的任何APP能够查询content provider的一部分(比如ContactsContract.Data)来读和写一个特定用于的信息。
content provider也有助于读写我们APP私有数据(不共享),比如记事本示例APP使用一个contentprovider来保存笔记。一个content provider作为ContentProvider的子类来实现,并且必须实现一组标准的API,这样可使其他APP执行事务。
(4) Broadcast receivers
广播接收器,一个broadcastreceivers组件响应整个系统的广播公告,很多广播产生于系统,比如,一个广播通知屏幕要已关闭,电池低电量,或是照片被捕获。APP能够发起广播,比如通知其他APP一些数据已经下载完成并且处于可用状态。虽然broadcast receivers没有显示用户界面,但它们可以在一个广播事件发生的时候创建一个状态栏通知来警告用户。更常见的,虽然一个广播接收器只是其他组件的“网关”,且适用于用来做最少的工作。比如,它根据事件启动一个服务来执行一些工作。一个广播接收器作为BroadcastReceiver的子类来实现,每个广播以Intent对象的形式被分发出去。
Android系统设计一个独特的一面是任何APP能够启动另一个APP的组件,比如,如果我们想要用户使用带摄像头的设备来拍照,可能已经有另一个APP实现了拍照功能,并且我们的APP能够使用这个拍照功能,而不需要自己来开发一个activity来实现拍照功能,而且我们也不需要包含或是链接这个拍照程序。相反,我们能够简单的启动拍照APP的activity来拍照。当拍照完成,照片甚至能够返回到我们的APP中。对于用户来说,就像拍照工恩给你是我们APP实际的一部分一样。
当系统启动一个组件,如果这个组件所在的程序之前没有运行,那么系统就开始这个APP的进程,并且实例化(初始化)这个组件所需要的类。比如,如果我们的APP启动拍照APP的拍照功能对应的activity,这个activity运行在拍照APP的进程,而不是我们APP的进程中。所以,不想其他大部分系统的APP一样,Android系统的APP没有一个单独入口点(比如没有main函数)。
因为系统每个APP运行在自己独立的进程中,并且APP中的文件都有自己的权限来限制访问其他APP。我哦们的AP不能直接激活其他APP的组件,但是Android系统可以,为了激活其他APP的组件,我们必须发送一个说明了我们气筒一个特定组件的意图的消息给系统,这样系统就可以为你激活这个组件。
2. 激活组件(Activating Components)
Android系统4种组件类型中的3种,activity、service和broadcastreceivers这3种是由一种名为intent的异步消息来激活的,这些intents在运行时,把属于我们的APP或是其他APP的单独组件绑定在一起,我们可以把intent看做是需要其他组件action(行动)的消息。
一个intent是以一个Intent对象的形式创建,intent定义一个激活一个指定组件或是一种指定类型的组件的消息,相应地,intent可以说是显示或是隐式指定。
对于activity和service来说,一个intent定义了要执行的操作(比如要view或是send什么)和指定要操作的数据URI(除此之外,开始的组件也需要知道)。比如,一个intent可能为一个activity发送显示一张图片或是打开一个web页的请求。在某种情况下,我们可以启动一个activity来获取返回的结果,在这种情况下,这个activity也能够返回一个intent结果(比如,我们可以发出一个intent让用户选择一个个人联系人名(a personal contact)和让它返回给我们,这个返回的intent包含已选中的contact的URI)。
对于广播接收器,intent简单的定义了要广播的通知(announcement)(比如,一个指示设备电池低电量的广播,只是包含了一个已知动作的字符串(“battery is low”))。
另一组件类型内容提供者不是由intent来激活,而是由一个指定请求目标的ContentResolver来激活。这个内容解析器处理所有与内容提供者的直接事务。所以执行提供者事务的内容提供者不需要intent,而是调用ContentResolver对象上的方法来激活。这样可在内容提供者和这个组件请求的信息保持在一个抽象层(为了安全)。
每种类型的组件有自己的办法来激活相应的组件:
(1) 我们可以通过传递一个Intent给startActivity()或startActivityForResult()(如果我们需要这个activity返回结果)启动一个activity(或是给它一些新的要做的事情)。
(2) 我哦们可以通过传递一个Intent给startSevice()来开启一个service(或是给一个新指令(instructions)给正在运行的serice),或是我们可以传递一个Intent给bindService()来绑定一个service。
(3) 我们可以通过传递一个intent给sendBroadcast()、sendOrderedBroadcast()或是sendStickyBroadcast()这些方法来发起一个广播。
(4) 我们可以通关过调用一个ContentResolver对象的query()来查询一个内容提供者。
3. Manifest文件
在Android系统能够启动一个APP组件之前,系统必须通过读取APP的AndroidManifest.xml(manifest文件)知道APP的组件。我们的APP必须在这个文件中声明所有的组件,并且这文件必须在APP工程目录的根目录下。
除了声明APP的组件之外,manifest文件还做了下面一些事情:
(1) 确定APP需要的任何用户权限,比如Internet访问权限或是读取用户联系人权限。
(2) 声明运行这个APP需要的最低API版本。
(3) 声明APP需要的硬件和软件特征,比如摄像头、蓝牙服务或是一个多点触屏。
(4) 申明该程序需要连接的API库(不是Android framework的API),比如Google Maps库。
(5) 等等。
4. 声明组件(declaring components)
Manifest文件的首要任务是通知系统关于APP中使用的组件,比如一个manifest文件能像下面一样声明activity:
<?xml version="1.0" encoding="utf-8"?>
<manifest ... >
<application android:icon="@drawable/app_icon.png" ... >
<activity android:name="com.example.project.ExampleActivity"
android:label="@string/example_label" ... >
</activity>
...
</application>
</manifest>
在<application>元素中,android:icon属性指向了标示这个APP的icon资源。
在<activity>元素中,android:name属性指定了Activity子类的完整类别名称,android:label属性指定了标示activity对于用户可见的label。
我们必须用下面的方式来声明APP的所有组件:
(1) <activity>元素声明activities。
(2) <service>元素声明services。
(3) <receiver>元素声明broadcastreceiver。
(4) <provider>元素声明contentprovider。
我们在程序中包含了activities、services和content providers,但没有在mainfest文件中声明,那么这些组件对系统不可见,因此,这些组件不能运行。但是,broadcast receivers不仅可以再manifest中声明,也可以在代码中动态创建(作为BroadcastReceiver对象),并通过调用registerReceiver()在系统中注册。
5. 声明组件功能(declaring component capabilities)
正如上面Activating Components部分描述的那样,我们可以使用一个Intent来启动activities、services和broadcast receivers,我们可以通过在Intent显式指定目标组件的名字(使用组件的类名)来实现。但是,intents真正强大的地方在于隐式Intent的概念。一个隐式intent简单的描述了要执行的组件的操作(并且可以有选择性地描述要执行操作的数据),允许系统在设备中找到能够执行这些操作的组件并启动它。如果有多个组件能够执行intent描述的操作,用户可以选择一个来执行。
系统通过比较接收到的intent和定义在设备其他应用manifest文件中的intent filters,从而识别能响应这个intent的组件。
当我们在manifest文件中声明一个activity,我们可选择地包含intent filters,这些intent filters表明了组件对来之其他APP的intent做出反应的能力。我们可以通过添加一个<intent-filter>元素作为组件声明元素的子元素来声明一个intent filter。
比如,如果我们已经有一个带有写邮件activity的邮件APP,我们像下面一样声明一个intent filter来响应“发送”intents(为了发送新邮件):
<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>
然后,如果其他APP创建带有ACTION_SEND操作的intent,并传递给startActivity(),系统就能够启动找到我们的写邮件的activity,这样用户就能够编写和发送邮件。
6. 声明应用需求(declaring app requirements)
Android支持各种不同的设备 ,并且这些设备提供的特征和功能也不同。为了避免我们的APP安装在缺少所需要特征的设备上,非常重要的一点是我们通过在manifest文件中声明设备和软件需求,来清晰的定义我们APP支持的设备类型。大部分这些声明只是一些信息和系统并不会读取它们,但像谷歌市场(Google Play)这样的外部服务在用户从他们设备搜索APP是,为用户提供过滤而读取这些信息。
比如,如果我们的APP需要摄像头和使用Android 2.1的API版本,我们可以再manifest文件中像下面一样声明这些要求:
<manifest ... >
<uses-feature android:name="android.hardware.camera.any"
android:required="true" />
<uses-sdk android:minSdkVersion="7" android:targetSdkVersion="19" />
...
</manifest>
由于设备没有摄像头和Android版本低于2.1,则不能从谷歌市场中安装我们的APP。
然而,虽然我们不需要摄像头,但也可以声明我们的APP使用拍照功能。载那种情况下,我们设置required属性为false和在运行时检查设备是否带有拍照功能,并相应的关闭任何拍照特征。
7. 应用资源(App Resources)
一个Android APP不仅仅是由代码组成,它需要和代码分离的资源,比如图片、音频文件和任何与我们APP相关的可视化内容。比如,我们可以定义动画、菜单、面板、色彩和XML文件中activity用户界面的布局。使用APP资源资源文件,可以更容易更新我们APP的特性而无需修改代码。并且通过提供多种可替换的资源文件,使我们可以优化多种配置的设备(比如不用的语言和屏幕大小)。
Android工程中每个资源,SDK编译工具定义一个唯一的整数ID,这样我们可以在APP代码或是XML定义的其他资源使用这些ID来引用对应的资源。比如,如果我们的APP包含一个名为logo.png的图像文件(保存在res/drawable/目录下),SDK工具产生一个名为R.drawable.logo的资源ID,我们可以可以使用它来引用这张图片和在用户界面中插入。
资源文件和代码分开这个机制最主要的一个方面是使我们可以为不同配置的设备提供可替换的资源。比如,在XML中定义UI字符串,我们可以翻译成其他语言的字符串并保存在独立的文件中。然后,基于语言标识符,我们添加资源文件夹名称(比如res/values-fr/对应于法语字符串)和用户语言设置,这样Android系统会为我们的UI应用合适的语言字符串。
对于我们可替换资源,Android支持多种不同的qualifiers(修饰符),限定符是包含在我们资源文件夹名称的短字符串,为了便于确定设备配置需要用的资源。再举另一个例子,对于不同的屏幕分享和大小,我们可能经常为我们的activities创建不同的布局。比如,当设备屏幕是纵向的,我们希望按键垂直排列的布局,但如果我们的屏幕是横向的,按键应当是水平排列的。为了根据方向来改变布局,我们可以定义两个不同的布局,并为每个布局文件夹名称提供适当的修饰符。这样,系统可以根据当前设备屏幕的方向来自动应用合适的布局。
Android开发者相关链接:
http://developer.android.com/guide/components/fundamentals.html