Android程序是用Java语言编写的。包含有任何应用都需要的数据和资源文件的编译好的Java代码被aapt tool绑定到了一个以.apk为后缀的归档文件,即Android包中。这个文件是发布应用的媒介,并且用户可以将其下载并安装在他们的移动设备上。一个单个的.apk文件中的所有代码可以认为是一个应用。
在多种情形下,每一个android应用都有自己的独立王国:
- 默认情况下,每一个应用都运行在自己的进程中。当应用中的任意代码需要执行的时候,android会启动进程,当不再需要执行或者其他应用需要系统资源时会关闭进程。
- 每一个应用都有自己独立的JVM,因此程序代码可以脱离于其它应用的代码而独立运行。
- 默认情况下,每一个应用会被赋予一个独立的Linux用户ID。为了使程序文件且只是程序本身对这个用户可见,所以给其赋予权限,尽管同样有很多种方式对其他应用也赋予权限。
给两个应用分配相同的用户ID是可能的,这样它们就可以在任意的情况下看到对方的文件。为了保证系统资源,拥有相同ID的应用将被安排运行在相同的Linux进程中,共享同一个VM。
程序组件
Android的一个核心特征是一个应用可以使用其他应用中的一部分内容(其他应用授权)。比如:如果你的应用需要展示图片的滚动列表,刚好其他应用已经开发好了一个合适的滚动列表,并且运行其他程序使用,那么你就不必要自己开发而可以直接调用。你只需要在需要的时候其它的应用中的这部分内容而不需要嵌入代码或者链接指向它。
对于这样的情况,系统必须能够在程序的这部分内容需要的时候启动进程,并且将这部分内容初始化为Java对象。因此和其他系统的程序不一样的是:Android中的应用没有一个独立的入口点(比如没有main函数),而只需在需要初始化和运行的时候有必要的组件即可。Android中一个有四种组件:
Activities
一个Activity可以尽最大努力向用户展示他们所能接受的用户的界面。比如:一个Activity可以展示一个用户可以从中选择的菜单项或者可能图文并茂的展示图片和标题。一个短信的应用可能有一个activity来显示需要发送信息的联系人列表,第二个activity用来写信息,其他的activity用来查看之前的信息或者改变设置。为了形成一个黏合的用户界面,它们协同工作,但每一个activity都互不依赖。每一个activity都需要做为Activity基类的子类实现。
一个应用可能只包含有一个activity或者像刚提到的短信应用有多个activity。在程序及其设计中,理所当然的要知道activities是什么以及需要多少个activity。典型的当一个应用启动时,需要向用户展示其中一个activity。从当前的activity启动下一个activity可以从一个activity到另一个中去。
每一个activity都有一个默认的窗体可以进行绘制。典型的:屏幕中有一个窗体,但它可能比屏幕小并且在另一个窗体的顶端浮动。一个activity还可以用作新弹出的窗体——比如,在activity中间用来收集用户回复的弹出的窗体或者当用户在屏幕上选择一个特殊的选项时向用户展示重要信息的窗体。
窗体中的可见内容是由views的子类——从View基类派生出的子类完成。在窗体中,每一个view可以控制一个特殊的长方形空间。父views包含和组织其子views的布局。叶子views(在继承机构的最底层)在这块空间中描绘出受他们控制并且直接对用户行为进行响应的长方形,因此view的作用是和用户进行交互的activity。比如:一个view可能显示一幅小图片并且在用户触碰图片时开始一个动作。Android已经有很多实现的view可以使用——包括按钮、文本框、滚动栏、菜单项、复选框等等。
通过Activity.setContentView()方法可以处理一个view在一个activity的窗体中放置的层次结构。content view是在继承结构的根部的View对象(关于view及其继承结构的详细信息可见另一个文档)。
Services
一个service并没有一个可见的用户界面 ,而是不定期的在后台运行。比如,一个service可能在用户准备做其他事情时在后台播放音乐或者通过网络获取数据再或者在需要的时候计算并且提供结果给activities。每一个service基础于Service基类。
一个音乐播放器播放播放列表上的歌曲是最好的例子。一个播放器音乐程序可能有一个或多个activities供用户选择歌曲并且进行播放。然而由于用户希望在他们离开播放器做其它事情的时候,音乐能够在后台自己播放而不是通过一个activity处理。为了保证音乐继续播放,播放器的activity可以启动在后台启动一个service。即使在activity启动完service并且离开屏幕之后,系统也能够保证音乐service在后台运行。
和一个正在运行的service(如果没有运行即启动service)进行连接(或绑定)是很有可能的。连接好之后,你可以通过service暴露的接口和service进行交互。比如音乐service,暴露的接口可能包括:暂停、后退、停止以及重启播放器。
类似于activities以及其它组件,services运行在应用程序进程的主线程中,因此他们不会阻塞其它组件或者用户接口,对于耗时的任务,他们通常产生一个新的线程来处理。具体内容见:Processes and Threads.
Broadcast receivers
broadcast receiver是一个只用来接收和响应“广播通告”但不做其它事的一个组件。许多广播起源于系统代码——比如时区发生改变、电量低、拍了一张照片或者用户改变了偏好语言等等。程序同样可以初始化一个广播——比如让其它应用知道一些数据已经下载到机器里,并且可以使用。
一个应用中可以有任意数量的broadcast receiver用来对他们认为重要的通告进行响应。所有的接收器继承与BroadcastReceiver类。
Broadcast Receiver不显示一个用户界面,然而它们可能启动一个activity来响应它们接受到的信息,或者使用NotificationManager来提示用户。提示可以以很多种方式来吸引用户注意——闪动背景光、机器震动、播放声音等等。典型的用法是在状态栏放置一个持久的图标,以便用户可以打开并获取信息。
Content providers
content provider提供了一系列对其他应用可用的数据。这些数据可以存放在文件系统、SQLite数据库或者在其他任意有意义的地方。为了让其它应用能够获取并且存储content provider所控制的类型的数据, content provider必须继承ContentProvider类并且实现其中的一套标准的方法。然后,程序不能直接调用这些方法而是使用ContentResolver对象,并且调用其中的方法。ContentResolver可以和任意的content provider交互,并且和provider共同来管理所涉及的进程间通信。
关于使用content provider,请见另一个文档。
无论何时,一个通常的组件都可以处理一个请求,Android保证组件的进程正在运行、如果有必要可以启动进程,并且有合适的组件实例可用,如果需要可以初始化实例。
激活组件:intents
当一个来自ContentResolver的请求指向ContentProviders时,其处于激活状态。而另三种组件——activities、services和broadcast receiver被称作intents的异步消息激活。intent是一个含有消息内容的Intent对象。对于activities和services,除了其它之前之外,其指定了请求的action的名字和指明需要执行的数据的URI。比如它可能给一个activity传达请求来给用户显示一幅图片或者让用户编辑一段文本。对于Broadcast Receiver,intent对象指明了需要通告的action的名字,比如它可能把照相按钮被按下通告给感兴趣的物体。
有很多不同的方法来激活各种组件:
- 通过传一个对象给
Context.startActivity()
或者Activity.startActivityForResult()可以启动一个activity(或者做一些新的事情)。调用activity的getIntent()方法可以查看启动本身的初始化的intent对象。Android调用activity的
onNewIntent()方法可以给activity传送一连串的intent对象。
一个activity可以启动下一个activity,如果activity需要从它启动的activity得到结果,可以调用startActivityForResult()方法而不 是startActivity()方法。比如:一个activity启动了一个允许用户选择照片的activity,它可能希望可以返回用户选择的照片。传给 startActivityForResult()方法的intent对象就包含了返回的结果。
- 通过传一个Intent对象给Context.startService()方法可以启动一个service(或者给正在运行的service一个说明),Android调用service的onStart()方法,并且将intent对象传给onStart()方法。
类似的,可以通过给Context.bindService()方法传一个intent对象,在调用的组件和目标service之间建立连接。service调用 onBind()方法可以接收到一个intent对象(如果service没有运行,可选择bindService()方法启动)。比如:一个activity为了让用 户控制播放器可能通过提供给用户一个工具(比如界面),以便和早前提到的音乐播放service建立连接。Activity可以调用 bindServic()建立连接,然后调用service中定义的方法来影响播放。
对于和一个service进行绑定,后边的部分会有详细描述。
- 通过给类似于
Context.sendBroadcast()
,Context.sendOrderedBroadcast()和
Context.sendStickyBroadcast()方法以及其他任意他们的变种方法传一个intent对象可以初始化一个broadcast。Android通过调用broadcast的onRecive()方法将intent内容分发给所有感兴趣的broadcast。
对于intent的更多信息,请见另一个文档:Intents and Intent Filters.