正文  基础知识 > 生命周期/运行原理 >

Android应用程序的基本原理详解

Android应用程序是用Java语言编写的。编译过后的字节码,以及应用程序要求的其他数据和资源文件,通过aapt工具被绑定在一起,称为 Android包,这是一个带.apk后缀的档案文件。这个文件也是用户下载到他们设备上......

Android应用程序是用Java语言编写的。编译过后的字节码,以及应用程序要求的其他数据和资源文件,通过aapt工具被绑定在一起,称为 Android包,这是一个带.apk后缀的档案文件。这个文件也是用户下载到他们设备上的文件。所有的代码在一个单一的.apk文件中,组成一个“应用程序”。
从许多方面来说,每个Android应用程序存活在它们自己的世界中:
 
默认地,每一个应用程序运行在它自己的Linux进程中。当应用程序的任何代码需要被执行时,Android启动进程;当不再需要时,或者系统资源被其他应用程序所要求时,关闭进程。
 
每一个进程有它自己的Java虚拟机(JVM),因此应用程序代码独立与所有其他应用程序代码而运行。
 
默认地,每一个应用程序被分配一个唯一的Linux用户ID。通过设置权限许可,应用程序的文件只对该用户可见,只对应用程序本身可见—虽然有办法将其导出到其他应用程序。
 
有可能安排两个应用程序共享同一用户ID,在这种情况下,它们可以彼此看见对方的文件。要保全系统资源,具有相同ID的应用程序也可以被组织起来运行在同一Linux进程中,共享同一VM。
 
2.1 应用程序组件
 
Android一 个最核心的特性是,一个应用程序可以利用其它应用程序的元素(假设这些程序允许这样做)。例如,如果你的应用程序需要显示一个滚动的图像列表,而其它应用 程序已经开发了一个合适的图像滚动器并且允许其被其他人使用,那么你可以要求使用该图像滚动器来完成这项工作,而不必自己再开发一个。你的应用程序不必合 并或链接到其他应用程序的代码。相反,当需要发生时,它仅是简单地启动其他应用程序。
 
要能如此地工作,系统必须在需要应用程序任何部分时,能启动该应用程序进程,并为该部分实例化Java对象。因此,不像在其他大多数系统上的应用程序,Android应用程序没有一个为应用程序的任何事设置单一的入口点(例如,没有main()函数)。相反,他们拥有一些主要组件,系统可以实例化这些组件并且根据需要来运行。总共有四类组件:
 
q Activities
 
q Services
 
q Broadcast receivers
 
q Content providers
 
2.1.1 四种组件简介
 
Activities
 
一个activity代表一个可视的用户接口,该接口致力于用户能做一些操作。例如,一个activity可能代表一个菜单项列表,用户可以从中选择,或者它可能显示带有标题说明的图像。一个文本消息应用程序可能有一个activity来显示一个联系人列表以发送消息,有第二个activity来写消息到被选择的联系人,以及其它activity来重新获取旧的消息或改变设置。虽然它们一起工作来形成一个内聚性的用户接口,但实际上每一个 activity是独立与其他的activity的。每一个activity作为Activity基类的一个子类来实现的。
 
一个应用程序可能仅由一个activity组成,也可能象刚才提到的文本消息应用程序一个,由数个activity组成。Activity是什么,以及有多少activity,当然依赖于应用程序及其设计。典型地,其中一个activity被标记为第一个,当应用程序被启动时,它应该被首先呈现出来。从一个activity迁移到另一个activity是通过当前activity启动下一个activity来完成的。
 
每一个activity都有一个默认的绘制窗口。通常,该窗口填满屏幕,但是它也可能比屏幕小并且浮动在其它窗口上面。一个activity还能利用另外的窗口—例如,一个位于activity中间的弹出对话框用于对用户做出响应,或者一个当用户选择一个屏幕上特定条目时带给用户重要信息的窗口。
 
窗口的可视内容由一个具有层次的视图所提供—从基类View继承来的对象。每一个视图控制一个窗口中特定的矩形空间。父视图容纳并组织其子视图的布局。叶子视图(在层次的最末端)在它们所控制的矩形中绘制并对作用于这一区域的用户动作做出响应。因此,视图是activity与用户的交互发生的地方。例如,一个视图可能会显示一个小图像,并且当用户轻击该图像时发起一个动作。Android有许多现成的视图可以使用,包括按钮、文本域、滚动条、菜单项、复选框,等等。
 
一个视图层次是由Activity.setContentView()方法布局到一个activity中的。内容视图(content view)是位于层次根部的View对象。
 
Services
 
一个service没有可视化的用户接口,而是运行在后台一个无限期限内。例如,当用户试图做其他事情时,一个service在后台播放音乐,或者它也可能在网络上提取数据,或做些计算并提供结果给需要它的activity。每一个service继承自Service基类。
 
一个最好的例证是一个媒体播放器从一个播放列表中播放歌曲。播放器应用程序可能有一到多个activity以允许用户来选择歌曲并开始播放。然而,音乐播放本身不能被一个activity处理,因为用户会期望音乐持续播放,甚至是在他们离开播放器并开始做其他事情时。要保持音乐继续播放,媒体播放器 activity可以开始一个service来在后台运行。然后系统将保持音乐播放服务持续运行,甚至在启动播放器的activity离开屏幕以后也会持续播放。
 
连接(绑定)到一个正在进行的service(如果service还没有运行,就启动它)也是可能的。当连接后,你可以通过service暴露的接口与service进行通信。对于音乐service,这个接口可能允许用户暂停、后退、停止以及恢复播放。
 
像activity和其它组件一样,service运行在应用程序进程的主线程中。因此它们不会阻塞其它组件或用户接口,他们经常为耗时的任务(如音乐播放)产生另外的线程。
 
Broadcast receivers
 
一个broadcast receiver是一个什么也不做的组件,除了它接受广播公告并对其做出反应。许多广播源(broadcast orginate)在系统代码中----例如,声明时区的改变,电量过低,一个图像已经被拍照,或者用户改变了一个语言参数。应用程序还可以启动一个广播 ----例如,让其他应用程序知道一些数据已经被下载到设备上并且可以使用它们了。
 
一个应用程序可以有许多广播接收器(broadcast receivers)来对任何它认为重要的公告做出响应。所有的接收器扩展自BroadcastReceiver基类。
 
广播接收器并不显示一个用户接口。然而,在对它们接收到的信息做出响应时,它们可以启动一个activity,或者它们可以使用通知管理器(NotificationManager)来提醒用户。通知可以通过多种方式来获得用户的注意----闪烁背景照明灯,振动设备,播放声音等等。它们典型地会放置一个固定的图标在状态栏,用户可以打开这个图标来查看信息。
 
Content providers
 
一个Content provider使得一系列特定的应用程序数据对其他应用程序可用。这些数据可以存放在文件系统中,在一个SQLite数据库中,或者在任何其它可以被感知的形式中。内容提供器(content provider)扩展自ContentProvider基类,实现了一系列标准的方法,这些方法能够使其他应用程序来重新获取并存储它所控制的类型的数据。然而,应用程序并不直接调用这些方法。相反,它们使用一个ContentResolver对象并调用其方法。一个ContentResolver可以与任何内容提供器通话,它与提供者合作来管理任何相关的通信工序。(Content provider在稍后一节会专门讲述)
 
无论何时,只要有一个被特定的一个组件所处理的请求,Android就确保组件的应用程序处理正在运行,如果有必要就启动它,并且保证组件的一个合适的实例是可用的,如果有必要就创建这个实例。
 
2.1.2 激活组件:intents
 
当一个请求来自一个ContentResolver时,内容提供器被激活。其他三个组件----activities,services和 broadcast receivers----被名为intents的异步消息所激活。一个intent是一个Intent对象,持有消息的内容。对于activities 和services来说,它意味着位于其他事物中被请求的动作和指定要操作的数据的URI。例如,它可能会为一个activity传送一个请求以代表给用户的一个图片,或者让用户编辑一些文本。对于broadcast receivers,Intent对象意味着被公告/通知的动作。例如,它可能会通告有兴趣的相关方,相机的按钮被按下了。
 
有各自的方法来用于激活每一类组件:
 
q 通过传递一个Intent对象到Context.startActivity()或 Activity.startActivityForResult(),来启动一个activity(或者让做一些新的东西)。进行响应的 activity可以通过调用其getIntent()方法来查看引起它被启动的原始内容(intent)。Android调用该activity的 onNewIntent()方法来向其传递任何后续的intent。
 
一个activity经常启动下一个activity。如果它期望从它所启动的activity获得一个返回的结果,那么它就要调用 startActivityForResult()而不是startActivity()。例如,如果它启动一个让用户挑选照片的activity,那么它可能期望返回被选中的照片。结果在一个Intent对象中被返回,而该Intent对象被传递给进行调用的activity的 onActivityResult()方法中。
 
q 通过传递一个Intent对象给Context.startService(),一个service被启动(或者一个新的指令传达给正在运行的 service)。Android调用该service的onStart()方法并将Intent对象传递给它。相似地,将一个intent传递给 Context.bindService(),能够在进行调用的组件和目标service之间建立一个持续的连接。该service在一个 onBind()调用中接收该Intent对象。(如果该service还没有运行,bindService()能有选择地启动它。)例如,一个 activity可能会与音乐播放服务建立一个连接,这样它就能向用户提供控制播放的方式(一个用户接口)。该activity将调用 bindService()来建立这个连接,然后调用service所定义的方法来影响播放。(在稍后“远程过程调用RPC”一节会专门讲述 service)
 
q 应用程序能通过传递一个Intent对象到诸如 Context.sendBroadcast(),Context.sendOrderedBroadcast(),和 Context.sendStickyBroadcast()这些方法中来创建一个广播。Android通过调用它们的onReceive()方法发布该 Intent到所有感兴趣的broadcast receivers。(更多有关Intent的信息,在稍后一节“Intents and Intent Filters”中会专门讲述)
 
2.1.3 停止组件
 
一个内容提供器只有当它在响应来自一个ContentResolver的请求时是活动的。一个广播接收器只有当它在响应一个广播消息时是活动的。因此没有必要显式地关闭这些组件。
 
另一方面,activities提供用户接口。它们与用户处于一个长时间运行的会话中,并且可能保存有active,甚至在空闲时,只要会话在继续。相似地,services也可能持续运行很长时间。因此Android有方法以一种有序的方式来关闭activities和services。
 
q 通过调用其finish()方法来关闭一个activity。一个activity可以通过调用finishActivity()来关闭另一个activity(它使用startActivityForResult()方法启动的activity)。
 
q 通过调用其stopSelf()方法来停止一个service,或者通过调用Context.stopService()。
 
当组件不再被使用时,或者当Android必须为更多活动的组件而收回内存时,它们有可能被系统所关闭。在后面“组件的生命周期”一节中,将讨论这种可能性及更多地细节。
 
2.1.4 manifest文件
 
在Android能启动一个应用程序组件之前,它必须知道组件的存在。因此,应用程序在一个manifest文件中声明它们的组件。manifest文件被绑定在一个Android包中。Android包是一个.apk文件,它还持有应用程序的代码、文件和资源。
 
manifest是结构化的XML文件,并且对所有的应用程序来说,它总是被命名为AndroidManifest.xml。除了声明应用程序的组件之外,它还做很多事情,如命名应用程序需要链接到的库(除了默认的Android库之外),以及应用程序所期望被授权的任何权限验证。
 
但是manifest的首要任务是告知Android关于应用程序的组件。例如,一个activity可能被声明如下的内容:
 
01 <?xml version="1.0" encoding="utf-8"?>
02
03 <manifest . . . >
04
05 <application . . . >
06
07 <activity android:name="com.example.project.FreneticActivity"
08 android:icon="@drawable/small_pic.png"
09 android:label="@string/freneticLabel"
10 . . . >
11
12 </activity>
13 . . .
14 </application>
15
16 </manifest>
在这个manifest文件中,元素的name属性命名实现了activity的Activity类的子类。而icon和label属性则指向包含有一个图标和标签的资源文件,这些图标和标签可以被显示给用户以代表这个activity。
 
另一个组件以相似的方式被声明--元素声明services,元素声明 broadcast receivers,以及元素声明内容提供器(content providers)。不在manifest文件中声明的activities,services和content providers对系统是不可见的,相应地永远不会运行。然而,broadcast receivers既可以在manifest文件中声明,也可以在代码中(如BroadcastReceiver对象)动态地创建并通过调用 Context.registerReceiver()向系统注册。(更多详细内容,请参见后面的”AndroidManifest.xml文件”一节)
 
2.1.4 Intent过滤器(Intent filters)
 
一个Intent对象能显式地命名一个目标组件。如果这样的话,Android会查找那个组件(基于在manifest文件中的声明)并激活它。但是如果一个目标组件没有被显式地命名,Android必须定位最合适的组件来响应该Intent。它通过对Intent对象和潜在目标的intent filters的比较来完成这种定位。一个组件的intent过滤器告知Android该组件能够处理的intent种类。象其他组件的基本信息一样,它们是在manifest文件中声明的。下面是前面示例代码的一个扩展,为activity添加了两个intent过滤器。
 
01 <?xml version="1.0" encoding="utf-8"?>
02
03 <manifest . . . >
04
05 <application . . . >
06
07 <activity android:name="com.example.project.FreneticActivity"
08 android:icon="@drawable/small_pic.png"
09 android:label="@string/freneticLabel"
10
11 . . . >
12
13 <intent-filter . . . >
14
15 <action android:name="android.intent.action.MAIN" />
16 <categoryandroid:name="android.intent.category.LAUNCHER" />
17
18 </intent-filter>
19
20 <intent-filter . . . >
21
22 <action android:name="com.example.project.BOUNCE" />
23 <data android:mimeType="image/jpeg" />
24 <categoryandroid:name="android.intent.category.DEFAULT" />
25
26 </intent-filter>
27 </activity>
28 . . .
29 </application>
30
31 </manifest>
上面示例代码中的第一个过滤器—action“android.intent.action.MAIN”和 category“android.intent.category.LAUNCHER”—是一个通用的过滤器。它使得activity成为在应用程序启动器中显示出来的应用程序之一。应用程序启动器指的是显示(列出)用户可以在设备上启动的应用程序的屏幕。换句话说,这个activity是应用程序的入口,是用户在启动器中选择应用程序时看得见的最初的一个。
 
第二个过滤器声明一个该activity可以在一个特定类型的数据上执行的动作。
 
一个组件可以拥有任何数量的intent过滤器,每一个都声明一套不同的功能。如果没有任何过滤器,那么它只能被组件作为目标显式命名的intents所激活。
 
对于在代码中创建和注册的一个broadcst receiver,intent过滤器是直接作为一个IntentFilter对象创建的。所有其他过滤器都是在manifest中建立的。